use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum QualifierKey {
Language,
Region,
Density,
Orientation,
Device,
Script,
ColorMode,
Mcc,
Mnc,
InputDevice,
Other(u32),
}
impl QualifierKey {
fn from_code(code: u32) -> QualifierKey {
match code {
0 => QualifierKey::Language,
1 => QualifierKey::Region,
2 => QualifierKey::Density,
3 => QualifierKey::Orientation,
4 => QualifierKey::Device,
5 => QualifierKey::Script,
6 => QualifierKey::ColorMode,
7 => QualifierKey::Mcc,
8 => QualifierKey::Mnc,
10 => QualifierKey::InputDevice,
other => QualifierKey::Other(other),
}
}
}
impl fmt::Display for QualifierKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
QualifierKey::Language => "language",
QualifierKey::Region => "region",
QualifierKey::Density => "density",
QualifierKey::Orientation => "orientation",
QualifierKey::Device => "device",
QualifierKey::Script => "script",
QualifierKey::ColorMode => "colorMode",
QualifierKey::Mcc => "mcc",
QualifierKey::Mnc => "mnc",
QualifierKey::InputDevice => "inputDevice",
QualifierKey::Other(code) => return write!(f, "key{code}"),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Qualifier {
pub key: QualifierKey,
pub value: u32,
}
impl Qualifier {
pub(super) fn from_raw(key: u32, value: u32) -> Qualifier {
Qualifier { key: QualifierKey::from_code(key), value }
}
fn letters(self) -> String {
self.value.to_be_bytes().into_iter().filter(|&b| b != 0).map(|b| if b.is_ascii_graphic() { char::from(b) } else { '?' }).collect()
}
fn enumerator(self, names: &[(u32, &'static str)]) -> Option<&'static str> {
names.iter().find(|(value, _)| *value == self.value).map(|(_, name)| *name)
}
}
const DENSITIES: &[(u32, &str)] = &[(120, "sdpi"), (160, "mdpi"), (240, "ldpi"), (320, "xldpi"), (480, "xxldpi"), (640, "xxxldpi")];
const ORIENTATIONS: &[(u32, &str)] = &[(0, "vertical"), (1, "horizontal")];
const DEVICES: &[(u32, &str)] = &[(0, "phone"), (1, "tablet"), (2, "car"), (3, "pad"), (4, "tv"), (6, "wearable"), (7, "2in1")];
const COLOR_MODES: &[(u32, &str)] = &[(0, "dark"), (1, "light")];
const INPUT_DEVICES: &[(u32, &str)] = &[(0, "pointingdevice")];
impl fmt::Display for Qualifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let named = match self.key {
QualifierKey::Language | QualifierKey::Script | QualifierKey::Region => return f.write_str(&self.letters()),
QualifierKey::Mcc => return write!(f, "mcc{}", self.value),
QualifierKey::Mnc => return write!(f, "mnc{}", self.value),
QualifierKey::Density => self.enumerator(DENSITIES),
QualifierKey::Orientation => self.enumerator(ORIENTATIONS),
QualifierKey::Device => self.enumerator(DEVICES),
QualifierKey::ColorMode => self.enumerator(COLOR_MODES),
QualifierKey::InputDevice => self.enumerator(INPUT_DEVICES),
QualifierKey::Other(_) => None,
};
match named {
Some(name) => f.write_str(name),
None => write!(f, "{}={}", self.key, self.value),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct Qualifiers {
params: Vec<Qualifier>,
}
impl Qualifiers {
pub(super) fn new(params: Vec<Qualifier>) -> Qualifiers {
Qualifiers { params }
}
pub fn params(&self) -> &[Qualifier] {
&self.params
}
pub fn is_base(&self) -> bool {
self.params.is_empty()
}
pub fn get(&self, key: QualifierKey) -> Option<u32> {
self.params.iter().find(|p| p.key == key).map(|p| p.value)
}
}
impl fmt::Display for Qualifiers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.params.is_empty() {
return f.write_str("base");
}
for (i, param) in self.params.iter().enumerate() {
let attaches = matches!(param.key, QualifierKey::Script | QualifierKey::Region | QualifierKey::Mnc);
if i > 0 {
f.write_str(if attaches { "_" } else { "-" })?;
}
write!(f, "{param}")?;
}
Ok(())
}
}