use crate::errors::ParseError;
use freedesktop_entry_parser::low_level::{parse_entry, SectionBytes};
use std::{fmt::Debug, str::from_utf8};
type Result<T> = std::result::Result<T, ParseError>;
#[derive(Debug, PartialEq, Eq)]
pub struct IndexHeader {
pub name: String,
pub inherits: Option<String>,
pub comment: Option<String>,
}
impl<'a> IndexHeader {
fn parse(section: &SectionBytes<'a>) -> Result<Self> {
let mut name = None;
let mut inherits = None;
let mut comment = None;
for attr in §ion.attrs {
match attr.name {
b"Name" => name = Some(attr.value),
b"Inherits" => inherits = Some(attr.value),
b"Comment" => comment = Some(attr.value),
_ => {}
}
}
let inherits = if let Some(inherits) = inherits {
Some(from_utf8(inherits)?.to_owned())
} else {
None
};
let comment = if let Some(comment) = comment {
Some(from_utf8(comment)?.to_owned())
} else {
None
};
Ok(IndexHeader {
name: from_utf8(name.ok_or(ParseError::MissingHeader)?)?.to_owned(),
inherits,
comment,
})
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Directory<'a> {
pub name: &'a [u8],
pub scale: u16,
pub max_size: u16,
pub min_size: u16,
}
impl<'a> Directory<'a> {
fn parse(section: &SectionBytes<'a>) -> Result<Self> {
let mut size = None;
let mut scale = None;
let mut min_size = None;
let mut max_size = None;
let mut threshold = None;
let mut type_: &[u8] = b"Threshold";
for attr in §ion.attrs {
match attr.name {
b"Size" => {
size = Some(from_utf8(attr.value)?.parse()?);
}
b"Scale" => {
scale = Some(from_utf8(attr.value)?.parse()?);
}
b"Type" => {
type_ = attr.value;
}
b"MaxSize" => {
max_size = Some(from_utf8(attr.value)?.parse()?);
}
b"MinSize" => {
min_size = Some(from_utf8(attr.value)?.parse()?);
}
b"Threshold" => {
threshold = Some(from_utf8(attr.value)?.parse()?);
}
_ => {}
}
}
if size.is_none() {
return Err(ParseError::MissingSize(
from_utf8(section.title)?.to_owned(),
));
}
let size = size.unwrap();
let scale = scale.unwrap_or(1);
match type_ {
b"Threshold" | b"threshold" => {
let threshold = threshold.unwrap_or(2);
Ok(Directory {
name: section.title,
scale,
max_size: size + threshold,
min_size: size - threshold,
})
}
b"Fixed" | b"fixed" => Ok(Directory {
name: section.title,
scale,
max_size: size,
min_size: size,
}),
b"Scalable" | b"scalable" => {
let max_size = max_size.unwrap_or(size);
let min_size = min_size.unwrap_or(size);
Ok(Directory {
name: section.title,
scale,
max_size,
min_size,
})
}
_ => {
return Err(ParseError::InvalidType(
from_utf8(section.title)?.to_owned(),
))
}
}
}
}
impl<'a> Debug for Directory<'a> {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::result::Result<(), std::fmt::Error> {
if let Ok(s) = from_utf8(self.name) {
write!(
f,
"Directory {{ \
name: {}, \
scale: {}, \
min_size: {}, \
max_size: {} }}",
s, self.scale, self.min_size, self.max_size
)
} else {
write!(
f,
"Directory {{ \
name: {:?}, \
scale: {}, \
min_size: {}, \
max_size: {} }}",
self.name, self.scale, self.min_size, self.max_size
)
}
}
}
pub(crate) fn parse_index(
input: &[u8],
) -> Result<(IndexHeader, Vec<Directory>)> {
let mut iter = parse_entry(input);
let first_section = iter.next().ok_or(ParseError::EmptyIndexFile)??;
if first_section.title != b"Icon Theme" {
return Err(ParseError::NoIconTheme);
}
let header = IndexHeader::parse(&first_section)?;
let dirs = iter
.map(|section| match section {
Ok(section) => Directory::parse(§ion),
Err(e) => Err(e.into()),
})
.collect::<Result<Vec<_>>>()?;
Ok((header, dirs))
}
#[cfg(test)]
mod test {
use super::*;
mod fn_parse_index {
use super::*;
#[test]
fn ok() {
let (index, mut dirs) = parse_index(
b"[Icon Theme]\nName=Test Theme\nInherits=Papirus,hicolor\n \
[48x48/apps]\nSize=48\nScale=1\nType=Fixed\n\n\
[scalable/apps]\nSize=128\nMaxSize=128\nMinSize=8\nScale=1\nType=Scalable\n\n\
[128x128/apps]\nSize=128\nThreshold=10\nScale=1\nType=Threshold"
)
.unwrap();
dirs.sort();
let test_index = IndexHeader {
name: "Test Theme".to_owned(),
inherits: Some("Papirus,hicolor".to_owned()),
comment: None,
};
let mut test_dirs = vec![
Directory {
name: &b"128x128/apps"[..],
scale: 1,
max_size: 138,
min_size: 118,
},
Directory {
name: &b"scalable/apps"[..],
scale: 1,
max_size: 128,
min_size: 8,
},
Directory {
name: &b"48x48/apps"[..],
scale: 1,
max_size: 48,
min_size: 48,
},
];
test_dirs.sort();
assert_eq!(index, test_index);
assert_eq!(dirs, test_dirs);
}
}
}