use serde::{Deserialize, Serialize};
pub use crate::types::argv::{ParseError, parse_command};
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Direction {
North,
South,
West,
East,
First,
Last,
Nth(usize),
}
impl Direction {
#[must_use]
pub fn reverse(&self) -> Self {
match self {
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::West => Direction::East,
Direction::East => Direction::West,
Direction::First => Direction::Last,
Direction::Last => Direction::First,
Direction::Nth(index) => Direction::Nth(*index),
}
}
pub fn parse(name: &str) -> Result<Self, ParseError> {
Ok(match name {
"north" => Direction::North,
"south" => Direction::South,
"west" => Direction::West,
"east" => Direction::East,
"first" => Direction::First,
"last" => Direction::Last,
other => return Err(ParseError::new(format!("unhandled direction '{other}'"))),
})
}
pub fn parse_positional(input: &str) -> Result<Self, ParseError> {
match input.parse::<usize>() {
Ok(0) => Err(ParseError::new("window numbers start at 1")),
Ok(number) => Ok(Direction::Nth(number - 1)),
Err(_) => Direction::parse(input),
}
}
#[must_use]
pub fn token(&self) -> String {
match self {
Direction::North => "north".into(),
Direction::South => "south".into(),
Direction::West => "west".into(),
Direction::East => "east".into(),
Direction::First => "first".into(),
Direction::Last => "last".into(),
Direction::Nth(index) => (index + 1).to_string(),
}
}
}
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum DirectionRepr {
North,
South,
West,
East,
First,
Last,
Nth(usize),
}
impl From<DirectionRepr> for Direction {
fn from(repr: DirectionRepr) -> Self {
match repr {
DirectionRepr::North => Self::North,
DirectionRepr::South => Self::South,
DirectionRepr::West => Self::West,
DirectionRepr::East => Self::East,
DirectionRepr::First => Self::First,
DirectionRepr::Last => Self::Last,
DirectionRepr::Nth(index) => Self::Nth(index),
}
}
}
impl<'de> Deserialize<'de> for Direction {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum Repr {
Name(String),
Position(i64),
}
if !deserializer.is_human_readable() {
return DirectionRepr::deserialize(deserializer).map(Direction::from);
}
match Repr::deserialize(deserializer)? {
Repr::Name(name) => Direction::parse(&name).map_err(serde::de::Error::custom),
Repr::Position(number) => usize::try_from(number)
.ok()
.filter(|number| *number > 0)
.map(|number| Direction::Nth(number - 1))
.ok_or_else(|| serde::de::Error::custom("window numbers start at 1")),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ResizeDirection {
Grow,
Shrink,
}
impl ResizeDirection {
pub fn parse(input: &str) -> Result<Self, ParseError> {
Ok(match input {
"grow" => ResizeDirection::Grow,
"shrink" => ResizeDirection::Shrink,
other => {
return Err(ParseError::new(format!(
"unhandled resize direction '{other}'"
)));
}
})
}
#[must_use]
pub fn token(self) -> &'static str {
match self {
ResizeDirection::Grow => "grow",
ResizeDirection::Shrink => "shrink",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MoveFocus {
Follow,
Stay,
}
impl MoveFocus {
#[must_use]
pub fn follows(follow: bool) -> Self {
if follow { Self::Follow } else { Self::Stay }
}
}
pub fn parse_virtual_workspace_number(input: &str) -> Result<u32, ParseError> {
let number = input
.parse::<u32>()
.map_err(|_| ParseError::new(format!("unhandled virtual workspace '{input}'")))?;
if number == 0 {
return Err(ParseError::new("virtual workspace numbers start at 1"));
}
Ok(number - 1)
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Operation {
Focus(Direction),
Swap(Direction),
Center,
Resize(ResizeDirection),
ResizeVertical(ResizeDirection),
SetWidth(f64),
FullWidth,
ToNextDisplay(MoveFocus),
Equalize,
Balance,
Manage,
Stack(bool),
ToggleTabbedDisplay,
Snap,
Virtual(Direction),
FocusOrVirtual(Direction),
VirtualNumber(u32),
VirtualAdd,
VirtualMove(Direction, MoveFocus),
VirtualMoveNumber(u32, MoveFocus),
FocusUnmanaged,
FocusManaged,
RaiseFloating,
ToggleFloatingLayer,
CopyRule,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MouseMove {
ToNextDisplay,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Command {
Window(Operation),
Mouse(MouseMove),
Quit,
Restart,
PrintState,
Lua(u32),
Layout(Vec<crate::types::windowset::LayoutOp>),
}