From 7324befafe91768174729c9fe2d218a830a78ca2 Mon Sep 17 00:00:00 2001 From: Esteban Borai Date: Thu, 14 Nov 2024 20:40:21 -0300 Subject: [PATCH] feat: deadline support and rm verbose (#6) - support for timeout (deadline) using humantime expressions. e.g. `wait-on -t 10m ...` - removes log on failed http requests for simplicity (as redundant) --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/bin/command/file.rs | 4 ++-- src/bin/command/http.rs | 4 ++-- src/bin/command/tcp.rs | 4 ++-- src/bin/main.rs | 14 +++++++++++--- src/lib.rs | 19 +++++++++++++++---- src/resource/file.rs | 2 +- src/resource/http.rs | 7 +++---- src/resource/mod.rs | 2 +- src/resource/tcp.rs | 2 +- src/task.rs | 29 +++++++++++++++++++++++++++++ 12 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 src/task.rs diff --git a/Cargo.lock b/Cargo.lock index 8def283..61fe11b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,6 +327,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "1.3.1" @@ -978,6 +984,7 @@ version = "0.0.10" dependencies = [ "anyhow", "clap", + "humantime", "notify", "pin-project", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 3124504..3f6a5be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ path = "src/bin/main.rs" [dependencies] anyhow = "1.0.81" clap = { version = "4.5.4", features = ["std", "derive", "env"] } +humantime = "2.1.0" pin-project = "1.1.5" reqwest = { version = "0.12.4", default-features = false, features = ["rustls-tls"] } tokio = { version = "1.37.0", features = ["io-util", "rt-multi-thread", "macros", "net"] } diff --git a/src/bin/command/file.rs b/src/bin/command/file.rs index ad77290..6bf293e 100644 --- a/src/bin/command/file.rs +++ b/src/bin/command/file.rs @@ -12,8 +12,8 @@ pub struct FileOpt { } impl FileOpt { - pub async fn exec(&self) -> Result<()> { + pub async fn exec(&self, options: &WaitOptions) -> Result<()> { let waiter = FileWaiter::new(self.path.clone()); - waiter.wait(WaitOptions::default()).await + waiter.wait(options).await } } diff --git a/src/bin/command/http.rs b/src/bin/command/http.rs index 7408cdc..edf9f3c 100644 --- a/src/bin/command/http.rs +++ b/src/bin/command/http.rs @@ -12,8 +12,8 @@ pub struct HttpOpt { } impl HttpOpt { - pub async fn exec(&self) -> Result<()> { + pub async fn exec(&self, options: &WaitOptions) -> Result<()> { let waiter = HttpWaiter::new(self.method.clone(), self.url.clone()); - waiter.wait(WaitOptions::default()).await + waiter.wait(options).await } } diff --git a/src/bin/command/tcp.rs b/src/bin/command/tcp.rs index 6fb54e3..83a58c7 100644 --- a/src/bin/command/tcp.rs +++ b/src/bin/command/tcp.rs @@ -15,8 +15,8 @@ pub struct TcpOpt { } impl TcpOpt { - pub async fn exec(&self) -> Result<()> { + pub async fn exec(&self, options: &WaitOptions) -> Result<()> { let waiter = TcpWaiter::new(self.addr, self.port); - waiter.wait(WaitOptions::default()).await + waiter.wait(options).await } } diff --git a/src/bin/main.rs b/src/bin/main.rs index 0c60b97..3cf59a5 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -3,6 +3,8 @@ mod command; use anyhow::Result; use clap::Parser; use command::http::HttpOpt; +use humantime::Duration; +use wait_on::WaitOptions; use self::command::file::FileOpt; use self::command::tcp::TcpOpt; @@ -25,6 +27,9 @@ pub enum Command { #[derive(Debug, Parser)] pub struct Cli { + /// Timeout for waiting tasks + #[clap(long, short = 't', default_value = "1h")] + pub timeout: Duration, #[command(subcommand)] pub command: Command, } @@ -32,10 +37,13 @@ pub struct Cli { #[tokio::main] async fn main() -> Result<()> { let args = Cli::parse(); + let options = WaitOptions { + timeout: args.timeout.into(), + }; match args.command { - Command::File(opt) => opt.exec().await, - Command::Http(opt) => opt.exec().await, - Command::Tcp(opt) => opt.exec().await, + Command::File(opt) => opt.exec(&options).await, + Command::Http(opt) => opt.exec(&options).await, + Command::Tcp(opt) => opt.exec(&options).await, } } diff --git a/src/lib.rs b/src/lib.rs index e1e5203..00ac2ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,16 +2,27 @@ //! such as Files, HTTP Servers, Ports & Sockets pub mod resource; +pub mod task; + +use std::time::Duration; use anyhow::Result; -pub type Millis = u64; +const SECONDS_IN_HOUR: u64 = 3600; /// Options available for waiting on a [`Waitable`]. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct WaitOptions { /// Timeout in milliseconds for the wait operation. - pub timeout: Option, + pub timeout: Duration, +} + +impl Default for WaitOptions { + fn default() -> Self { + Self { + timeout: Duration::from_secs(SECONDS_IN_HOUR), + } + } } /// A [`Waitable`] is an resource that can be waited on. @@ -24,5 +35,5 @@ pub struct WaitOptions { /// implemented by the [`Resource`] enum variants in the `lib` scope. #[allow(async_fn_in_trait)] pub trait Waitable { - async fn wait(self, options: WaitOptions) -> Result<()>; + async fn wait(&self, options: &WaitOptions) -> Result<()>; } diff --git a/src/resource/file.rs b/src/resource/file.rs index 9da3d93..a57d7a9 100644 --- a/src/resource/file.rs +++ b/src/resource/file.rs @@ -17,7 +17,7 @@ impl FileWaiter { } impl Waitable for FileWaiter { - async fn wait(self, _: WaitOptions) -> Result<()> { + async fn wait(&self, _: &WaitOptions) -> Result<()> { let (file_exists_handler, rx) = FileExistsHandler::new(); let mut watcher = notify::recommended_watcher(file_exists_handler).unwrap(); let parent = self.path.parent().unwrap(); diff --git a/src/resource/http.rs b/src/resource/http.rs index b7ff601..43d3863 100644 --- a/src/resource/http.rs +++ b/src/resource/http.rs @@ -18,9 +18,9 @@ impl HttpWaiter { } impl Waitable for HttpWaiter { - async fn wait(self, _: WaitOptions) -> Result<()> { + async fn wait(&self, _: &WaitOptions) -> Result<()> { let client = Client::new(); - let request = Request::new(self.method, self.url); + let request = Request::new(self.method.clone(), self.url.clone()); loop { if let Some(req) = request.try_clone() { @@ -29,8 +29,7 @@ impl Waitable for HttpWaiter { println!("Got {}", res.status()); break; } - Err(err) => { - println!("Rec {}", err); + Err(_) => { sleep(Duration::from_secs(1)).await; continue; } diff --git a/src/resource/mod.rs b/src/resource/mod.rs index a365c2a..27da151 100644 --- a/src/resource/mod.rs +++ b/src/resource/mod.rs @@ -20,7 +20,7 @@ pub enum Resource { } impl Waitable for Resource { - async fn wait(self, options: WaitOptions) -> Result<()> { + async fn wait(&self, options: &WaitOptions) -> Result<()> { match self { Resource::File(file) => file.wait(options).await, Resource::Http(http) => http.wait(options).await, diff --git a/src/resource/tcp.rs b/src/resource/tcp.rs index c2e09ef..27b542a 100644 --- a/src/resource/tcp.rs +++ b/src/resource/tcp.rs @@ -26,7 +26,7 @@ impl TcpWaiter { } impl Waitable for TcpWaiter { - async fn wait(self, _: WaitOptions) -> Result<()> { + async fn wait(&self, _: &WaitOptions) -> Result<()> { let tcp_listener = TcpListener::bind(self.socket()).await?; let (socket, _) = tcp_listener.accept().await?; let mut socket = PacketExtractor::<8>::read(socket).await?; diff --git a/src/task.rs b/src/task.rs new file mode 100644 index 0000000..aaf5c53 --- /dev/null +++ b/src/task.rs @@ -0,0 +1,29 @@ +use anyhow::{bail, Result}; +use tokio::select; +use tokio::time::sleep; + +use crate::resource::Resource; +use crate::{WaitOptions, Waitable}; + +pub struct WaitOnTask { + resource: Resource, + options: WaitOptions, +} + +impl WaitOnTask { + pub fn new(resource: Resource, options: WaitOptions) -> Self { + Self { resource, options } + } + + pub async fn run(self) -> Result<()> { + select! { + _ = self.resource.wait(&self.options) => Ok(()), + _ = self.deadline() => bail!("Deadline reached"), + } + } + + async fn deadline(&self) -> Result<()> { + sleep(self.options.timeout).await; + bail!("Timeout reached"); + } +}