Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
3a1b1673ef | |||
89cbae24d0 | |||
7c1b317d08
|
|||
1fec4e3708
|
|||
0eb24aa937 | |||
5c88cdd3e3
|
|||
d51716893f
|
|||
ff350f9193 | |||
3d774f6d9c | |||
b78423377c
|
|||
1446f4c3cf | |||
8a80480d94
|
|||
b7b2992730
|
|||
61cbec0477 | |||
10e2739b6e
|
|||
ffa3efd99c | |||
7c08d7a5df
|
|||
6b481ba2af | |||
56cc671593
|
38
CHANGELOG.md
38
CHANGELOG.md
@@ -6,6 +6,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.6.0] - 2024-11-23
|
||||
|
||||
### Added
|
||||
- adding test to make sure we can gracefully shutdown
|
||||
- make sure to close down properly
|
||||
|
||||
## [0.5.0] - 2024-11-19
|
||||
|
||||
### Added
|
||||
- update name
|
||||
- respect sigterm
|
||||
- include author
|
||||
- update with rename
|
||||
|
||||
### Docs
|
||||
- add examples
|
||||
|
||||
## [0.4.0] - 2024-08-07
|
||||
|
||||
### Added
|
||||
- add correction
|
||||
- add small docs
|
||||
|
||||
## [0.3.0] - 2024-08-07
|
||||
|
||||
### Added
|
||||
- add add_fn to execute immediate lambdas
|
||||
|
||||
## [0.2.1] - 2024-08-07
|
||||
|
||||
### Docs
|
||||
- add a small readme
|
||||
|
||||
## [0.2.0] - 2024-08-07
|
||||
|
||||
### Added
|
||||
- with ctrl-c signal
|
||||
|
||||
## [0.1.0] - 2024-08-07
|
||||
|
||||
### Added
|
||||
|
193
Cargo.lock
generated
193
Cargo.lock
generated
@@ -1,21 +1,21 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
@@ -28,15 +28,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.81"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -45,23 +45,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -78,15 +78,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -96,9 +90,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -111,9 +105,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -121,15 +115,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -138,15 +132,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -155,21 +149,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -196,9 +190,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -214,9 +208,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
version = "0.2.164"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -234,22 +228,6 @@ version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "mad"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"rand",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@@ -267,18 +245,18 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
@@ -286,6 +264,23 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notmad"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"rand",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@@ -298,18 +293,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.3"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
@@ -342,9 +337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@@ -363,18 +358,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -411,23 +406,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.3"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.6"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.7",
|
||||
"regex-syntax 0.8.4",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -441,13 +436,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.7"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.4",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -458,9 +453,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
@@ -519,9 +514,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -530,18 +525,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -560,9 +555,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.39.2"
|
||||
version = "1.41.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -589,9 +584,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.11"
|
||||
version = "0.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -685,9 +680,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
|
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
version = "0.6.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
mad = { path = "crates/mad" }
|
||||
|
51
README.md
Normal file
51
README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# MAD
|
||||
|
||||
Mad is a life-cycle manager for long running rust operations.
|
||||
|
||||
- Webservers
|
||||
- Queue bindings
|
||||
- gRPC servers etc
|
||||
- Cron runners
|
||||
|
||||
It is supposed to be the main thing the application runs, and everything from it is spawned and managed by it.
|
||||
|
||||
```rust
|
||||
struct WaitServer {}
|
||||
|
||||
#[async_trait]
|
||||
impl Component for WaitServer {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("NeverEndingRun".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
||||
let millis_wait = rand::thread_rng().gen_range(50..1000);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
Mad::builder()
|
||||
.add(WaitServer {})
|
||||
.add(WaitServer {})
|
||||
.add(WaitServer {})
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Can be found (here)[crates/mad/examples]
|
||||
|
||||
- basic
|
||||
- fn
|
||||
- signals
|
||||
- error_log
|
@@ -1,6 +1,10 @@
|
||||
[package]
|
||||
name = "mad"
|
||||
name = "notmad"
|
||||
version.workspace = true
|
||||
description = "notmad is a life-cycle manager for long running rust operations"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/kjuulh/mad"
|
||||
author = "kjuulh"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -15,4 +19,5 @@ tokio-util = "0.7.11"
|
||||
tracing.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tracing-subscriber = "0.3.18"
|
||||
tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
|
||||
|
40
crates/mad/examples/basic/main.rs
Normal file
40
crates/mad/examples/basic/main.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use async_trait::async_trait;
|
||||
use rand::Rng;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::Level;
|
||||
|
||||
struct WaitServer {}
|
||||
#[async_trait]
|
||||
impl notmad::Component for WaitServer {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("WaitServer".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
let millis_wait = rand::thread_rng().gen_range(500..3000);
|
||||
|
||||
tracing::debug!("waiting: {}ms", millis_wait);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::TRACE)
|
||||
.init();
|
||||
|
||||
notmad::Mad::builder()
|
||||
.add(WaitServer {})
|
||||
.add(WaitServer {})
|
||||
.add(WaitServer {})
|
||||
.add(WaitServer {})
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
42
crates/mad/examples/error_log/main.rs
Normal file
42
crates/mad/examples/error_log/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use async_trait::async_trait;
|
||||
use rand::Rng;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::Level;
|
||||
|
||||
struct ErrorServer {}
|
||||
#[async_trait]
|
||||
impl notmad::Component for ErrorServer {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("ErrorServer".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
let millis_wait = rand::thread_rng().gen_range(500..3000);
|
||||
|
||||
tracing::debug!("waiting: {}ms", millis_wait);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Err(notmad::MadError::Inner(anyhow::anyhow!("expected error")))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::TRACE)
|
||||
.init();
|
||||
|
||||
// Do note that only the first server which returns an error is guaranteed to be handled. This is because if servers don't respect cancellation, they will be dropped
|
||||
|
||||
notmad::Mad::builder()
|
||||
.add(ErrorServer {})
|
||||
.add(ErrorServer {})
|
||||
.add(ErrorServer {})
|
||||
.add(ErrorServer {})
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
67
crates/mad/examples/fn/main.rs
Normal file
67
crates/mad/examples/fn/main.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use async_trait::async_trait;
|
||||
use rand::Rng;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::Level;
|
||||
|
||||
struct WaitServer {}
|
||||
#[async_trait]
|
||||
impl notmad::Component for WaitServer {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("WaitServer".into())
|
||||
}
|
||||
|
||||
async fn run(&self, _cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
let millis_wait = rand::thread_rng().gen_range(500..3000);
|
||||
|
||||
tracing::debug!("waiting: {}ms", millis_wait);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::TRACE)
|
||||
.init();
|
||||
|
||||
let item = "some item".to_string();
|
||||
|
||||
notmad::Mad::builder()
|
||||
.add(WaitServer {})
|
||||
.add_fn(|_cancel| async move {
|
||||
let millis_wait = 50;
|
||||
|
||||
tracing::debug!("waiting: {}ms", millis_wait);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.add_fn(move |_cancel| {
|
||||
// I am an actual closure
|
||||
|
||||
let item = item.clone();
|
||||
|
||||
async move {
|
||||
let _item = item;
|
||||
|
||||
let millis_wait = 50;
|
||||
|
||||
tracing::debug!("waiting: {}ms", millis_wait);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
69
crates/mad/examples/signals/main.rs
Normal file
69
crates/mad/examples/signals/main.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use async_trait::async_trait;
|
||||
use rand::Rng;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::Level;
|
||||
|
||||
struct WaitServer {}
|
||||
#[async_trait]
|
||||
impl notmad::Component for WaitServer {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("WaitServer".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
let millis_wait = rand::thread_rng().gen_range(500..3000);
|
||||
|
||||
tracing::debug!("waiting: {}ms", millis_wait);
|
||||
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct RespectCancel {}
|
||||
#[async_trait]
|
||||
impl notmad::Component for RespectCancel {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("RespectCancel".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
cancellation.cancelled().await;
|
||||
tracing::debug!("stopping because job is cancelled");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct NeverStopServer {}
|
||||
#[async_trait]
|
||||
impl notmad::Component for NeverStopServer {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some("NeverStopServer".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
||||
tokio::time::sleep(std::time::Duration::from_millis(999999999)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::TRACE)
|
||||
.init();
|
||||
|
||||
notmad::Mad::builder()
|
||||
.add(WaitServer {})
|
||||
.add(NeverStopServer {})
|
||||
.add(RespectCancel {})
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures_util::StreamExt;
|
||||
use std::{fmt::Display, sync::Arc};
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
@@ -49,6 +50,11 @@ pub struct Mad {
|
||||
should_cancel: Option<std::time::Duration>,
|
||||
}
|
||||
|
||||
struct CompletionResult {
|
||||
res: Result<(), MadError>,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
impl Mad {
|
||||
pub fn builder() -> Self {
|
||||
Self {
|
||||
@@ -64,6 +70,16 @@ impl Mad {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_fn<F, Fut>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(CancellationToken) -> Fut + Send + Sync + 'static,
|
||||
Fut: futures::Future<Output = Result<(), MadError>> + Send + 'static,
|
||||
{
|
||||
let comp = ClosureComponent { inner: Box::new(f) };
|
||||
|
||||
self.add(comp)
|
||||
}
|
||||
|
||||
pub fn cancellation(&mut self, should_cancel: Option<std::time::Duration>) -> &mut Self {
|
||||
self.should_cancel = should_cancel;
|
||||
|
||||
@@ -79,6 +95,7 @@ impl Mad {
|
||||
|
||||
let close_result = self.close_components().await;
|
||||
|
||||
tracing::info!("mad is closed down");
|
||||
match (run_result, close_result) {
|
||||
(Err(run), Err(close)) => {
|
||||
return Err(MadError::AggregateError(AggregateError {
|
||||
@@ -116,47 +133,85 @@ impl Mad {
|
||||
let mut channels = Vec::new();
|
||||
let cancellation_token = CancellationToken::new();
|
||||
let job_cancellation = CancellationToken::new();
|
||||
let job_done = CancellationToken::new();
|
||||
|
||||
for comp in &self.components {
|
||||
let comp = comp.clone();
|
||||
let cancellation_token = cancellation_token.child_token();
|
||||
let job_cancellation = job_cancellation.child_token();
|
||||
|
||||
let (error_tx, error_rx) = tokio::sync::mpsc::channel::<Result<(), MadError>>(1);
|
||||
let (error_tx, error_rx) = tokio::sync::mpsc::channel::<CompletionResult>(1);
|
||||
channels.push(error_rx);
|
||||
|
||||
tokio::spawn(async move {
|
||||
tracing::debug!(component = &comp.name(), "mad running");
|
||||
let name = comp.name().clone();
|
||||
|
||||
tracing::debug!(component = name, "mad running");
|
||||
|
||||
tokio::select! {
|
||||
_ = cancellation_token.cancelled() => {
|
||||
error_tx.send(Ok(())).await
|
||||
error_tx.send(CompletionResult { res: Ok(()) , name }).await
|
||||
}
|
||||
res = comp.run(job_cancellation) => {
|
||||
error_tx.send(res).await
|
||||
error_tx.send(CompletionResult { res , name }).await
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tokio::spawn({
|
||||
let cancellation_token = cancellation_token.child_token();
|
||||
let job_done = job_done.child_token();
|
||||
|
||||
let wait_cancel = self.should_cancel;
|
||||
|
||||
async move {
|
||||
let should_cancel =
|
||||
|cancel: CancellationToken, wait: Option<std::time::Duration>| async move {
|
||||
if let Some(cancel_wait) = wait {
|
||||
tokio::time::sleep(cancel_wait).await;
|
||||
|
||||
cancel.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
tokio::select! {
|
||||
_ = cancellation_token.cancelled() => {
|
||||
job_cancellation.cancel();
|
||||
}
|
||||
_ = job_done.cancelled() => {
|
||||
should_cancel(job_cancellation, wait_cancel).await;
|
||||
}
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
should_cancel(job_cancellation, wait_cancel).await;
|
||||
}
|
||||
_ = signal_unix_terminate() => {
|
||||
should_cancel(job_cancellation, wait_cancel).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut futures = FuturesUnordered::new();
|
||||
for channel in channels.iter_mut() {
|
||||
futures.push(channel.recv());
|
||||
}
|
||||
|
||||
while let Some(Some(msg)) = futures.next().await {
|
||||
tracing::trace!("received end signal from a component");
|
||||
|
||||
if let Err(e) = msg {
|
||||
tracing::debug!(error = e.to_string(), "stopping running components");
|
||||
job_cancellation.cancel();
|
||||
|
||||
if let Some(cancel_wait) = self.should_cancel {
|
||||
tokio::time::sleep(cancel_wait).await;
|
||||
|
||||
cancellation_token.cancel();
|
||||
match msg.res {
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
error = e.to_string(),
|
||||
component = msg.name,
|
||||
"component ran to completion with error"
|
||||
);
|
||||
}
|
||||
Ok(_) => {
|
||||
tracing::debug!(component = msg.name, "component ran to completion");
|
||||
}
|
||||
}
|
||||
|
||||
job_done.cancel();
|
||||
}
|
||||
|
||||
tracing::debug!("ran components");
|
||||
@@ -181,6 +236,11 @@ impl Mad {
|
||||
}
|
||||
}
|
||||
|
||||
async fn signal_unix_terminate() {
|
||||
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to bind SIGTERM handler");
|
||||
sigterm.recv().await;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Component {
|
||||
fn name(&self) -> Option<String> {
|
||||
@@ -207,3 +267,34 @@ impl<T: Component + Send + Sync + 'static> IntoComponent for T {
|
||||
Arc::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
struct ClosureComponent<F, Fut>
|
||||
where
|
||||
F: Fn(CancellationToken) -> Fut + Send + Sync + 'static,
|
||||
Fut: futures::Future<Output = Result<(), MadError>> + Send + 'static,
|
||||
{
|
||||
inner: Box<F>,
|
||||
}
|
||||
|
||||
impl<F, Fut> ClosureComponent<F, Fut>
|
||||
where
|
||||
F: Fn(CancellationToken) -> Fut + Send + Sync + 'static,
|
||||
Fut: futures::Future<Output = Result<(), MadError>> + Send + 'static,
|
||||
{
|
||||
pub async fn execute(&self, cancellation_token: CancellationToken) -> Result<(), MadError> {
|
||||
(*self.inner)(cancellation_token).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<F, Fut> Component for ClosureComponent<F, Fut>
|
||||
where
|
||||
F: Fn(CancellationToken) -> Fut + Send + Sync + 'static,
|
||||
Fut: futures::Future<Output = Result<(), MadError>> + Send + 'static,
|
||||
{
|
||||
async fn run(&self, cancellation_token: CancellationToken) -> Result<(), MadError> {
|
||||
self.execute(cancellation_token).await
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mad::{Component, Mad};
|
||||
use notmad::{Component, Mad};
|
||||
use rand::Rng;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing_test::traced_test;
|
||||
|
||||
@@ -12,7 +15,7 @@ impl Component for NeverEndingRun {
|
||||
Some("NeverEndingRun".into())
|
||||
}
|
||||
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
let millis_wait = rand::thread_rng().gen_range(50..1000);
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
||||
@@ -86,3 +89,51 @@ async fn test_can_run_components() -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[traced_test]
|
||||
async fn test_can_shutdown_gracefully() -> anyhow::Result<()> {
|
||||
let check = Arc::new(Mutex::new(None));
|
||||
|
||||
Mad::builder()
|
||||
.add_fn({
|
||||
let check = check.clone();
|
||||
|
||||
move |cancel| {
|
||||
let check = check.clone();
|
||||
|
||||
async move {
|
||||
let start = std::time::SystemTime::now();
|
||||
tracing::info!("waiting for cancel");
|
||||
cancel.cancelled().await;
|
||||
tracing::info!("submitting check");
|
||||
let mut check = check.lock().await;
|
||||
let elapsed = start.elapsed().expect("to be able to get elapsed");
|
||||
*check = Some(elapsed);
|
||||
tracing::info!("check submitted");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.add_fn(|_| async move {
|
||||
tracing::info!("starting sleep");
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
tracing::info!("sleep ended");
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
let check = check
|
||||
.lock()
|
||||
.await
|
||||
.expect("to be able to get a duration from cancel");
|
||||
|
||||
// We default wait 100 ms for graceful shutdown, and we explicitly wait 100ms in the sleep routine
|
||||
tracing::info!("check millis: {}", check.as_millis());
|
||||
assert!(check.as_millis() < 250);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||
}
|
Reference in New Issue
Block a user