use { crate::{ cli::{GlobalArgs, IdleArgs, IdleCmd, IdleSetArgs}, tools::tool_client::{Handle, ToolClient}, utils::errorfmt::ErrorFmt, wire::{jay_compositor, jay_idle, JayIdleId}, }, std::{cell::Cell, collections::VecDeque, rc::Rc, str::FromStr}, }; pub fn main(global: GlobalArgs, args: IdleArgs) { let tc = ToolClient::new(global.log_level.into()); let idle = Idle { tc: tc.clone() }; tc.run(idle.run(args)); } struct Idle { tc: Rc, } impl Idle { async fn run(self, args: IdleArgs) { let tc = &self.tc; let comp = tc.jay_compositor().await; let idle = tc.id(); tc.send(jay_compositor::GetIdle { self_id: comp, id: idle, }); match args.command.unwrap_or_default() { IdleCmd::Status => self.status(idle).await, IdleCmd::Set(args) => self.set(idle, args).await, } } async fn status(self, idle: JayIdleId) { let tc = &self.tc; tc.send(jay_idle::GetStatus { self_id: idle }); let interval = Rc::new(Cell::new(0u64)); jay_idle::Interval::handle(tc, idle, interval.clone(), |iv, msg| { iv.set(msg.interval); }); tc.round_trip().await; let minutes = interval.get() / 60; let seconds = interval.get() % 60; if minutes == 0 && seconds == 0 { println!("Interval: disabled"); } else { println!("Interval: {} minutes {} seconds", minutes, seconds); } } async fn set(self, idle: JayIdleId, args: IdleSetArgs) { let tc = &self.tc; let interval; if args.interval.len() == 1 && args.interval[0] == "disabled" { interval = 0; } else { let comp = parse_components(&args.interval); let mut minutes = None; let mut seconds = None; let mut pending_num = None; for comp in comp { match comp { Component::Number(_) if pending_num.is_some() => { fatal!("missing number unit after {}", pending_num.unwrap()) } Component::Number(n) => pending_num = Some(n), Component::Minutes(n) if pending_num.is_none() => { fatal!("`{}` must be preceded by a number", n) } Component::Minutes(_) if minutes.is_some() => { fatal!("minutes specified multiple times") } Component::Minutes(_) => minutes = pending_num.take(), Component::Seconds(n) if pending_num.is_none() => { fatal!("`{}` must be preceded by a number", n) } Component::Seconds(_) if seconds.is_some() => { fatal!("seconds specified multiple times") } Component::Seconds(_) => seconds = pending_num.take(), } } if pending_num.is_some() { fatal!("missing number unit after {}", pending_num.unwrap()); } if minutes.is_none() && seconds.is_none() { fatal!("minutes and/or numbers must be specified"); } interval = minutes.unwrap_or(0) * 60 + seconds.unwrap_or(0); } tc.send(jay_idle::SetInterval { self_id: idle, interval, }); tc.round_trip().await; } } #[derive(Debug)] enum Component { Number(u64), Minutes(String), Seconds(String), } fn parse_components(args: &[String]) -> Vec { let mut args = VecDeque::from_iter(args.iter().map(|s| s.to_ascii_lowercase())); let mut res = vec![]; while let Some(arg) = args.pop_front() { if arg.is_empty() { continue; } let mut arg = &arg[..]; if is_num(arg.as_bytes()[0]) { if let Some(pos) = arg.as_bytes().iter().position(|&a| !is_num(a)) { args.push_front(arg[pos..].to_string()); arg = &arg[..pos]; } match u64::from_str(arg) { Ok(n) => res.push(Component::Number(n)), Err(e) => fatal!("Could not parse `{}` as a number: {}", arg, ErrorFmt(e)), } } else { if let Some(pos) = arg.as_bytes().iter().position(|&a| is_num(a)) { args.push_front(arg[pos..].to_string()); arg = &arg[..pos]; } let comp = match &arg[..] { "minutes" | "minute" | "min" | "m" => Component::Minutes(arg.to_string()), "seconds" | "second" | "sec" | "s" => Component::Seconds(arg.to_string()), _ => fatal!("Could not parse `{}`", arg), }; res.push(comp); } } res } fn is_num(b: u8) -> bool { matches!(b, b'0'..=b'9') }