diff --git a/mgr/src/cli.rs b/mgr/src/cli.rs index 988217c..b1724d7 100644 --- a/mgr/src/cli.rs +++ b/mgr/src/cli.rs @@ -8,6 +8,10 @@ pub enum ParseError { UnknownAction { action: String }, #[error("unexpected input: {rest:?}")] UnexpectedInput { rest: Vec }, + #[error("missing argument")] + MissingArgument, + #[error("error parsing argument: {message}")] + ArgumentParse { message: String }, } pub trait CliCommand { diff --git a/mgr/src/lib.rs b/mgr/src/lib.rs index 241474c..f103f92 100644 --- a/mgr/src/lib.rs +++ b/mgr/src/lib.rs @@ -11,6 +11,7 @@ pub mod power; pub mod present; pub(crate) mod pulseaudio; pub(crate) mod redshift; +pub(crate) mod sleep; pub(crate) mod spotify; pub(crate) mod systemd; pub(crate) mod theme; @@ -36,6 +37,8 @@ pub enum ExecError { #[error(transparent)] Brightness(#[from] brightness::Error), #[error(transparent)] + Sleep(#[from] sleep::Error), + #[error(transparent)] Parse(#[from] cli::ParseError), } @@ -49,6 +52,7 @@ pub enum Action { Redshift(redshift::Action), Weather(weather::Action), Brightness(brightness::Action), + Sleep(sleep::Action), } impl wire::WireCommand for Action { @@ -62,6 +66,7 @@ impl wire::WireCommand for Action { 0x06 => Ok(Self::Redshift(redshift::Action::parse_wire(input)?)), 0x07 => Ok(Self::Weather(weather::Action::parse_wire(input)?)), 0x08 => Ok(Self::Brightness(brightness::Action::parse_wire(input)?)), + 0x09 => Ok(Self::Sleep(sleep::Action::parse_wire(input)?)), other => Err(wire::server::ParseError::Unknown(other)), } } @@ -108,6 +113,11 @@ impl wire::WireCommand for Action { v.extend_from_slice(&action.to_wire()); v } + Self::Sleep(action) => { + let mut v = vec![0x09]; + v.extend_from_slice(&action.to_wire()); + v + } } } } @@ -159,6 +169,10 @@ impl cli::CliCommand for Action { &choice, rest, )?)) } + "sleep" => { + let choice = rest.next().ok_or(cli::ParseError::MissingAction)?; + Ok(Self::Sleep(sleep::Action::parse_str(&choice, rest)?)) + } s => Err(cli::ParseError::UnknownAction { action: s.to_owned(), }), @@ -185,6 +199,7 @@ impl Exec for Action { Self::Redshift(action) => Ok(action.execute()?), Self::Weather(action) => Ok(action.execute()?), Self::Brightness(action) => Ok(action.execute()?), + Self::Sleep(action) => Ok(action.execute()?), } } } diff --git a/mgr/src/sleep.rs b/mgr/src/sleep.rs new file mode 100644 index 0000000..2362ffe --- /dev/null +++ b/mgr/src/sleep.rs @@ -0,0 +1,104 @@ +use super::{ + Exec, + cli::{self, CliCommand}, + cmd, + wire::{WireCommand, server}, +}; + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + Cmd(#[from] cmd::Error), + #[error(transparent)] + Cli(#[from] cli::ParseError), +} + +#[derive(Debug, Clone, Copy)] +pub enum Action { + Sleep(std::time::Duration), +} + +impl WireCommand for Action { + fn parse_wire(mut input: impl Iterator) -> Result { + match input.next().ok_or(server::ParseError::Eof)? { + 0x01 => { + const BYTES: usize = (u64::BITS / 8) as usize; + let input = input.take(BYTES).collect::>(); + let input: [u8; BYTES] = + input + .try_into() + .map_err(|vec: Vec| server::ParseError::MissingBytes { + expected: BYTES, + received: vec.len(), + })?; + + let secs = u64::from_le_bytes(input); + let duration = std::time::Duration::from_secs(secs); + Ok(Self::Sleep(duration)) + } + byte => Err(server::ParseError::Unknown(byte)), + } + } + + fn to_wire(&self) -> Vec { + match *self { + Self::Sleep(duration) => { + let mut v = vec![0x01]; + v.extend(duration.as_secs().to_le_bytes()); + v + } + } + } +} + +impl Exec for Action { + type ExecErr = Error; + + fn execute(&self) -> Result, Self::ExecErr> { + match *self { + Self::Sleep(duration) => { + std::thread::sleep(duration); + Ok(None) + } + } + } +} + +impl CliCommand for Action { + type ExecErr = Error; + + fn parse_str( + input: &str, + mut rest: impl Iterator, + ) -> Result + where + Self: Sized, + { + let result = match input { + "sleep" => { + let input = rest.next().ok_or(cli::ParseError::MissingArgument)?; + let seconds = + input + .parse::() + .map_err(|err| cli::ParseError::ArgumentParse { + message: err.to_string(), + })?; + Self::Sleep(std::time::Duration::from_secs(seconds)) + } + s => { + return Err(cli::ParseError::UnknownAction { + action: s.to_owned(), + }); + } + }; + + let rest = rest.collect::>(); + if rest.is_empty() { + Ok(result) + } else { + Err(cli::ParseError::UnexpectedInput { rest }) + } + } +} diff --git a/mgr/src/wire/server.rs b/mgr/src/wire/server.rs index ad37b0b..87e4682 100644 --- a/mgr/src/wire/server.rs +++ b/mgr/src/wire/server.rs @@ -32,6 +32,8 @@ pub enum ParseError { Unknown(u8), #[error("received surplus input: {0:?}")] Surplus(Vec), + #[error("expected {expected} bytes, received only {received}")] + MissingBytes { expected: usize, received: usize }, } fn handle_client(stream: &mut UnixStream) -> Result<(), Error> {