Skip to main content

azul_css/props/layout/
flow.rs

1//! CSS properties for flowing content into regions (`flow-into`, `flow-from`).
2
3use alloc::string::{String, ToString};
4
5use crate::{corety::AzString, props::formatter::PrintAsCssValue};
6
7// --- flow-into ---
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[repr(C, u8)]
11pub enum FlowInto {
12    None,
13    Named(AzString),
14}
15
16impl Default for FlowInto {
17    fn default() -> Self {
18        Self::None
19    }
20}
21
22impl PrintAsCssValue for FlowInto {
23    fn print_as_css_value(&self) -> String {
24        match self {
25            Self::None => "none".to_string(),
26            Self::Named(s) => s.to_string(),
27        }
28    }
29}
30
31// --- flow-from ---
32
33#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[repr(C, u8)]
35pub enum FlowFrom {
36    None,
37    Named(AzString),
38}
39
40impl Default for FlowFrom {
41    fn default() -> Self {
42        Self::None
43    }
44}
45
46impl PrintAsCssValue for FlowFrom {
47    fn print_as_css_value(&self) -> String {
48        match self {
49            Self::None => "none".to_string(),
50            Self::Named(s) => s.to_string(),
51        }
52    }
53}
54
55// Formatting to Rust code
56impl crate::format_rust_code::FormatAsRustCode for FlowInto {
57    fn format_as_rust_code(&self, _tabs: usize) -> String {
58        match self {
59            FlowInto::None => String::from("FlowInto::None"),
60            FlowInto::Named(s) => format!(
61                "FlowInto::Named(AzString::from_const_str({:?}))",
62                s.as_str()
63            ),
64        }
65    }
66}
67
68impl crate::format_rust_code::FormatAsRustCode for FlowFrom {
69    fn format_as_rust_code(&self, _tabs: usize) -> String {
70        match self {
71            FlowFrom::None => String::from("FlowFrom::None"),
72            FlowFrom::Named(s) => format!(
73                "FlowFrom::Named(AzString::from_const_str({:?}))",
74                s.as_str()
75            ),
76        }
77    }
78}
79
80// --- PARSERS ---
81
82#[cfg(feature = "parser")]
83mod parser {
84    use super::*;
85
86    macro_rules! define_flow_parser {
87        (
88            $fn_name:ident,
89            $struct_name:ident,
90            $error_name:ident,
91            $error_owned_name:ident,
92            $prop_name:expr
93        ) => {
94            #[derive(Clone, PartialEq)]
95            pub enum $error_name<'a> {
96                InvalidValue(&'a str),
97            }
98
99            impl_debug_as_display!($error_name<'a>);
100            impl_display! { $error_name<'a>, {
101                InvalidValue(v) => format!("Invalid {} value: \"{}\"", $prop_name, v),
102            }}
103
104            #[derive(Debug, Clone, PartialEq)]
105            pub enum $error_owned_name {
106                InvalidValue(String),
107            }
108
109            impl<'a> $error_name<'a> {
110                pub fn to_contained(&self) -> $error_owned_name {
111                    match self {
112                        Self::InvalidValue(s) => $error_owned_name::InvalidValue(s.to_string()),
113                    }
114                }
115            }
116
117            impl $error_owned_name {
118                pub fn to_shared<'a>(&'a self) -> $error_name<'a> {
119                    match self {
120                        Self::InvalidValue(s) => $error_name::InvalidValue(s.as_str()),
121                    }
122                }
123            }
124
125            pub fn $fn_name<'a>(input: &'a str) -> Result<$struct_name, $error_name<'a>> {
126                let trimmed = input.trim();
127                if trimmed.is_empty() {
128                    return Err($error_name::InvalidValue(input));
129                }
130                match trimmed {
131                    "none" => Ok($struct_name::None),
132                    // any other value is a custom identifier
133                    ident => Ok($struct_name::Named(ident.to_string().into())),
134                }
135            }
136        };
137    }
138
139    define_flow_parser!(
140        parse_flow_into,
141        FlowInto,
142        FlowIntoParseError,
143        FlowIntoParseErrorOwned,
144        "flow-into"
145    );
146    define_flow_parser!(
147        parse_flow_from,
148        FlowFrom,
149        FlowFromParseError,
150        FlowFromParseErrorOwned,
151        "flow-from"
152    );
153}
154
155#[cfg(feature = "parser")]
156pub use parser::*;
157
158#[cfg(all(test, feature = "parser"))]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_parse_flow_into() {
164        assert_eq!(parse_flow_into("none").unwrap(), FlowInto::None);
165        assert_eq!(
166            parse_flow_into("my-article-flow").unwrap(),
167            FlowInto::Named("my-article-flow".into())
168        );
169        assert!(parse_flow_into("").is_err());
170    }
171
172    #[test]
173    fn test_parse_flow_from() {
174        assert_eq!(parse_flow_from("none").unwrap(), FlowFrom::None);
175        assert_eq!(
176            parse_flow_from("  main-thread  ").unwrap(),
177            FlowFrom::Named("main-thread".into())
178        );
179        assert!(parse_flow_from("").is_err());
180    }
181}