Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,34 +1,48 @@ [package] name = "schmoozer" -version = "0.1.2" +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" +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.77" } +async-trait = { version = "0.1.82" } killswitch = { version = "0.4.2", optional = true } -tokio = { version = "1.36.0", optional = true } +tokio = { version = "1.40.0", optional = true } [dev-dependencies] -tokio = { version = "1.36.0", features = [ +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 Index: bacon.toml ================================================================== --- /dev/null +++ bacon.toml @@ -0,0 +1,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 Index: examples/net.rs ================================================================== --- examples/net.rs +++ examples/net.rs @@ -8,10 +8,11 @@ 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 } @@ -33,24 +34,24 @@ Err(e) => match e.kind() { ErrorKind::ConnectionRefused | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::TimedOut => { - println!("Retryable error: {}", e); + println!("Retryable error: {e}"); Err(ControlFlow::Continue(e)) } _ => { - println!("Fatal error: {}", e); + println!("Fatal error: {e}"); Err(ControlFlow::Break(e)) } } } } async fn retry_delay(&mut self) -> ControlFlow { let dur = Duration::from_secs(self.delay.try_into().unwrap()); - println!("Retrying in {:?} ..", dur); + println!("Retrying in {dur:?} .."); tokio::time::sleep(dur).await; // for next iteration double sleep duration for each iteration, but cap at // 60 seconds Index: examples/tcpconn.rs ================================================================== --- examples/tcpconn.rs +++ examples/tcpconn.rs @@ -2,11 +2,11 @@ mod inner { use std::{env, ops::ControlFlow}; use schmoozer::tcpconn::{KillSwitch, SimpleTcpConnector, TcpStream}; - pub(super) async fn main() { + pub async fn main() { let args: Vec = env::args().skip(1).collect(); let ks = KillSwitch::new(); let connector = SimpleTcpConnector::new(&args[0], ks, Box::new(proc_connection)); Index: src/lib.rs ================================================================== --- src/lib.rs +++ src/lib.rs @@ -7,11 +7,11 @@ //! 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 -//! attemtp to establish any connections -- it relies on the `Connector` trait +//! 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 @@ -43,14 +43,14 @@ /// Application callbacks for the [`run()`] function (or equivalent). #[async_trait] pub trait Connector { /// The connection type. - type ConnType; + type ConnType: Send; /// The application error return type. - type Error; + type Error: Send; /// Establish a connection. /// /// If a connection was successfully established the implementation should /// return `Ok(Self::ConnType)`. @@ -98,13 +98,17 @@ /// 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( - mut connector: impl Connector -) -> Result<(), E> { + mut connector: impl Connector + Send +) -> Result<(), E> +where + E: Send +{ loop { // Call the application's connect callback to attempt to establish // connection. match connector.connect().await { Ok(conn) => { @@ -135,11 +139,11 @@ // 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(_) => { + ControlFlow::Continue(()) => { // Application wants to reconnect. continue; } ControlFlow::Break(err) => { // Application does not want to reconnect Index: src/tcpconn.rs ================================================================== --- src/tcpconn.rs +++ src/tcpconn.rs @@ -31,10 +31,11 @@ Result<(), std::io::Error>, Result<(), std::io::Error> > > { + #[allow(clippy::needless_pass_by_value)] pub fn new( addr: impl ToString, ks: KillSwitch, cb: Box F + Send> ) -> Self { @@ -74,11 +75,11 @@ } _ => Err(ControlFlow::Break(e)) } } } - _ = self.ks.wait() => { + () = self.ks.wait() => { // Aborted -- use ErrorKind::Other to signal abortion let err = std::io::Error::other(String::from("aborted")); Err(ControlFlow::Break(err)) } } @@ -85,15 +86,15 @@ } async fn retry_delay(&mut self) -> ControlFlow { let dur = Duration::from_secs(self.delay.try_into().unwrap()); tokio::select! { - _ = self.ks.wait() => { + () = self.ks.wait() => { let err = std::io::Error::other(String::from("aborted")); ControlFlow::Break(err) } - _ = tokio::time::sleep(dur) => { + () = 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(()) } } Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -1,17 +1,27 @@ # Change Log ## [Unreleased] -[Details](/vdiff?from=schmoozer-0.1.2&to=trunk) +[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) Index: www/index.md ================================================================== --- www/index.md +++ www/index.md @@ -8,12 +8,11 @@ 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 +$ RUSTFLAGS="--cfg docsrs" RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features ``` ## Change log