atmos/os_lib/css/
counter_scope.rs1extern crate alloc;
7use alloc::collections::BTreeMap;
8use alloc::format;
9use alloc::string::{String, ToString};
10use alloc::vec::Vec;
11use crate::os_lib::css::types::CounterStyleDef;
12
13#[derive(Debug, Clone, PartialEq, Eq, Default)]
15pub struct CounterScopeStack {
16 counters: BTreeMap<String, Vec<i64>>,
18}
19
20impl CounterScopeStack {
21 pub fn new() -> Self {
22 Self {
23 counters: BTreeMap::new(),
24 }
25 }
26
27 pub fn from_map(map: &BTreeMap<String, i64>) -> Self {
29 let mut counters = BTreeMap::new();
30 for (k, &v) in map {
31 counters.insert(k.clone(), alloc::vec![v]);
32 }
33 Self { counters }
34 }
35
36 pub fn to_flat_map(&self) -> BTreeMap<String, i64> {
38 let mut map = BTreeMap::new();
39 for (k, v) in &self.counters {
40 if let Some(&top) = v.last() {
41 map.insert(k.clone(), top);
42 }
43 }
44 map
45 }
46
47 pub fn push_reset(&mut self, name: &str, value: i64) {
51 self.counters
52 .entry(name.to_string())
53 .or_insert_with(Vec::new)
54 .push(value);
55 }
56
57 pub fn pop_reset(&mut self, name: &str) {
59 if let Some(stack) = self.counters.get_mut(name) {
60 stack.pop();
61 if stack.is_empty() {
62 self.counters.remove(name);
63 }
64 }
65 }
66
67 pub fn increment(&mut self, name: &str, value: i64) {
71 let stack = self.counters.entry(name.to_string()).or_insert_with(Vec::new);
72 if let Some(top) = stack.last_mut() {
73 *top += value;
74 } else {
75 stack.push(value);
76 }
77 }
78
79 pub fn set(&mut self, name: &str, value: i64) {
82 let stack = self.counters.entry(name.to_string()).or_insert_with(Vec::new);
83 if let Some(top) = stack.last_mut() {
84 *top = value;
85 } else {
86 stack.push(value);
87 }
88 }
89
90 pub fn get_current(&self, name: &str) -> i64 {
93 self.counters
94 .get(name)
95 .and_then(|stack| stack.last().copied())
96 .unwrap_or(0)
97 }
98
99 pub fn get_all(&self, name: &str) -> &[i64] {
101 self.counters
102 .get(name)
103 .map(|s| s.as_slice())
104 .unwrap_or(&[])
105 }
106
107 pub fn format_counter(
109 &self,
110 name: &str,
111 style: Option<&str>,
112 counter_styles: &[CounterStyleDef],
113 ) -> String {
114 let val = self.get_current(name);
115 format_single_counter_value(val, style, counter_styles)
116 }
117
118 pub fn format_counters(
121 &self,
122 name: &str,
123 sep: &str,
124 style: Option<&str>,
125 counter_styles: &[CounterStyleDef],
126 ) -> String {
127 let all = self.get_all(name);
128 if all.is_empty() {
129 return format_single_counter_value(0, style, counter_styles);
131 }
132 let formatted_pieces: Vec<String> = all
133 .iter()
134 .map(|&val| format_single_counter_value(val, style, counter_styles))
135 .collect();
136 formatted_pieces.join(sep)
137 }
138}
139
140pub fn format_single_counter_value(
142 val: i64,
143 style: Option<&str>,
144 counter_styles: &[CounterStyleDef],
145) -> String {
146 let val_u32 = val.max(0) as u32;
147 match style.map(|s| s.to_ascii_lowercase()).as_deref() {
148 Some("upper-roman") => crate::os_lib::web_engine::number_to_roman(val_u32, true),
149 Some("lower-roman") => crate::os_lib::web_engine::number_to_roman(val_u32, false),
150 Some("upper-alpha") | Some("upper-latin") => {
151 crate::os_lib::web_engine::number_to_alpha(val_u32, true)
152 }
153 Some("lower-alpha") | Some("lower-latin") => {
154 crate::os_lib::web_engine::number_to_alpha(val_u32, false)
155 }
156 Some("decimal") | None => format!("{}", val),
157 Some(custom) => {
158 if let Some(def) = counter_styles.iter().find(|d| d.name == custom) {
159 crate::os_lib::css::types::format_with_counter_style(def, val)
160 } else {
161 format!("{}", val)
162 }
163 }
164 }
165}
166
167pub fn apply_counter_decls_to_stack(
170 specified_values: &BTreeMap<String, String>,
171 stack: &mut CounterScopeStack,
172) -> Vec<String> {
173 let mut pushed_resets = Vec::new();
174 if let Some(reset) = specified_values.get("counter-reset") {
175 for (name, value) in crate::os_lib::css::selector::parse_counter_decl(reset, 0) {
176 stack.push_reset(&name, value);
177 pushed_resets.push(name);
178 }
179 }
180 if let Some(incr) = specified_values.get("counter-increment") {
181 for (name, value) in crate::os_lib::css::selector::parse_counter_decl(incr, 1) {
182 stack.increment(&name, value);
183 }
184 }
185 if let Some(set) = specified_values.get("counter-set") {
186 for (name, value) in crate::os_lib::css::selector::parse_counter_decl(set, 0) {
187 stack.set(&name, value);
188 }
189 }
190 pushed_resets
191}