1use alloc::string::{String, ToString};
8
9use crate::{corety::AzString, props::formatter::PrintAsCssValue};
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(C)]
21pub struct Content {
22 pub inner: AzString,
23}
24
25impl Default for Content {
26 fn default() -> Self {
27 Self {
28 inner: "normal".into(),
29 }
30 }
31}
32
33impl PrintAsCssValue for Content {
34 fn print_as_css_value(&self) -> String {
35 self.inner.as_str().to_string()
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[repr(C)]
42pub struct CounterReset {
43 pub counter_name: AzString,
44 pub value: i32,
45}
46
47impl CounterReset {
48 pub const fn new(counter_name: AzString, value: i32) -> Self {
49 Self {
50 counter_name,
51 value,
52 }
53 }
54
55 pub const fn none() -> Self {
56 Self {
57 counter_name: AzString::from_const_str("none"),
58 value: 0,
59 }
60 }
61
62 pub const fn list_item() -> Self {
63 Self {
64 counter_name: AzString::from_const_str("list-item"),
65 value: 0,
66 }
67 }
68}
69
70impl Default for CounterReset {
71 fn default() -> Self {
72 Self::none()
73 }
74}
75
76impl PrintAsCssValue for CounterReset {
77 fn print_as_css_value(&self) -> String {
78 if self.counter_name.as_str() == "none" {
79 "none".to_string()
80 } else {
81 alloc::format!("{} {}", self.counter_name.as_str(), self.value)
82 }
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
88#[repr(C)]
89pub struct CounterIncrement {
90 pub counter_name: AzString,
91 pub value: i32,
92}
93
94impl CounterIncrement {
95 pub const fn new(counter_name: AzString, value: i32) -> Self {
96 Self {
97 counter_name,
98 value,
99 }
100 }
101
102 pub const fn none() -> Self {
103 Self {
104 counter_name: AzString::from_const_str("none"),
105 value: 0,
106 }
107 }
108
109 pub const fn list_item() -> Self {
110 Self {
111 counter_name: AzString::from_const_str("list-item"),
112 value: 1,
113 }
114 }
115}
116
117impl Default for CounterIncrement {
118 fn default() -> Self {
119 Self::none()
120 }
121}
122
123impl PrintAsCssValue for CounterIncrement {
124 fn print_as_css_value(&self) -> String {
125 if self.counter_name.as_str() == "none" {
126 "none".to_string()
127 } else {
128 alloc::format!("{} {}", self.counter_name.as_str(), self.value)
129 }
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137#[repr(C)]
138pub struct StringSet {
139 pub inner: AzString,
140}
141
142impl Default for StringSet {
143 fn default() -> Self {
144 Self {
145 inner: "none".into(),
146 }
147 }
148}
149
150impl PrintAsCssValue for StringSet {
151 fn print_as_css_value(&self) -> String {
152 self.inner.as_str().to_string()
153 }
154}
155
156impl crate::format_rust_code::FormatAsRustCode for Content {
158 fn format_as_rust_code(&self, _tabs: usize) -> String {
159 format!("Content {{ inner: String::from({:?}) }}", self.inner)
160 }
161}
162
163impl crate::format_rust_code::FormatAsRustCode for CounterReset {
164 fn format_as_rust_code(&self, _tabs: usize) -> String {
165 alloc::format!(
166 "CounterReset {{ counter_name: AzString::from_const_str({:?}), value: {} }}",
167 self.counter_name.as_str(),
168 self.value
169 )
170 }
171}
172
173impl crate::format_rust_code::FormatAsRustCode for CounterIncrement {
174 fn format_as_rust_code(&self, _tabs: usize) -> String {
175 alloc::format!(
176 "CounterIncrement {{ counter_name: AzString::from_const_str({:?}), value: {} }}",
177 self.counter_name.as_str(),
178 self.value
179 )
180 }
181}
182
183impl crate::format_rust_code::FormatAsRustCode for StringSet {
184 fn format_as_rust_code(&self, _tabs: usize) -> String {
185 format!("StringSet {{ inner: String::from({:?}) }}", self.inner)
186 }
187}
188
189#[cfg(feature = "parser")]
192pub mod parser {
193 use super::*;
194
195 pub fn parse_content(input: &str) -> Result<Content, ()> {
197 Ok(Content {
198 inner: input.trim().into(),
199 })
200 }
201
202 fn parse_counter_name_value(input: &str, default_value: i32) -> Result<(AzString, i32), ()> {
203 let trimmed = input.trim();
204
205 if trimmed == "none" {
206 return Ok((AzString::from_const_str("none"), 0));
207 }
208
209 let parts: Vec<&str> = trimmed.split_whitespace().collect();
210
211 if parts.is_empty() {
212 return Err(());
213 }
214
215 let counter_name = parts[0].into();
216 let value = if parts.len() > 1 {
217 parts[1].parse::<i32>().map_err(|_| ())?
218 } else {
219 default_value
220 };
221
222 Ok((counter_name, value))
223 }
224
225 pub fn parse_counter_reset(input: &str) -> Result<CounterReset, ()> {
226 let (counter_name, value) = parse_counter_name_value(input, 0)?;
227 Ok(CounterReset::new(counter_name, value))
228 }
229
230 pub fn parse_counter_increment(input: &str) -> Result<CounterIncrement, ()> {
231 let (counter_name, value) = parse_counter_name_value(input, 1)?;
232 Ok(CounterIncrement::new(counter_name, value))
233 }
234
235 pub fn parse_string_set(input: &str) -> Result<StringSet, ()> {
236 Ok(StringSet {
237 inner: input.trim().into(),
238 })
239 }
240}
241
242#[cfg(feature = "parser")]
243pub use parser::*;
244
245#[cfg(all(test, feature = "parser"))]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_simple_content_parser() {
251 assert_eq!(parse_content("'Hello'").unwrap().inner.as_str(), "'Hello'");
252
253 let reset = parse_counter_reset("page 1").unwrap();
255 assert_eq!(reset.counter_name.as_str(), "page");
256 assert_eq!(reset.value, 1);
257
258 let reset = parse_counter_reset("list-item 0").unwrap();
259 assert_eq!(reset.counter_name.as_str(), "list-item");
260 assert_eq!(reset.value, 0);
261
262 let reset = parse_counter_reset("none").unwrap();
263 assert_eq!(reset.counter_name.as_str(), "none");
264
265 let inc = parse_counter_increment("section").unwrap();
267 assert_eq!(inc.counter_name.as_str(), "section");
268 assert_eq!(inc.value, 1); let inc = parse_counter_increment("list-item 2").unwrap();
271 assert_eq!(inc.counter_name.as_str(), "list-item");
272 assert_eq!(inc.value, 2);
273
274 assert_eq!(
275 parse_string_set("chapter-title content()")
276 .unwrap()
277 .inner
278 .as_str(),
279 "chapter-title content()"
280 );
281 }
282}