Index: .efiles ================================================================== --- .efiles +++ .efiles @@ -12,5 +12,7 @@ src/futures.rs src/iox.rs src/tokiox.rs src/tokiox/tcpconn.rs src/serde_parsers.rs +examples/fut_if_some.rs +examples/deser.rs Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,8 +1,8 @@ [package] name = "orphanage" -version = "0.0.3" +version = "0.0.4" edition = "2021" license = "0BSD" # https://crates.io/category_slugs categories = [ "network-programming" ] keywords = [ "sqlite", "fs", "path" ] @@ -28,20 +28,26 @@ serde = ["dep:serde", "dep:parse-size"] [dependencies] async-trait = { version = "0.1.82", optional = true } killswitch = { version = "0.4.2", optional = true } -parse-size = { version = "1.0.0", optional = true } +parse-size = { version = "1.1.0", optional = true } rand = { version = "0.8.5" } rusqlite = { version = "0.32.1", optional = true, features = ["functions"] } serde = { version = "1.0.210", optional = true, features = ["derive"] } sha2 = { version = "0.10.7", optional = true } shellexpand = { version = "3.1.0" } tokio = { version = "1.40.0", optional = true, features = [ "macros", "net", "time" ] } +[dev-dependencies] +killswitch = { version = "0.4.2" } +tokio = { version = "1.40.0", features = ["full"] } +tokio-test = { version = "0.4.3" } +toml = { version = "0.8.18" } + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [lints.clippy] ADDED examples/deser.rs Index: examples/deser.rs ================================================================== --- /dev/null +++ examples/deser.rs @@ -0,0 +1,58 @@ +#[cfg(feature = "serde")] +mod inner { + use serde::Deserialize; + + use orphanage::serde_parsers::ExpandedPath; + + #[derive(Debug, Deserialize)] + #[allow(dead_code)] + struct Config { + #[serde(rename = "expanded-path")] + epth: ExpandedPath, + + #[serde(default, rename = "optional-expanded-path")] + optepth: Option + } + + #[cfg(feature = "serde")] + pub fn main() { + match toml::from_str::( + r#" +expanded-path = "~/tmp" +"# + ) { + Ok(config) => { + println!("{config:?}"); + } + Err(e) => { + println!("{e}"); + } + } + + match toml::from_str::( + r#" +optional-expanded-path = "$NonExistent" +"# + ) { + Ok(config) => { + println!("{config:?}"); + } + Err(e) => { + println!("{e}"); + } + } + } +} + + +#[cfg(feature = "serde")] +fn main() { + inner::main(); +} + +#[cfg(not(feature = "serde"))] +fn main() { + println!("Example requires `serde` feature"); +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : ADDED examples/fut_if_some.rs Index: examples/fut_if_some.rs ================================================================== --- /dev/null +++ examples/fut_if_some.rs @@ -0,0 +1,40 @@ +use std::time::Duration; + +use orphanage::futures::if_some; + +use tokio::{task, time::sleep}; + +use killswitch::KillSwitch; + +#[tokio::main(flavor = "multi_thread")] +async fn main() { + let ks = KillSwitch::new(); + + let ks2 = ks.clone(); + task::spawn(async move { + sleep(Duration::from_secs(1)).await; + ks2.trigger(); + }); + + println!("Should sleep for 1 second, and is then killed"); + maybe_kill_sleep(Some(&ks)).await; + + println!("Should sleep for 4 seconds, and then expires"); + maybe_kill_sleep(None).await; +} + + +#[allow(clippy::redundant_pub_crate)] +async fn maybe_kill_sleep(ks: Option<&KillSwitch>) { + let mut kwfut = ks.map(KillSwitch::wait); + tokio::select! { + _ = if_some(&mut kwfut) => { + println!("Killed"); + } + () = sleep(Duration::from_secs(4)) => { + println!("Expired"); + } + } +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: src/serde_parsers.rs ================================================================== --- src/serde_parsers.rs +++ src/serde_parsers.rs @@ -19,13 +19,18 @@ //! //! /// Support `decsize = 20000` and `decsize = "20KB"` //! decsize: DecSize, //! //! /// Support optional `decsize = 20000` and `decsize = "20KB"` -//! decsize_opt: Option +//! decsize_opt: Option, +//! +//! /// Support optional `~/tmp` +//! epth: Option //! } //! ``` + +use std::path::{Path, PathBuf}; use serde::{de::Deserializer, Deserialize}; #[derive(Debug, Default, PartialEq, Eq)] pub struct Count(pub u64); @@ -143,6 +148,42 @@ { decsize(deserializer) } } + +#[derive(Debug, Default, PartialEq, Eq)] +pub struct ExpandedPath(pub PathBuf); + +impl ExpandedPath { + #[must_use] + pub fn get(&self) -> &Path { + &self.0 + } +} + + +#[allow(clippy::missing_errors_doc)] +pub fn expand_path<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de> +{ + let s = String::deserialize(deserializer)?; + match shellexpand::full(&s) { + Ok(value) => Ok(ExpandedPath(PathBuf::from(value.into_owned()))), + Err(e) => { + let msg = format!("Unable to expand path; {e}"); + Err(serde::de::Error::custom(msg)) + } + } +} + +impl<'de> Deserialize<'de> for ExpandedPath { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + expand_path(deserializer) + } +} + // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -3,10 +3,12 @@ ## [Unreleased] [Details](/vdiff?from=orphanage-0.0.3&to=trunk) ### Added + +Add a `ExpandedPath` deserializer. ### Changed ### Removed