1use crate::ident::decode_css_identifier_escapes;
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6struct CanonicalLayerIdentifierKeySealV0(());
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct CanonicalLayerIdentifierKeyV0(String, CanonicalLayerIdentifierKeySealV0);
12
13impl CanonicalLayerIdentifierKeyV0 {
14 pub fn from_authored(authored: &str) -> Option<Self> {
15 let decoded = decode_css_identifier_escapes(authored).into_owned();
16 (!decoded.is_empty()).then_some(Self(decoded, CanonicalLayerIdentifierKeySealV0(())))
17 }
18
19 pub fn as_str(&self) -> &str {
20 self.0.as_str()
21 }
22}
23
24impl serde::Serialize for CanonicalLayerIdentifierKeyV0 {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26 where
27 S: serde::Serializer,
28 {
29 serializer.serialize_str(self.as_str())
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct LayerPathV0 {
36 segments: Vec<CanonicalLayerIdentifierKeyV0>,
37}
38
39impl LayerPathV0 {
40 pub fn from_authored_segments<'a>(segments: impl IntoIterator<Item = &'a str>) -> Option<Self> {
41 let segments = segments
42 .into_iter()
43 .map(CanonicalLayerIdentifierKeyV0::from_authored)
44 .collect::<Option<Vec<_>>>()?;
45 (!segments.is_empty()).then_some(Self { segments })
46 }
47
48 pub fn segments(&self) -> &[CanonicalLayerIdentifierKeyV0] {
49 self.segments.as_slice()
50 }
51
52 pub fn nesting_depth(&self) -> usize {
53 self.segments.len().saturating_sub(1)
54 }
55
56 pub fn canonical_name(&self) -> String {
57 self.segments
58 .iter()
59 .map(|segment| canonical_css_identifier(segment.as_str()))
60 .collect::<Vec<_>>()
61 .join(".")
62 }
63
64 pub fn prefix(&self, segment_count: usize) -> Option<Self> {
65 (segment_count > 0 && segment_count <= self.segments.len()).then(|| Self {
66 segments: self.segments[..segment_count].to_vec(),
67 })
68 }
69
70 pub fn parent(&self) -> Option<Self> {
71 self.prefix(self.segments.len().checked_sub(1)?)
72 }
73
74 pub fn local_name(&self) -> &str {
75 self.segments
76 .last()
77 .map(CanonicalLayerIdentifierKeyV0::as_str)
78 .unwrap_or_default()
79 }
80
81 pub fn joined(&self, local: &Self) -> Self {
82 let mut segments = self.segments.clone();
83 segments.extend(local.segments.iter().cloned());
84 Self { segments }
85 }
86}
87
88fn canonical_css_identifier(identifier: &str) -> String {
89 let chars = identifier.chars().collect::<Vec<_>>();
90 let mut canonical = String::with_capacity(identifier.len());
91 for (index, character) in chars.iter().copied().enumerate() {
92 let ascii_name_character = character.is_ascii_alphabetic()
93 || character == '_'
94 || character == '-'
95 || (character.is_ascii_digit()
96 && index > 0
97 && !(index == 1 && chars.first() == Some(&'-')));
98 if ascii_name_character || !character.is_ascii() {
99 canonical.push(character);
100 } else {
101 canonical.push('\\');
102 canonical.push_str(format!("{:06x}", u32::from(character)).as_str());
103 }
104 }
105 canonical
106}