1
//! CSS properties for flowing content into regions (`flow-into`, `flow-from`).
2

            
3
use alloc::string::{String, ToString};
4

            
5
use crate::{corety::AzString, props::formatter::PrintAsCssValue};
6

            
7
// --- flow-into ---
8

            
9
/// CSS `flow-into` property — diverts an element's content into a named flow.
10
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11
#[repr(C, u8)]
12
#[derive(Default)]
13
pub enum FlowInto {
14
    /// Content is not diverted into any named flow (default).
15
    #[default]
16
    None,
17
    /// Content is diverted into the named flow identified by this string.
18
    Named(AzString),
19
}
20

            
21

            
22
impl 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
/// CSS `flow-from` property — consumes content from a named flow into a region.
34
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35
#[repr(C, u8)]
36
#[derive(Default)]
37
pub enum FlowFrom {
38
    /// No named flow is consumed (default).
39
    #[default]
40
    None,
41
    /// Content is consumed from the named flow identified by this string.
42
    Named(AzString),
43
}
44

            
45

            
46
impl 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
56
impl 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

            
68
impl 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")]
83
pub mod parser {
84
    use super::*;
85
    use crate::corety::AzString;
86

            
87
    macro_rules! define_flow_parser {
88
        (
89
            $fn_name:ident,
90
            $struct_name:ident,
91
            $error_name:ident,
92
            $error_owned_name:ident,
93
            $prop_name:expr
94
        ) => {
95
            #[derive(Clone, PartialEq)]
96
            pub enum $error_name<'a> {
97
                InvalidValue(&'a str),
98
            }
99

            
100
            impl_debug_as_display!($error_name<'a>);
101
            impl_display! { $error_name<'a>, {
102
                InvalidValue(v) => format!("Invalid {} value: \"{}\"", $prop_name, v),
103
            }}
104

            
105
            #[derive(Debug, Clone, PartialEq)]
106
            #[repr(C, u8)]
107
            pub enum $error_owned_name {
108
                InvalidValue(AzString),
109
            }
110

            
111
            impl<'a> $error_name<'a> {
112
                pub fn to_contained(&self) -> $error_owned_name {
113
                    match self {
114
                        Self::InvalidValue(s) => $error_owned_name::InvalidValue(s.to_string().into()),
115
                    }
116
                }
117
            }
118

            
119
            impl $error_owned_name {
120
                pub fn to_shared<'a>(&'a self) -> $error_name<'a> {
121
                    match self {
122
                        Self::InvalidValue(s) => $error_name::InvalidValue(s.as_str()),
123
                    }
124
                }
125
            }
126

            
127
6
            pub fn $fn_name<'a>(input: &'a str) -> Result<$struct_name, $error_name<'a>> {
128
6
                let trimmed = input.trim();
129
6
                if trimmed.is_empty() {
130
2
                    return Err($error_name::InvalidValue(input));
131
4
                }
132
4
                match trimmed {
133
4
                    "none" => Ok($struct_name::None),
134
                    // any other value is a custom identifier
135
2
                    ident => Ok($struct_name::Named(ident.to_string().into())),
136
                }
137
6
            }
138
        };
139
    }
140

            
141
    define_flow_parser!(
142
        parse_flow_into,
143
        FlowInto,
144
        FlowIntoParseError,
145
        FlowIntoParseErrorOwned,
146
        "flow-into"
147
    );
148
    define_flow_parser!(
149
        parse_flow_from,
150
        FlowFrom,
151
        FlowFromParseError,
152
        FlowFromParseErrorOwned,
153
        "flow-from"
154
    );
155
}
156

            
157
#[cfg(feature = "parser")]
158
pub use parser::*;
159

            
160
#[cfg(all(test, feature = "parser"))]
161
mod tests {
162
    use super::*;
163

            
164
    #[test]
165
1
    fn test_parse_flow_into() {
166
1
        assert_eq!(parse_flow_into("none").unwrap(), FlowInto::None);
167
1
        assert_eq!(
168
1
            parse_flow_into("my-article-flow").unwrap(),
169
1
            FlowInto::Named("my-article-flow".into())
170
        );
171
1
        assert!(parse_flow_into("").is_err());
172
1
    }
173

            
174
    #[test]
175
1
    fn test_parse_flow_from() {
176
1
        assert_eq!(parse_flow_from("none").unwrap(), FlowFrom::None);
177
1
        assert_eq!(
178
1
            parse_flow_from("  main-thread  ").unwrap(),
179
1
            FlowFrom::Named("main-thread".into())
180
        );
181
1
        assert!(parse_flow_from("").is_err());
182
1
    }
183
}