From dd5078b4be2dbf8627dc0122e0ddbdb47008495a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 18:51:13 +0200 Subject: [PATCH] continue implementing window definition --- Cargo.lock | 1 + Cargo.toml | 2 +- src/config/attributes.rs | 74 +++++++------- src/config/mod.rs | 1 + src/config/script_var_definition.rs | 2 +- src/config/widget_use.rs | 2 +- src/config/window_definition.rs | 52 ++++++---- src/config/window_geometry.rs | 150 ++++++++++++++++++++++++++++ src/parser/ast.rs | 2 +- 9 files changed, 223 insertions(+), 63 deletions(-) create mode 100644 src/config/window_geometry.rs diff --git a/Cargo.lock b/Cargo.lock index 8a5778d..b720e0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,6 +222,7 @@ dependencies = [ "serde_json", "simplexpr", "smart-default", + "strum", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 81ee6d8..81f54f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" lazy_static = "1.4" pretty_assertions = "0.7" -smart-default = "0.6" +strum = { version = "0.21", features = ["derive"] } anyhow = "1" diff --git a/src/config/attributes.rs b/src/config/attributes.rs index 132e292..536bbfa 100644 --- a/src/config/attributes.rs +++ b/src/config/attributes.rs @@ -3,9 +3,14 @@ use std::{ convert::{TryFrom, TryInto}, }; -use simplexpr::{dynval::DynVal, eval::EvalError, SimplExpr}; +use simplexpr::{ + dynval::{DynVal, FromDynVal}, + eval::EvalError, + SimplExpr, +}; use crate::{ + error::AstError, parser::{ ast::{Ast, Span}, from_ast::FromAst, @@ -44,11 +49,11 @@ pub struct UnusedAttrs { #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] pub struct AttrEntry { pub key_span: Span, - pub value: SimplExpr, + pub value: Ast, } impl AttrEntry { - pub fn new(key_span: Span, value: SimplExpr) -> AttrEntry { + pub fn new(key_span: Span, value: Ast) -> AttrEntry { AttrEntry { key_span, value } } } @@ -64,51 +69,42 @@ impl Attributes { Attributes { span, attrs } } - pub fn eval_required>(&mut self, key: &str) -> Result { + pub fn ast_required(&mut self, key: &str) -> Result { let key = AttrName(key.to_string()); match self.attrs.remove(&key) { - Some(AttrEntry { key_span, value }) => { - let value_span = value.span(); - let dynval = value.eval_no_vars().map_err(|err| AttrError::EvaluationError(value_span.into(), err))?; - T::try_from(dynval).map_err(|_| AttrError::AttrTypeError(value_span.into(), key.clone())) - } - None => Err(AttrError::MissingRequiredAttr(self.span, key.clone())), + Some(AttrEntry { key_span, value }) => T::from_ast(value), + None => Err(AttrError::MissingRequiredAttr(self.span, key.clone()).into()), } } - pub fn eval_optional>(&mut self, key: &str) -> Result, AttrError> { - let key = AttrName(key.to_string()); - match self.attrs.remove(&key) { - Some(AttrEntry { key_span, value }) => { - let value_span = value.span(); - let dynval = value.eval_no_vars().map_err(|err| AttrError::EvaluationError(value_span.into(), err))?; - T::try_from(dynval).map(Some).map_err(|_| AttrError::AttrTypeError(value_span.into(), key.clone())) - } + pub fn ast_optional(&mut self, key: &str) -> Result, AstError> { + match self.attrs.remove(&AttrName(key.to_string())) { + Some(AttrEntry { key_span, value }) => T::from_ast(value).map(Some), None => Ok(None), } } - // pub fn parse_required>(&mut self, key: &str) -> Result { - // let key = AttrName(key.to_string()); - // match self.attrs.remove(&key) { - // Some(value) => match value.value.try_into() { - // Ok(value) => Ok(value), - // Err(_) => Err(AttrError::AttrTypeError(value.value.span().into(), key.clone())), - // }, - // None => Err(AttrError::MissingRequiredAttr(self.span, key.clone())), - // } - // } - // - // pub fn parse_optional>(&mut self, key: &str) -> Result, AttrError> { - // let key = AttrName(key.to_string()); - // match self.attrs.remove(&key) { - // Some(value) => match value.value.try_into() { - // Ok(value) => Ok(Some(value)), - // Err(_) => Err(AttrError::AttrTypeError(value.value.span().into(), key.clone())), - // }, - // None => Ok(None), - // } - // } + pub fn primitive_required(&mut self, key: &str) -> Result { + let ast: SimplExpr = self.ast_required(&key)?; + Ok(ast + .eval_no_vars() + .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? + .read_as() + .map_err(|_| AttrError::AttrTypeError(ast.span().into(), AttrName(key.to_string())))?) + } + + pub fn primitive_optional(&mut self, key: &str) -> Result, AstError> { + let ast: SimplExpr = match self.ast_optional(key)? { + Some(ast) => ast, + None => return Ok(None), + }; + Ok(Some( + ast.eval_no_vars() + .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? + .read_as() + .map_err(|_| AttrError::AttrTypeError(ast.span().into(), AttrName(key.to_string())))?, + )) + } /// Consumes the attributes to return a list of unused attributes which may be used to emit a warning. /// TODO actually use this and implement warnings,... lol diff --git a/src/config/mod.rs b/src/config/mod.rs index 91325de..03d20a8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -9,3 +9,4 @@ pub mod var_definition; pub mod widget_definition; pub mod widget_use; pub mod window_definition; +pub mod window_geometry; diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index 9d6d907..147c5c0 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -49,7 +49,7 @@ impl FromAstElementContent for PollScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; - let interval: String = attrs.eval_required("interval")?; + let interval: String = attrs.primitive_required("interval")?; let interval = crate::util::parse_duration(&interval).map_err(|e| AstError::Other(Some(span), e.into()))?; let (_, script) = iter.expect_literal()?; Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index 2c5e314..4bdd06a 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -35,7 +35,7 @@ impl FromAst for WidgetUse { maplit::hashmap! { AttrName("text".to_string()) => AttrEntry::new( span.into(), - SimplExpr::Literal(span.into(), text.clone()) + Ast::Literal(span.into(), text.clone()) ) }, ), diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs index 75dbbcb..b19fb9c 100644 --- a/src/config/window_definition.rs +++ b/src/config/window_definition.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt::Display, str::FromStr}; use simplexpr::{dynval::DynVal, SimplExpr}; @@ -9,15 +9,15 @@ use crate::{ from_ast::{FromAst, FromAstElementContent}, }, spanned, - value::{AttrName, VarName}, + value::{AttrName, NumWithUnit, VarName}, }; -use super::widget_use::WidgetUse; +use super::{widget_use::WidgetUse, window_geometry::WindowGeometry}; #[derive(Debug, Clone, serde::Serialize)] pub struct EwwWindowDefinition { pub name: String, - pub geometry: Option, + pub geometry: Option, pub stacking: WindowStacking, pub monitor_number: Option, pub widget: WidgetUse, @@ -33,17 +33,24 @@ impl FromAstElementContent for EwwWindowDefinition { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; - let monitor_number = attrs.eval_optional("monitor")?; - let resizable = attrs.eval_optional("resizable")?.unwrap_or(true); - let stacking = attrs.eval_optional("stacking")?.unwrap_or(WindowStacking::Foreground); + let monitor_number = attrs.primitive_optional("monitor")?; + let resizable = attrs.primitive_optional("resizable")?.unwrap_or(true); + let stacking = attrs.primitive_optional("stacking")?.unwrap_or(WindowStacking::Foreground); + let geometry = attrs.ast_optional("geometry")?; let widget = iter.expect_any()?; - Ok(Self { name, monitor_number, resizable, widget, stacking }) + Ok(Self { name, monitor_number, resizable, widget, stacking, geometry }) } } +#[derive(Debug, thiserror::Error)] pub struct EnumParseError { - input: String, - expected: Vec<&'static str>, + pub input: String, + pub expected: Vec<&'static str>, +} +impl Display for EnumParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Failed to parse `{}`, must be one of {}", self.input, self.expected.join(", ")) + } } /// Parse a string with a concrete set of options into some data-structure, @@ -62,14 +69,14 @@ macro_rules! enum_parse { match input.as_str() { $( $( $s )|* => Ok($val) ),*, _ => Err(EnumParseError { - input: $name, + input: input, expected: vec![$($($s),*),*], }) } }; } -#[derive(Debug, Clone, PartialEq, Eq, SmartDefault)] +#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault)] pub enum EwwWindowType { #[default] Dock, @@ -102,9 +109,9 @@ pub enum Side { } impl std::str::FromStr for Side { - type Err = anyhow::Error; + type Err = EnumParseError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { enum_parse! { "side", s, "l" | "left" => Side::Left, "r" | "right" => Side::Right, @@ -121,13 +128,18 @@ pub struct StrutDefinition { pub dist: NumWithUnit, } -impl StrutDefinition { - pub fn from_xml_element(xml: XmlElement) -> Result { - Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) +impl FromAstElementContent for StrutDefinition { + fn get_element_name() -> &'static str { + "struts" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let mut attrs = iter.expect_key_values()?; + Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, smart_default::SmartDefault, serde::Serialize)] pub enum WindowStacking { #[default] Foreground, @@ -137,9 +149,9 @@ pub enum WindowStacking { } impl std::str::FromStr for WindowStacking { - type Err = anyhow::Error; + type Err = EnumParseError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { enum_parse! { "WindowStacking", s, "foreground" | "fg" => WindowStacking::Foreground, "background" | "bg" => WindowStacking::Background, diff --git a/src/config/window_geometry.rs b/src/config/window_geometry.rs new file mode 100644 index 0000000..599984e --- /dev/null +++ b/src/config/window_geometry.rs @@ -0,0 +1,150 @@ +use std::collections::HashMap; + +use simplexpr::{dynval::DynVal, SimplExpr}; + +use crate::{enum_parse, error::{AstError, AstResult}, parser::{ + ast::{Ast, AstIterator, Span}, + from_ast::{FromAst, FromAstElementContent}, + }, spanned, value::{AttrName, Coords, VarName}}; + +use super::{widget_use::WidgetUse, window_definition::EnumParseError}; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, strum::Display)] +pub enum AnchorAlignment { + #[strum(serialize = "start")] + #[default] + START, + #[strum(serialize = "center")] + CENTER, + #[strum(serialize = "end")] + END, +} + +impl AnchorAlignment { + pub fn from_x_alignment(s: &str) -> Result { + enum_parse! { "x-alignment", s, + "l" | "left" => AnchorAlignment::START, + "c" | "center" => AnchorAlignment::CENTER, + "r" | "right" => AnchorAlignment::END, + } + } + + pub fn from_y_alignment(s: &str) -> Result { + enum_parse! { "y-alignment", s, + "t" | "top" => AnchorAlignment::START, + "c" | "center" => AnchorAlignment::CENTER, + "b" | "bottom" => AnchorAlignment::END, + } + } + + pub fn alignment_to_coordinate(&self, size_inner: i32, size_container: i32) -> i32 { + match self { + AnchorAlignment::START => 0, + AnchorAlignment::CENTER => (size_container / 2) - (size_inner / 2), + AnchorAlignment::END => size_container - size_inner, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)] +pub struct AnchorPoint { + pub x: AnchorAlignment, + pub y: AnchorAlignment, +} + +impl std::fmt::Display for AnchorPoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use AnchorAlignment::*; + match (self.x, self.y) { + (CENTER, CENTER) => write!(f, "center"), + (x, y) => write!( + f, + "{} {}", + match x { + START => "left", + CENTER => "center", + END => "right", + }, + match y { + START => "top", + CENTER => "center", + END => "bottom", + } + ), + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum AnchorPointParseError { + #[error("Could not parse anchor: Must either be \"center\" or be formatted like \"top left\"")] + WrongFormat(String), + #[error(transparent)] + EnumParseError(#[from] EnumParseError), +} + + +impl std::str::FromStr for AnchorPoint { + type Err = AnchorPointParseError; + + fn from_str(s: &str) -> Result { + if s == "center" { + Ok(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::CENTER }) + } else { + let (first, second) = s + .split_once(' ') + .ok_or_else(|| AnchorPointParseError::WrongFormat(s.to_string()))?; + let x_y_result: Result<_, EnumParseError> = try { + AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? } + }; + x_y_result.or_else(|_| { + Ok(AnchorPoint { x: AnchorAlignment::from_x_alignment(second)?, y: AnchorAlignment::from_y_alignment(first)? }) + }) + } + } +} + +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Serialize)] +pub struct WindowGeometry { + pub anchor_point: AnchorPoint, + pub offset: Coords, + pub size: Coords, +} + +impl FromAstElementContent for WindowGeometry { + fn get_element_name() -> &'static str { + "geometry" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let mut attrs = iter.expect_key_values()?; + Ok(WindowGeometry { + anchor_point: attrs.primitive_optional("anchor")?.unwrap_or_default(), + size: Coords { + x: attrs.primitive_optional("width")?.unwrap_or_default(), + y: attrs.primitive_optional("height")?.unwrap_or_default(), + }, + offset: Coords { + x: attrs.primitive_optional("x")?.unwrap_or_default(), + y: attrs.primitive_optional("y")?.unwrap_or_default(), + }, + }) + } +} + +impl WindowGeometry { + pub fn override_if_given(&self, anchor_point: Option, offset: Option, size: Option) -> Self { + WindowGeometry { + anchor_point: anchor_point.unwrap_or(self.anchor_point), + offset: offset.unwrap_or(self.offset), + size: size.unwrap_or(self.size), + } + } +} + +impl std::fmt::Display for WindowGeometry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}-{} ({})", self.offset, self.size, self.anchor_point) + } +} diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 0861ab7..a9f0903 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -252,7 +252,7 @@ fn parse_key_values(iter: &mut AstIterator>) -> AstRes Some(Ast::Keyword(key_span, kw)) => match iter.next() { Some(value) => { attrs_span.1 = iter.remaining_span.0; - let attr_value = AttrEntry { key_span, value: value.as_simplexpr()? }; + let attr_value = AttrEntry { key_span, value }; data.insert(AttrName(kw), attr_value); } None => {