Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add aliases for custom layout of notification banner #91

Merged
merged 5 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/config/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public! {
public! {
#[derive(ConfigProperty, GenericBuilder, Debug, Clone)]
#[cfg_prop(name(TomlImageProperty), derive(Debug, Clone, Default, Deserialize))]
#[gbuilder(name(GBuilderImageProperty))]
#[gbuilder(name(GBuilderImageProperty), derive(Clone))]
struct ImageProperty {
#[cfg_prop(default(64))]
#[gbuilder(default(64))]
Expand Down Expand Up @@ -171,7 +171,7 @@ impl TryFromValue for ResizingMethod {
public! {
#[derive(ConfigProperty, GenericBuilder, Debug, Default, Clone)]
#[cfg_prop(name(TomlBorder), derive(Debug, Clone, Default, Deserialize))]
#[gbuilder(name(GBuilderBorder))]
#[gbuilder(name(GBuilderBorder), derive(Clone))]
struct Border {
#[cfg_prop(default(0))]
#[gbuilder(default(0))]
Expand Down
2 changes: 1 addition & 1 deletion crates/config/src/spacing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::{de::Visitor, Deserialize};
use shared::value::TryFromValue;

#[derive(GenericBuilder, Debug, Default, Clone)]
#[gbuilder(name(GBuilderSpacing))]
#[gbuilder(name(GBuilderSpacing), derive(Clone))]
pub struct Spacing {
#[gbuilder(default(0))]
top: u8,
Expand Down
4 changes: 2 additions & 2 deletions crates/config/src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use super::{public, Spacing};

public! {
#[derive(ConfigProperty, GenericBuilder, Debug, Clone)]
#[cfg_prop(name(TomlTextProperty),derive(Debug, Clone, Default, Deserialize))]
#[gbuilder(name(GBuilderTextProperty))]
#[cfg_prop(name(TomlTextProperty), derive(Debug, Clone, Default, Deserialize))]
#[gbuilder(name(GBuilderTextProperty), derive(Clone))]
struct TextProperty {
#[cfg_prop(default(true))]
#[gbuilder(default(true))]
Expand Down
207 changes: 106 additions & 101 deletions crates/filetype/src/converter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::collections::HashMap;

use anyhow::bail;
use config::{
display::{Border, GBuilderBorder, GBuilderImageProperty, ImageProperty},
display::{Border, GBuilderBorder},
spacing::{GBuilderSpacing, Spacing},
text::{GBuilderTextProperty, TextProperty},
};
use log::warn;
use pest::iterators::{Pair, Pairs};
Expand All @@ -27,11 +28,57 @@ pub(super) fn convert_into_widgets(mut pairs: Pairs<Rule>) -> anyhow::Result<Wid
"In input should be a parsed Layout"
);

let node_type = pair.into_inner().next().unwrap();
convert_node_type(node_type)
let mut inner_pairs = pair.into_inner();
let mut alias_storage = HashMap::new();

let maybe_aliases = inner_pairs.next().unwrap();
let node_type = if maybe_aliases.as_rule() == Rule::AliasDefinitions {
convert_aliases(maybe_aliases, &mut alias_storage)?;
inner_pairs.next().unwrap()
} else {
maybe_aliases
};

convert_node_type(node_type, &alias_storage)
}

fn convert_aliases<'a>(
alias_definitions: Pair<'a, Rule>,
alias_storage: &mut HashMap<&'a str, GBuilder>,
) -> anyhow::Result<()> {
assert_eq!(
alias_definitions.as_rule(),
Rule::AliasDefinitions,
"In input should be an AliasDefinitions"
);

for alias_definition in alias_definitions.into_inner() {
debug_assert_eq!(
alias_definition.as_rule(),
Rule::AliasDefinition,
"In input should be an AliasDefinition"
);

let mut alias_definition_pairs = alias_definition.into_inner();

let _alias_keyword = alias_definition_pairs.next().unwrap();
let alias_identifier = alias_definition_pairs.next().unwrap().as_str();
let _eq_token = alias_definition_pairs.next().unwrap();
let type_value_definition = alias_definition_pairs.next().unwrap();

alias_storage.insert(
alias_identifier,
convert_type_value(type_value_definition, alias_storage)?,
);
}

Ok(())
}

fn convert_node_type(node_type: Pair<Rule>) -> anyhow::Result<Widget> {
fn convert_node_type<'a>(
node_type: Pair<'a, Rule>,
alias_storage: &'a HashMap<&'a str, GBuilder>,
) -> anyhow::Result<Widget> {
assert_eq!(
node_type.as_rule(),
Rule::NodeType,
Expand All @@ -41,26 +88,29 @@ fn convert_node_type(node_type: Pair<Rule>) -> anyhow::Result<Widget> {
let mut node_type_pairs = node_type.into_inner();

let widget_name = node_type_pairs.next().unwrap().as_str();
let mut widget_gbuilder: GBuilder = widget_name.try_into()?;
let mut widget_gbuilder: GBuilder = (widget_name, alias_storage).try_into()?;

let properties = convert_properties(&mut node_type_pairs);
let properties = convert_properties(&mut node_type_pairs, alias_storage);

let children = convert_children(&mut node_type_pairs)?;
let children = convert_children(&mut node_type_pairs, alias_storage)?;
widget_gbuilder.set_properties(widget_name, properties);

if !children.is_empty() {
let assignment_result =
widget_gbuilder.set_value("children", Value::Any(Box::new(children)));

if let Err(ConversionError::UnknownField { field_name }) = assignment_result {
if let Err(ConversionError::UnknownField { field_name, .. }) = assignment_result {
warn!("The {widget_name} doesn't contain the '{field_name}' field! Skipped.");
}
}

Ok(widget_gbuilder.try_build()?.try_downcast()?)
}

fn convert_properties(node_type_pairs: &mut Pairs<Rule>) -> Vec<Property> {
fn convert_properties<'a>(
node_type_pairs: &mut Pairs<'a, Rule>,
alias_storage: &'a HashMap<&'a str, GBuilder>,
) -> Vec<Property> {
let _open_paren = node_type_pairs.next();

let properties_or_close_paren = node_type_pairs.next().unwrap();
Expand All @@ -81,7 +131,7 @@ fn convert_properties(node_type_pairs: &mut Pairs<Rule>) -> Vec<Property> {
let mut converted_properties = vec![];
let mut properties_pairs = properties.into_inner();
while let Some(property) = properties_pairs.next() {
match convert_property(property) {
match convert_property(property, alias_storage) {
Ok(property) => converted_properties.push(property),
Err(err) => warn!("Failed to parse property and skipped. Error: {err}"),
}
Expand All @@ -91,7 +141,10 @@ fn convert_properties(node_type_pairs: &mut Pairs<Rule>) -> Vec<Property> {
converted_properties
}

fn convert_property(property: Pair<Rule>) -> anyhow::Result<Property> {
fn convert_property<'a>(
property: Pair<'a, Rule>,
alias_storage: &'a HashMap<&'a str, GBuilder>,
) -> anyhow::Result<Property> {
assert_eq!(
property.as_rule(),
Rule::Property,
Expand All @@ -101,12 +154,15 @@ fn convert_property(property: Pair<Rule>) -> anyhow::Result<Property> {
let mut property_pairs = property.into_inner();
let name = property_pairs.next().unwrap().as_str().to_string();
let _eq_token = property_pairs.next();
let value = convert_property_value(property_pairs.next().unwrap())?;
let value = convert_property_value(property_pairs.next().unwrap(), alias_storage)?;

Ok(Property { name, value })
}

fn convert_property_value(property_value: Pair<Rule>) -> anyhow::Result<Value> {
fn convert_property_value<'a>(
property_value: Pair<'a, Rule>,
alias_storage: &'a HashMap<&'a str, GBuilder>,
) -> anyhow::Result<Value> {
assert_eq!(
property_value.as_rule(),
Rule::PropertyValue,
Expand All @@ -116,14 +172,19 @@ fn convert_property_value(property_value: Pair<Rule>) -> anyhow::Result<Value> {
let value = property_value.into_inner().next().unwrap();

Ok(match value.as_rule() {
Rule::TypeValue => convert_type_value(value)?,
Rule::TypeValue => convert_type_value(value, alias_storage)
.and_then(GBuilder::try_build)
.map(Value::Any)?,
Rule::Literal => Value::String(value.as_str().to_string()),
Rule::UInt => Value::UInt(value.as_str().parse().unwrap()),
_ => unreachable!(),
})
}

fn convert_children(node_type_pairs: &mut Pairs<Rule>) -> anyhow::Result<Vec<Widget>> {
fn convert_children<'a>(
node_type_pairs: &mut Pairs<'a, Rule>,
alias_storage: &'a HashMap<&'a str, GBuilder>,
) -> anyhow::Result<Vec<Widget>> {
let open_brace = node_type_pairs.next();

let mut children = None;
Expand All @@ -148,11 +209,14 @@ fn convert_children(node_type_pairs: &mut Pairs<Rule>) -> anyhow::Result<Vec<Wid

children
.into_inner()
.map(convert_node_type)
.map(|child| convert_node_type(child, alias_storage))
.collect::<anyhow::Result<Vec<Widget>>>()
}

fn convert_type_value(type_value: Pair<Rule>) -> anyhow::Result<Value> {
fn convert_type_value<'a>(
type_value: Pair<'a, Rule>,
alias_storage: &'a HashMap<&'a str, GBuilder>,
) -> anyhow::Result<GBuilder> {
assert_eq!(
type_value.as_rule(),
Rule::TypeValue,
Expand All @@ -162,91 +226,36 @@ fn convert_type_value(type_value: Pair<Rule>) -> anyhow::Result<Value> {
let mut type_value_pairs = type_value.into_inner();

let type_name = type_value_pairs.next().unwrap().as_str();
let mut type_gbuilder: GBuilder = type_name.try_into()?;
let mut type_gbuilder: GBuilder = (type_name, alias_storage).try_into()?;

type_gbuilder.set_properties(
type_name,
convert_properties(&mut type_value_pairs, alias_storage),
);

type_gbuilder.set_properties(type_name, convert_properties(&mut type_value_pairs));
type_gbuilder.try_build().map(Value::Any).map(Ok)?
Ok(type_gbuilder)
}

#[derive(Clone)]
enum GBuilder {
FlexContainer(GBuilderFlexContainer),
WImage(GBuilderWImage),
WText(GBuilderWText),

TextProperty(GBuilderTextProperty),
ImageProperty(GBuilderImageProperty),

Spacing(GBuilderSpacing),
Alignment(GBuilderAlignment),
Border(GBuilderBorder),
}

impl GBuilder {
fn get_associated_property(&self) -> Option<GBuilder> {
match self {
GBuilder::WImage(_) => Some(GBuilder::ImageProperty(GBuilderImageProperty::new())),
GBuilder::WText(_) => Some(GBuilder::TextProperty(GBuilderTextProperty::new())),
_ => None,
}
}

fn set_properties(&mut self, self_name: &str, properties: Vec<Property>) {
let mut associated_property = self.get_associated_property();

for Property { name, value } in properties {
let this = if self.contains_field(&name) {
&mut *self
} else if let Some(property) = associated_property.as_mut() {
property
} else {
warn!("The '{name}' field for the {self_name} widget is unknown. Skipped.");
continue;
};

if let Err(err) = this.set_value(&name, value) {
if let Err(err) = self.set_value(&name, value) {
warn!(
"Cannot set value for the '{name}' field due error and skipped. Error: {err}"
"Cannot set value for the '{name}' field in {self_name} due error and skipped. Error: {err}"
);
}
}

if let Some(property) = associated_property {
let property = match property.try_build() {
Ok(property) => Value::Any(property),
Err(err) => {
println!("{err}");
warn!("Failed to analyze properties of the '{self_name}' type and skipped. Error: {err}");
return;
}
};

if let Err(err) = self.set_value("property", property) {
warn!("Failed to apply properties to the '{self_name}' wiget and skipped. Error: {err}");
}
}
}

fn contains_field(&self, field_name: &str) -> bool {
macro_rules! implement_variants {
($($variant:ident),*) => {
match self {
$(
Self::$variant(val) => val.contains_field(field_name),
)*
}
};
}

implement_variants!(
FlexContainer,
WImage,
WText,
TextProperty,
ImageProperty,
Spacing,
Alignment,
Border
)
}

fn set_value(&mut self, field_name: &str, value: Value) -> Result<&mut Self, ConversionError> {
Expand All @@ -262,20 +271,11 @@ impl GBuilder {
};
}

implement_variants!(
FlexContainer,
WImage,
WText,
TextProperty,
ImageProperty,
Spacing,
Alignment,
Border
);
implement_variants!(FlexContainer, WImage, WText, Spacing, Alignment, Border);
Ok(self)
}

fn try_build(self) -> anyhow::Result<Box<dyn std::any::Any>> {
fn try_build(self) -> anyhow::Result<Box<dyn std::any::Any + Send + Sync>> {
macro_rules! implement_variants {
($($variant:ident into $dest_type:path),*) => {
match self {
Expand All @@ -293,28 +293,33 @@ impl GBuilder {
WText into Widget,
FlexContainer into Widget,

TextProperty into TextProperty,
ImageProperty into ImageProperty,

Spacing into Spacing,
Alignment into Alignment,
Border into Border
))
}
}

impl TryFrom<&str> for GBuilder {
impl TryFrom<(&str, &HashMap<&str, GBuilder>)> for GBuilder {
type Error = anyhow::Error;

fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
fn try_from(
(identifier, alias_storage): (&str, &HashMap<&str, GBuilder>),
) -> Result<Self, Self::Error> {
Ok(match identifier {
"FlexContainer" => GBuilder::FlexContainer(GBuilderFlexContainer::new()),
"Image" => GBuilder::WImage(GBuilderWImage::new()),
"Text" => GBuilder::WText(GBuilderWText::new()),
"Spacing" => GBuilder::Spacing(GBuilderSpacing::new()),
"Alignment" => GBuilder::Alignment(GBuilderAlignment::new()),
"Border" => GBuilder::Border(GBuilderBorder::new()),
_ => bail!("Unknown type: {value}!"),
other => {
if let Some(aliased_gbuilder) = alias_storage.get(other).cloned() {
aliased_gbuilder
} else {
bail!("Unknown type: {other}!")
}
}
})
}
}
Expand Down
Loading
Loading