mirror of
https://github.com/danbulant/icon
synced 2026-05-24 12:22:13 +00:00
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:
parent
bae6cb822e
commit
b58a34b589
3 changed files with 35 additions and 109 deletions
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
133
src/theme.rs
133
src/theme.rs
|
|
@ -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(§ion, "Size")?.parse()?;
|
let size: u32 = find_attr_req(§ion, "Size")?.parse()?;
|
||||||
let scale: u32 = find_attr(§ion, "Scale")?
|
let scale: u32 = find_attr(§ion, "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))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue