Remove 🐄 to simplify API, reducing lifetimes

Okay. I'll admit it. It was a bad idea. You don't need zero-copy theme parsing if you're only going to use copied versions everywhere anyways.
This commit is contained in:
Ridan Vandenbergh 2025-06-20 19:17:09 +02:00
parent bae6cb822e
commit b58a34b589
No known key found for this signature in database
3 changed files with 35 additions and 109 deletions

View file

@ -50,7 +50,6 @@
//! - it does not scan "standalone" icons correctly, such as those usually found in `/usr/share/pixmaps`. //! - it does not scan "standalone" icons correctly, such as those usually found in `/usr/share/pixmaps`.
//! - it adopts a one-shot approach, repeating all parsing and file-finding work for each icon. //! - it adopts a one-shot approach, repeating all parsing and file-finding work for each icon.
//! - it does not provide support for caching. //! - it does not provide support for caching.
//! - it does not support zero-copy parsing of icon theme metadata.
//! //!
//! - [icon-loader](https://crates.io/crates/icon-loader) also implements icon finding, but: //! - [icon-loader](https://crates.io/crates/icon-loader) also implements icon finding, but:
//! - like `linicon`, it does not scan "standalone" icons correctly. //! - like `linicon`, it does not scan "standalone" icons correctly.

View file

@ -1,5 +1,5 @@
use crate::icon::IconFile; use crate::icon::IconFile;
use crate::theme::{OwnedThemeDescriptor, Theme, ThemeDescriptor, ThemeParseError}; use crate::theme::{Theme, ThemeDescriptor, ThemeParseError};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::path::PathBuf; use std::path::PathBuf;
@ -100,11 +100,11 @@ pub struct IconLocations {
} }
impl IconLocations { impl IconLocations {
pub fn resolve(&self) -> Vec<Arc<Theme<'static>>> { pub fn resolve(&self) -> Vec<Arc<Theme>> {
self.resolve_only(self.themes_directories.keys()) self.resolve_only(self.themes_directories.keys())
} }
pub fn resolve_only<I, S>(&self, theme_names: I) -> Vec<Arc<Theme<'static>>> pub fn resolve_only<I, S>(&self, theme_names: I) -> Vec<Arc<Theme>>
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
@ -148,7 +148,7 @@ impl IconLocations {
// Collect all parents of this theme: // Collect all parents of this theme:
for parent in parents { for parent in parents {
collect_themes(parent.as_ref().as_ref(), locations, themes); collect_themes(parent.as_ref(), locations, themes);
} }
} }
@ -282,7 +282,7 @@ impl IconLocations {
full_themes full_themes
} }
pub fn theme_description<S>(&self, internal_name: S) -> std::io::Result<OwnedThemeDescriptor> pub fn theme_description<S>(&self, internal_name: S) -> std::io::Result<ThemeDescriptor>
where where
S: AsRef<OsStr>, S: AsRef<OsStr>,
{ {

View file

@ -1,32 +1,26 @@
use crate::icon::IconFile; use crate::icon::IconFile;
use crate::theme::ThemeParseError::MissingRequiredAttribute; use crate::theme::ThemeParseError::MissingRequiredAttribute;
use freedesktop_entry_parser::low_level::{EntryIter, SectionBytes}; use freedesktop_entry_parser::low_level::{EntryIter, SectionBytes};
use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
pub type OwnedIcons = Icons<'static>; pub struct Icons {
pub type OwnedThemeDescriptor = ThemeDescriptor<'static>;
pub type OwnedThemeIndex = ThemeIndex<'static>;
pub type OwnedDirectoryIndex = DirectoryIndex<'static>;
pub struct Icons<'a> {
pub standalone_icons: Vec<IconFile>, pub standalone_icons: Vec<IconFile>,
pub themes: HashMap<OsString, Arc<Theme<'a>>>, pub themes: HashMap<OsString, Arc<Theme>>,
} }
pub struct Theme<'a> { pub struct Theme {
pub description: ThemeDescriptor<'a>, pub description: ThemeDescriptor,
pub parents: Vec<Arc<Theme<'a>>>, pub parents: Vec<Arc<Theme>>,
} }
pub struct ThemeDescriptor<'a> { pub struct ThemeDescriptor {
pub internal_name: String, pub internal_name: String,
pub base_dirs: Vec<PathBuf>, pub base_dirs: Vec<PathBuf>,
pub index_location: PathBuf, pub index_location: PathBuf,
pub index: ThemeIndex<'a>, pub index: ThemeIndex,
// additional groups? // additional groups?
} }
@ -48,7 +42,7 @@ pub enum ThemeParseError {
ParseError(#[from] freedesktop_entry_parser::ParseError), ParseError(#[from] freedesktop_entry_parser::ParseError),
} }
impl ThemeDescriptor<'_> { impl ThemeDescriptor {
pub fn new_from_folders(internal_name: String, folders: Vec<PathBuf>) -> std::io::Result<Self> { pub fn new_from_folders(internal_name: String, folders: Vec<PathBuf>) -> std::io::Result<Self> {
let index_location = folders let index_location = folders
.iter() .iter()
@ -65,52 +59,36 @@ impl ThemeDescriptor<'_> {
index, index,
}) })
} }
pub fn into_owned(self) -> OwnedThemeDescriptor {
theme_into_owned(self)
}
} }
fn theme_into_owned(theme: ThemeDescriptor) -> OwnedThemeDescriptor { pub struct ThemeIndex {
let base_dirs = theme.base_dirs; pub name: String,
let index = theme.index.into_owned(); pub comment: String,
pub inherits: Vec<String>,
OwnedThemeDescriptor { pub directories: Vec<DirectoryIndex>,
base_dirs,
index,
..theme
}
}
pub struct ThemeIndex<'a> {
pub name: Cow<'a, str>,
pub comment: Cow<'a, str>,
pub inherits: Vec<Cow<'a, str>>,
pub directories: Vec<DirectoryIndex<'a>>,
pub hidden: bool, pub hidden: bool,
pub example: Option<Cow<'a, str>>, pub example: Option<String>,
} }
impl<'a> ThemeIndex<'a> { impl ThemeIndex {
pub fn parse_from_file(path: &Path) -> std::io::Result<OwnedThemeIndex> { pub fn parse_from_file(path: &Path) -> std::io::Result<Self> {
let bytes = std::fs::read(path)?; let bytes = std::fs::read(path)?;
let index = ThemeIndex::parse(&bytes).map_err(std::io::Error::other)?; let index = ThemeIndex::parse(&bytes).map_err(std::io::Error::other)?;
Ok(index.into_owned()) Ok(index)
} }
pub fn parse(bytes: &'a [u8]) -> Result<Self, ThemeParseError> { pub fn parse(bytes: &[u8]) -> Result<Self, ThemeParseError> {
let mut entry: EntryIter<'a> = freedesktop_entry_parser::low_level::parse_entry(bytes); let mut entry: EntryIter = freedesktop_entry_parser::low_level::parse_entry(bytes);
let icon_theme_section: SectionBytes<'a> = let icon_theme_section: SectionBytes =
entry.next().ok_or(ThemeParseError::NotAnIconTheme)??; entry.next().ok_or(ThemeParseError::NotAnIconTheme)??;
let name: &'a str = find_attr_req(&icon_theme_section, "Name")?; let name: &str = find_attr_req(&icon_theme_section, "Name")?;
// SPEC: `Comment` is required, but most icon theme developers can't actually be arsed to // SPEC: `Comment` is required, but most icon theme developers can't actually be arsed to
// include it! To make `icon` practical, we choose a default of an empty string instead. // include it! To make `icon` practical, we choose a default of an empty string instead.
// let comment = find_attr_req(&icon_theme_section, "Comment")?; // let comment = find_attr_req(&icon_theme_section, "Comment")?;
let comment = find_attr(&icon_theme_section, "Comment")? let comment = find_attr(&icon_theme_section, "Comment")?.unwrap_or("");
.unwrap_or("");
// If no theme is specified, implementations are required to add the "hicolor" theme to the inheritance tree. // If no theme is specified, implementations are required to add the "hicolor" theme to the inheritance tree.
let inherits = find_attr(&icon_theme_section, "Inherits")? let inherits = find_attr(&icon_theme_section, "Inherits")?
.iter() .iter()
@ -165,53 +143,23 @@ impl<'a> ThemeIndex<'a> {
example: example.map(Into::into), example: example.map(Into::into),
}) })
} }
pub fn into_owned(self) -> OwnedThemeIndex {
theme_index_into_owned(self)
}
} }
fn theme_index_into_owned(index: ThemeIndex) -> OwnedThemeIndex { pub struct DirectoryIndex {
let name = index.name.into_owned().into(); pub directory_name: String,
let comment = index.comment.into_owned().into();
let inherits = index
.inherits
.into_iter()
.map(Cow::into_owned)
.map(Into::into)
.collect();
let directories = index
.directories
.into_iter()
.map(|x| x.into_owned())
.collect();
let example = index.example.map(Cow::into_owned).map(Into::into);
OwnedThemeIndex {
name,
comment,
inherits,
directories,
example,
..index
}
}
pub struct DirectoryIndex<'a> {
pub directory_name: Cow<'a, str>,
pub is_scaled_dir: bool, pub is_scaled_dir: bool,
pub size: u32, pub size: u32,
pub scale: u32, pub scale: u32,
pub context: Option<Cow<'a, str>>, pub context: Option<String>,
pub directory_type: DirectoryType, pub directory_type: DirectoryType,
pub max_size: u32, pub max_size: u32,
pub min_size: u32, pub min_size: u32,
pub threshold: u32, pub threshold: u32,
// pub additional_values: HashMap<Cow<'a, str>, Cow<'a, str>>, // pub additional_values: HashMap<String, String>,
} }
impl<'a> DirectoryIndex<'a> { impl DirectoryIndex {
fn parse(section: SectionBytes<'a>) -> Result<Self, ThemeParseError> { fn parse(section: SectionBytes) -> Result<Self, ThemeParseError> {
let dir_name = str::from_utf8(section.title)?; let dir_name = str::from_utf8(section.title)?;
let size: u32 = find_attr_req(&section, "Size")?.parse()?; let size: u32 = find_attr_req(&section, "Size")?.parse()?;
let scale: u32 = find_attr(&section, "Scale")? let scale: u32 = find_attr(&section, "Scale")?
@ -299,27 +247,6 @@ impl<'a> DirectoryIndex<'a> {
} }
} }
} }
pub fn into_owned(self) -> OwnedDirectoryIndex {
dir_index_into_owned(self)
}
}
fn dir_index_into_owned(index: DirectoryIndex) -> OwnedDirectoryIndex {
let directory_name: Cow<'static, str> = index.directory_name.into_owned().into();
let context: Option<Cow<'static, str>> = index.context.map(|c| c.into_owned().into());
// let additional_values: HashMap<Cow<'static, str>, Cow<'static, str>> = index
// .additional_values
// .into_iter()
// .map(|(k, v)| (Cow::Owned(k.into_owned()), Cow::Owned(v.into_owned())))
// .collect();
OwnedDirectoryIndex {
directory_name,
context,
// additional_values,
..index
}
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
@ -345,7 +272,7 @@ impl TryFrom<&str> for DirectoryType {
} }
fn find_attr<'a>( fn find_attr<'a>(
section: &SectionBytes<'a>, section: &'a SectionBytes,
name: &str, name: &str,
) -> Result<Option<&'a str>, std::str::Utf8Error> { ) -> Result<Option<&'a str>, std::str::Utf8Error> {
section section
@ -357,7 +284,7 @@ fn find_attr<'a>(
} }
fn find_attr_req<'a>( fn find_attr_req<'a>(
section: &SectionBytes<'a>, section: &'a SectionBytes,
name: &'static str, name: &'static str,
) -> Result<&'a str, ThemeParseError> { ) -> Result<&'a str, ThemeParseError> {
find_attr(section, name)?.ok_or(MissingRequiredAttribute(name)) find_attr(section, name)?.ok_or(MissingRequiredAttribute(name))