Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From schmoozer-0.1.2 To schmoozer-0.2.0
2024-10-03
| ||
00:30 | Change trait methods return types. Add ConnResult and RunResult. check-in: 2924879fbf user: jan tags: trunk | |
2024-09-16
| ||
05:25 | Style fixups. check-in: de2e43c6de user: jan tags: schmoozer-0.2.0, trunk | |
05:24 | Exclude backon.toml from packaging. check-in: 42c4c94c32 user: jan tags: trunk | |
2024-03-28
| ||
17:38 | Fix typo. check-in: cdd924e4cd user: jan tags: trunk | |
2024-03-01
| ||
00:20 | Enable feature labels in docs. check-in: b6c565a755 user: jan tags: schmoozer-0.1.2, trunk | |
00:11 | docsrs label for tcpconn. check-in: 6ba6d163f7 user: jan tags: trunk | |
Changes to Cargo.toml.
1 2 | [package] name = "schmoozer" | | > > | > > > > > > | | | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | [package] name = "schmoozer" version = "0.2.0" edition = "2021" license = "0BSD" # https://crates.io/category_slugs categories = [ "network-programming" ] keywords = [ "connector", "network", "tokio" ] repository = "https://repos.qrnch.tech/pub/schmoozer" description = "A simple abstraction over a retryable async operation, such as establishing a connection" rust-version = "1.74" exclude = [ ".fossil-settings", ".efiles", ".fslckout", "examples", "www", "bacon.toml", "rustfmt.toml" ] # https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section [badges] maintenance = { status = "actively-developed" } [features] tcpconn = ["dep:killswitch", "tokio/macros", "tokio/net", "tokio/time"] [dependencies] async-trait = { version = "0.1.82" } killswitch = { version = "0.4.2", optional = true } tokio = { version = "1.40.0", optional = true } [dev-dependencies] tokio = { version = "1.40.0", features = [ "macros", "net", "rt-multi-thread", "time" ] } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [lints.clippy] all = { level = "deny", priority = -1 } pedantic = { level = "warn", priority = -1 } nursery = { level = "warn", priority = -1 } cargo = { level = "warn", priority = -1 } |
Added bacon.toml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | # This is a configuration file for the bacon tool # # Bacon repository: https://github.com/Canop/bacon # Complete help on configuration: https://dystroy.org/bacon/config/ # You can also check bacon's own bacon.toml file # as an example: https://github.com/Canop/bacon/blob/main/bacon.toml default_job = "clippy-all" [jobs.check] command = ["cargo", "check", "--color", "always"] need_stdout = false [jobs.check-all] command = ["cargo", "check", "--all-targets", "--color", "always"] need_stdout = false # Run clippy on the default target [jobs.clippy] command = [ "cargo", "clippy", "--all-features", "--color", "always", ] need_stdout = false # Run clippy on all targets # To disable some lints, you may change the job this way: # [jobs.clippy-all] # command = [ # "cargo", "clippy", # "--all-targets", # "--color", "always", # "--", # "-A", "clippy::bool_to_int_with_if", # "-A", "clippy::collapsible_if", # "-A", "clippy::derive_partial_eq_without_eq", # ] # need_stdout = false [jobs.clippy-all] command = [ "cargo", "clippy", "--all-features", "--all-targets", "--color", "always", ] need_stdout = false # This job lets you run # - all tests: bacon test # - a specific test: bacon test -- config::test_default_files # - the tests of a package: bacon test -- -- -p config [jobs.test] command = [ "cargo", "test", "--color", "always", "--", "--color", "always", # see https://github.com/Canop/bacon/issues/124 ] need_stdout = true [jobs.doc] command = ["cargo", "doc", "--color", "always", "--no-deps"] need_stdout = false # If the doc compiles, then it opens in your browser and bacon switches # to the previous job [jobs.doc-open] command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"] need_stdout = false on_success = "back" # so that we don't open the browser at each change # You can run your application and have the result displayed in bacon, # *if* it makes sense for this crate. # Don't forget the `--color always` part or the errors won't be # properly parsed. # If your program never stops (eg a server), you may set `background` # to false to have the cargo run output immediately displayed instead # of waiting for program's end. [jobs.run] command = [ "cargo", "run", "--color", "always", # put launch parameters for your program behind a `--` separator ] need_stdout = true allow_warnings = true background = true # This parameterized job runs the example of your choice, as soon # as the code compiles. # Call it as # bacon ex -- my-example [jobs.ex] command = ["cargo", "run", "--color", "always", "--example"] need_stdout = true allow_warnings = true # You may define here keybindings that would be specific to # a project, for example a shortcut to launch a specific job. # Shortcuts to internal functions (scrolling, toggling, etc.) # should go in your personal global prefs.toml file instead. [keybindings] # alt-m = "job:my-job" c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target |
Changes to examples/net.rs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | use std::{env, io::ErrorKind, ops::ControlFlow, time::Duration}; pub use tokio::net::TcpStream; use schmoozer::{async_trait, Connector}; pub struct TcpConnector { addr: String, delay: usize } impl TcpConnector { pub fn new(addr: impl ToString) -> Self { Self { addr: addr.to_string(), delay: 1 } } } | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | use std::{env, io::ErrorKind, ops::ControlFlow, time::Duration}; pub use tokio::net::TcpStream; use schmoozer::{async_trait, Connector}; pub struct TcpConnector { addr: String, delay: usize } impl TcpConnector { #[allow(clippy::needless_pass_by_value)] pub fn new(addr: impl ToString) -> Self { Self { addr: addr.to_string(), delay: 1 } } } |
︙ | ︙ | |||
31 32 33 34 35 36 37 | match res { Ok(conn) => Ok(conn), Err(e) => match e.kind() { ErrorKind::ConnectionRefused | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::TimedOut => { | | | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | match res { Ok(conn) => Ok(conn), Err(e) => match e.kind() { ErrorKind::ConnectionRefused | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::TimedOut => { println!("Retryable error: {e}"); Err(ControlFlow::Continue(e)) } _ => { println!("Fatal error: {e}"); Err(ControlFlow::Break(e)) } } } } async fn retry_delay(&mut self) -> ControlFlow<Self::Error> { let dur = Duration::from_secs(self.delay.try_into().unwrap()); println!("Retrying in {dur:?} .."); tokio::time::sleep(dur).await; // for next iteration double sleep duration for each iteration, but cap at // 60 seconds self.delay = std::cmp::min(self.delay * 2, 60); ControlFlow::Continue(()) |
︙ | ︙ |
Changes to examples/tcpconn.rs.
1 2 3 4 5 6 | #[cfg(feature = "tcpconn")] mod inner { use std::{env, ops::ControlFlow}; use schmoozer::tcpconn::{KillSwitch, SimpleTcpConnector, TcpStream}; | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #[cfg(feature = "tcpconn")] mod inner { use std::{env, ops::ControlFlow}; use schmoozer::tcpconn::{KillSwitch, SimpleTcpConnector, TcpStream}; pub async fn main() { let args: Vec<String> = env::args().skip(1).collect(); let ks = KillSwitch::new(); let connector = SimpleTcpConnector::new(&args[0], ks, Box::new(proc_connection)); schmoozer::run(connector).await.unwrap(); |
︙ | ︙ |
Changes to src/lib.rs.
1 2 3 4 5 6 7 8 9 10 11 | //! _schmoozer_ is intended to be used as an `async` (re)connector. It //! consists of two parts: //! - The [`Connector`] trait is implemented by applications/libraries that //! need to run retryable connection loops. //! - [`run()`] is a function that takes in a `Connector` implementation, and //! attempts to establish a connection, delaying and retrying on failures //! that the callback reports as retriable, and calls the //! [`Connector::run()`] trait method once a connection has been successfully //! been established. //! //! Perhaps paradoxically the [`run()`] function does not itself actually | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //! _schmoozer_ is intended to be used as an `async` (re)connector. It //! consists of two parts: //! - The [`Connector`] trait is implemented by applications/libraries that //! need to run retryable connection loops. //! - [`run()`] is a function that takes in a `Connector` implementation, and //! attempts to establish a connection, delaying and retrying on failures //! that the callback reports as retriable, and calls the //! [`Connector::run()`] trait method once a connection has been successfully //! been established. //! //! Perhaps paradoxically the [`run()`] function does not itself actually //! attempt to establish any connections -- it relies on the `Connector` trait //! to implement the means to establish connections. //! //! The "good path" overall flow of the connector loop is to call the //! `connect()` method. If it is successful, call the `run()` method, passing //! along the newly allocated connection. The main application logic relating //! to the connection should called from this method. //! |
︙ | ︙ | |||
41 42 43 44 45 46 47 | #[cfg(feature = "tcpconn")] pub use tcpconn::SimpleTcpConnector; /// Application callbacks for the [`run()`] function (or equivalent). #[async_trait] pub trait Connector { /// The connection type. | | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #[cfg(feature = "tcpconn")] pub use tcpconn::SimpleTcpConnector; /// Application callbacks for the [`run()`] function (or equivalent). #[async_trait] pub trait Connector { /// The connection type. type ConnType: Send; /// The application error return type. type Error: Send; /// Establish a connection. /// /// If a connection was successfully established the implementation should /// return `Ok(Self::ConnType)`. /// /// If an error is returned as `Err(ControlFlow::Continue(Self::Error))` it |
︙ | ︙ | |||
96 97 98 99 100 101 102 103 | /// /// # Exit conditions /// The (re)connection loop will keep running until an exit condition has been /// triggered: /// - [`Connector::connect()`] returns `Err(ControlFlow::Break(Self::Error))` /// - [`Connector::retry_delay()`] returns `ControlFlow::Break(Self::Error)` /// - [`Connector::run()`] returns `ControlFlow::Break(_)` pub async fn run<E>( | > | | > > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /// /// # Exit conditions /// The (re)connection loop will keep running until an exit condition has been /// triggered: /// - [`Connector::connect()`] returns `Err(ControlFlow::Break(Self::Error))` /// - [`Connector::retry_delay()`] returns `ControlFlow::Break(Self::Error)` /// - [`Connector::run()`] returns `ControlFlow::Break(_)` #[allow(clippy::missing_errors_doc)] pub async fn run<E>( mut connector: impl Connector<Error = E> + Send ) -> Result<(), E> where E: Send { loop { // Call the application's connect callback to attempt to establish // connection. match connector.connect().await { Ok(conn) => { // A connection was successfully established -- call the run() // implementation. |
︙ | ︙ | |||
133 134 135 136 137 138 139 | } // If this point is reached the application has requested a reconnection. // Call `retry_delay()` to allow the application to determine whether to // retry or not. match connector.retry_delay().await { | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | } // If this point is reached the application has requested a reconnection. // Call `retry_delay()` to allow the application to determine whether to // retry or not. match connector.retry_delay().await { ControlFlow::Continue(()) => { // Application wants to reconnect. continue; } ControlFlow::Break(err) => { // Application does not want to reconnect break Err(err); } } } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to src/tcpconn.rs.
︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 | F: Future< Output = ControlFlow< Result<(), std::io::Error>, Result<(), std::io::Error> > > { pub fn new( addr: impl ToString, ks: KillSwitch, cb: Box<dyn Fn(TcpStream, KillSwitch) -> F + Send> ) -> Self { Self { addr: addr.to_string(), | > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | F: Future< Output = ControlFlow< Result<(), std::io::Error>, Result<(), std::io::Error> > > { #[allow(clippy::needless_pass_by_value)] pub fn new( addr: impl ToString, ks: KillSwitch, cb: Box<dyn Fn(TcpStream, KillSwitch) -> F + Send> ) -> Self { Self { addr: addr.to_string(), |
︙ | ︙ | |||
72 73 74 75 76 77 78 | ErrorKind::NotConnected | ErrorKind::TimedOut => { Err(ControlFlow::Continue(e)) } _ => Err(ControlFlow::Break(e)) } } } | | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | ErrorKind::NotConnected | ErrorKind::TimedOut => { Err(ControlFlow::Continue(e)) } _ => Err(ControlFlow::Break(e)) } } } () = self.ks.wait() => { // Aborted -- use ErrorKind::Other to signal abortion let err = std::io::Error::other(String::from("aborted")); Err(ControlFlow::Break(err)) } } } async fn retry_delay(&mut self) -> ControlFlow<Self::Error> { let dur = Duration::from_secs(self.delay.try_into().unwrap()); tokio::select! { () = self.ks.wait() => { let err = std::io::Error::other(String::from("aborted")); ControlFlow::Break(err) } () = tokio::time::sleep(dur) => { // double sleep duration for each iteration, but cap at 60 seconds self.delay = std::cmp::min(self.delay * 2, 60); ControlFlow::Continue(()) } } } |
︙ | ︙ |
Changes to www/changelog.md.
1 2 3 4 | # Change Log ## [Unreleased] | | > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # Change Log ## [Unreleased] [Details](/vdiff?from=schmoozer-0.2.0&to=trunk) ### Added ### Changed ### Removed --- ## [0.2.0] - 2024-09-16 [Details](/vdiff?from=schmoozer-0.1.2&to=schmoozer-0.2.0) ### Changed - Add `Send` bounds to ensure that `Future`s are `Send`. --- ## [0.1.2] - 2024-03-02 [Details](/vdiff?from=schmoozer-0.1.1&to=schmoozer-0.1.2) ### Changed |
︙ | ︙ |
Changes to www/index.md.
1 2 3 4 5 6 7 8 9 10 11 12 | # schmoozer A simple abstraction over a retryable async operation, such as establishing a TCP connection. ## Feature labels in documentation The crate's documentation uses automatically generated feature labels, which currently requires nightly featuers. To build the documentation locally use: ``` | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # schmoozer A simple abstraction over a retryable async operation, such as establishing a TCP connection. ## Feature labels in documentation The crate's documentation uses automatically generated feature labels, which currently requires nightly featuers. To build the documentation locally use: ``` $ RUSTFLAGS="--cfg docsrs" RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features ``` ## Change log The details of changes can always be found in the timeline, but for a high-level view of changes between released versions there's a manually |
︙ | ︙ |