atmos/os_lib/css/
rule_index.rs1extern crate alloc;
26
27use super::types::{Rule, Selector, SimpleSelector};
28use alloc::collections::BTreeMap;
29use alloc::string::{String, ToString};
30use alloc::vec::Vec;
31
32#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
34pub enum RuleKey {
35 Id(String),
37 Class(String),
39 Tag(String),
41 Universal,
44}
45
46#[derive(Debug, PartialEq, Eq, Clone)]
48pub enum IndexError {
49 EmptyTagName,
51}
52
53fn key_from_simple(s: &SimpleSelector) -> RuleKey {
55 if let Some(id) = &s.id {
56 if !id.is_empty() {
57 return RuleKey::Id(id.clone());
58 }
59 }
60 if let Some(c) = s.class.iter().find(|c| !c.is_empty()) {
61 return RuleKey::Class(c.clone());
62 }
63 if let Some(t) = &s.tag_name {
64 if !t.is_empty() {
65 return RuleKey::Tag(t.to_lowercase());
67 }
68 }
69 RuleKey::Universal
71}
72
73pub fn rightmost_key(sel: &Selector) -> RuleKey {
75 match sel {
76 Selector::Simple(s) => key_from_simple(s),
77 Selector::Chain(steps) => match steps.last() {
78 Some(step) => key_from_simple(&step.simple),
79 None => RuleKey::Universal,
81 },
82 }
83}
84
85#[derive(Debug, Default)]
88pub struct RuleIndex {
89 by_id: BTreeMap<String, Vec<usize>>,
90 by_class: BTreeMap<String, Vec<usize>>,
91 by_tag: BTreeMap<String, Vec<usize>>,
92 universal: Vec<usize>,
93 rule_count: usize,
94 state_masks: Vec<u8>,
101}
102
103pub const STATE_HOVER: u8 = 1 << 0;
105pub const STATE_FOCUS: u8 = 1 << 1;
106pub const STATE_ACTIVE: u8 = 1 << 2;
107
108impl RuleIndex {
109 pub fn states_of(&self, cands: &[usize]) -> Option<u8> {
123 let mut m = 0u8;
124 for &i in cands {
125 match self.state_masks.get(i) {
126 Some(bits) => m |= bits,
127 None => return None,
128 }
129 if m == STATE_HOVER | STATE_FOCUS | STATE_ACTIVE {
130 break;
131 }
132 }
133 Some(m)
134 }
135
136 pub fn build(rules: &[Rule]) -> Self {
141 let mut idx = RuleIndex {
142 rule_count: rules.len(),
143 ..Default::default()
144 };
145 idx.state_masks = rules
146 .iter()
147 .map(|r| {
148 let mut m = 0u8;
149 if super::pseudo_state::rule_uses_state(r, "hover") {
150 m |= STATE_HOVER;
151 }
152 if super::pseudo_state::rule_uses_state(r, "focus") {
153 m |= STATE_FOCUS;
154 }
155 if super::pseudo_state::rule_uses_state(r, "active") {
156 m |= STATE_ACTIVE;
157 }
158 m
159 })
160 .collect();
161 for (i, rule) in rules.iter().enumerate() {
162 if rule.selectors.is_empty() {
163 idx.universal.push(i);
165 continue;
166 }
167 for sel in &rule.selectors {
168 match rightmost_key(sel) {
169 RuleKey::Id(v) => push_unique(idx.by_id.entry(v).or_default(), i),
170 RuleKey::Class(v) => push_unique(idx.by_class.entry(v).or_default(), i),
171 RuleKey::Tag(v) => push_unique(idx.by_tag.entry(v).or_default(), i),
172 RuleKey::Universal => push_unique(&mut idx.universal, i),
173 }
174 }
175 }
176 idx
177 }
178
179 pub fn rule_count(&self) -> usize {
181 self.rule_count
182 }
183
184 pub fn universal_count(&self) -> usize {
187 self.universal.len()
188 }
189
190 pub fn candidates(
198 &self,
199 tag: &str,
200 id: Option<&str>,
201 classes: &[String],
202 ) -> Result<Vec<usize>, IndexError> {
203 if tag.trim().is_empty() {
204 crate::error!(
205 "[CSS][RULEIDX] 要素のタグ名が空です (id={:?} classes={})。全ルール照合へフォールバックします",
206 id,
207 classes.len()
208 );
209 return Err(IndexError::EmptyTagName);
210 }
211
212 let mut list: Vec<usize> = Vec::with_capacity(self.universal.len() + 16);
213 list.extend_from_slice(&self.universal);
214
215 if let Some(id) = id {
216 if !id.is_empty() {
217 if let Some(v) = self.by_id.get(id) {
218 list.extend_from_slice(v);
219 }
220 }
221 }
222 for c in classes {
223 if !c.is_empty() {
224 if let Some(v) = self.by_class.get(c) {
225 list.extend_from_slice(v);
226 }
227 }
228 }
229 if let Some(v) = self.by_tag.get(&tag.to_lowercase()) {
230 list.extend_from_slice(v);
231 }
232
233 list.sort_unstable();
234 list.dedup();
235
236 Ok(list)
237 }
238}
239
240fn push_unique(v: &mut Vec<usize>, i: usize) {
242 if v.last() != Some(&i) {
243 v.push(i);
244 }
245}
246
247impl RuleKey {
249 pub fn describe(&self) -> String {
250 match self {
251 RuleKey::Id(v) => {
252 let mut s = String::from("#");
253 s.push_str(v);
254 s
255 }
256 RuleKey::Class(v) => {
257 let mut s = String::from(".");
258 s.push_str(v);
259 s
260 }
261 RuleKey::Tag(v) => v.clone(),
262 RuleKey::Universal => "*".to_string(),
263 }
264 }
265}