1use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[non_exhaustive]
9pub enum QualifierKey {
10 Language,
12 Region,
14 Density,
16 Orientation,
18 Device,
20 Script,
22 ColorMode,
24 Mcc,
26 Mnc,
28 InputDevice,
30 Other(u32),
32}
33
34impl QualifierKey {
35 fn from_code(code: u32) -> QualifierKey {
36 match code {
37 0 => QualifierKey::Language,
38 1 => QualifierKey::Region,
39 2 => QualifierKey::Density,
40 3 => QualifierKey::Orientation,
41 4 => QualifierKey::Device,
42 5 => QualifierKey::Script,
43 6 => QualifierKey::ColorMode,
44 7 => QualifierKey::Mcc,
45 8 => QualifierKey::Mnc,
46 10 => QualifierKey::InputDevice,
47 other => QualifierKey::Other(other),
48 }
49 }
50}
51
52impl fmt::Display for QualifierKey {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 f.write_str(match self {
56 QualifierKey::Language => "language",
57 QualifierKey::Region => "region",
58 QualifierKey::Density => "density",
59 QualifierKey::Orientation => "orientation",
60 QualifierKey::Device => "device",
61 QualifierKey::Script => "script",
62 QualifierKey::ColorMode => "colorMode",
63 QualifierKey::Mcc => "mcc",
64 QualifierKey::Mnc => "mnc",
65 QualifierKey::InputDevice => "inputDevice",
66 QualifierKey::Other(code) => return write!(f, "key{code}"),
67 })
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73#[non_exhaustive]
74pub struct Qualifier {
75 pub key: QualifierKey,
77 pub value: u32,
80}
81
82impl Qualifier {
83 pub(super) fn from_raw(key: u32, value: u32) -> Qualifier {
84 Qualifier { key: QualifierKey::from_code(key), value }
85 }
86
87 fn letters(self) -> String {
90 self.value.to_be_bytes().into_iter().filter(|&b| b != 0).map(|b| if b.is_ascii_graphic() { char::from(b) } else { '?' }).collect()
91 }
92
93 fn enumerator(self, names: &[(u32, &'static str)]) -> Option<&'static str> {
94 names.iter().find(|(value, _)| *value == self.value).map(|(_, name)| *name)
95 }
96}
97
98const DENSITIES: &[(u32, &str)] = &[(120, "sdpi"), (160, "mdpi"), (240, "ldpi"), (320, "xldpi"), (480, "xxldpi"), (640, "xxxldpi")];
99const ORIENTATIONS: &[(u32, &str)] = &[(0, "vertical"), (1, "horizontal")];
100const DEVICES: &[(u32, &str)] = &[(0, "phone"), (1, "tablet"), (2, "car"), (3, "pad"), (4, "tv"), (6, "wearable"), (7, "2in1")];
101const COLOR_MODES: &[(u32, &str)] = &[(0, "dark"), (1, "light")];
102const INPUT_DEVICES: &[(u32, &str)] = &[(0, "pointingdevice")];
103
104impl fmt::Display for Qualifier {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 let named = match self.key {
110 QualifierKey::Language | QualifierKey::Script | QualifierKey::Region => return f.write_str(&self.letters()),
111 QualifierKey::Mcc => return write!(f, "mcc{}", self.value),
112 QualifierKey::Mnc => return write!(f, "mnc{}", self.value),
113 QualifierKey::Density => self.enumerator(DENSITIES),
114 QualifierKey::Orientation => self.enumerator(ORIENTATIONS),
115 QualifierKey::Device => self.enumerator(DEVICES),
116 QualifierKey::ColorMode => self.enumerator(COLOR_MODES),
117 QualifierKey::InputDevice => self.enumerator(INPUT_DEVICES),
118 QualifierKey::Other(_) => None,
119 };
120 match named {
121 Some(name) => f.write_str(name),
122 None => write!(f, "{}={}", self.key, self.value),
123 }
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
130pub struct Qualifiers {
131 params: Vec<Qualifier>,
132}
133
134impl Qualifiers {
135 pub(super) fn new(params: Vec<Qualifier>) -> Qualifiers {
136 Qualifiers { params }
137 }
138
139 pub fn params(&self) -> &[Qualifier] {
141 &self.params
142 }
143
144 pub fn is_base(&self) -> bool {
146 self.params.is_empty()
147 }
148
149 pub fn get(&self, key: QualifierKey) -> Option<u32> {
151 self.params.iter().find(|p| p.key == key).map(|p| p.value)
152 }
153}
154
155impl fmt::Display for Qualifiers {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 if self.params.is_empty() {
161 return f.write_str("base");
162 }
163 for (i, param) in self.params.iter().enumerate() {
164 let attaches = matches!(param.key, QualifierKey::Script | QualifierKey::Region | QualifierKey::Mnc);
165 if i > 0 {
166 f.write_str(if attaches { "_" } else { "-" })?;
167 }
168 write!(f, "{param}")?;
169 }
170 Ok(())
171 }
172}