use std::os::unix::io::{AsRawFd, RawFd};
use crate::arguments::hook::HookToggleAction;
use crate::error::{ArgumentError, Context, SystemError};
use crate::io::fd::HasFixedFd;
use crate::io::fifo::Fifo;
use crate::io::fifo::LineRead;
use crate::stream::Setup;
pub struct ControlFifo {
source: Box<dyn LineRead>,
path: String,
}
impl ControlFifo {
pub fn create(path: String) -> Result<ControlFifo, SystemError> {
let source = Box::new(Fifo::create(&path)?);
Ok(ControlFifo { path, source })
}
pub fn poll(&mut self) -> Result<Vec<Command>, SystemError> {
let lines = self.source.read_lines()?;
let commands = lines
.into_iter()
.filter(|line| !line.is_empty())
.filter_map(|line| match parse_command(&line) {
Ok(effect) => Some(effect),
Err(error) => {
error
.with_context(format!("While parsing the command {}:", line))
.print_err();
None
}
})
.collect();
Ok(commands)
}
pub fn path(&self) -> &str {
&self.path
}
}
pub enum Command {
Toggle(HookToggleAction),
}
fn parse_command(line: &str) -> Result<Command, ArgumentError> {
let mut parts = line.split_whitespace();
let command = match parts.next() {
Some(command) => command,
None => return Err(ArgumentError::new("No command provided.")),
};
let args: Vec<&str> = parts.collect();
match command {
"toggle" => {
let has_toggle_flag = args.is_empty();
let toggle_clauses = args.into_iter().map(str::to_owned).collect();
Ok(Command::Toggle(HookToggleAction::parse(
has_toggle_flag,
toggle_clauses,
)?))
}
_ => Err(ArgumentError::new(format!(
"Unknown command name: {}",
command
))),
}
}
impl Command {
pub fn execute(self, setup: &mut Setup) -> Result<(), ArgumentError> {
match self {
Command::Toggle(action) => {
let effects = action.implement(setup.state(), setup.toggle_indices())?;
for effect in effects {
effect(setup.state_mut());
}
}
}
Ok(())
}
}
impl AsRawFd for ControlFifo {
fn as_raw_fd(&self) -> RawFd {
self.source.as_raw_fd()
}
}
unsafe impl HasFixedFd for ControlFifo {}