use std::fs; use std::io; use std::path::{Path, PathBuf}; use ini::Ini; pub const THEMES_DIRECTORY: &str = "/usr/share/plymouth/themes"; pub const THEME_FILE_EXTENSION: &str = "plymouth"; #[derive(Debug)] pub struct Theme { path: PathBuf, name: String, description: String, module_name: String, } fn find_theme_config_file(path: &Path) -> io::Result { fs::read_dir(&path)? .filter_map(|p| p.ok()) .map(|e| e.path()) .filter(|p| match p.extension() { Some(ext) => ext == THEME_FILE_EXTENSION, None => false, }) .nth(0) .map_or( Err(io::Error::new( io::ErrorKind::NotFound, "No *.plymouth file found in directory", )), |e| Ok(e), ) } impl Theme { pub fn from_ini>(path: P, ini: Ini) -> Self { let theme_section = ini .section(Some("Plymouth Theme")) .expect("could not find [Plymouth Theme] section"); let name = theme_section.get("Name").unwrap_or("").into(); let description = theme_section.get("Description").unwrap_or("").into(); let module_name = theme_section.get("ModuleName").unwrap_or("").into(); Theme { path: PathBuf::from(path.as_ref()), name, description, module_name, } } pub fn from_path>(path: P) -> io::Result { let config_path = find_theme_config_file(path.as_ref())?; let ini = Ini::load_from_file(&config_path).map_err(|e| super::ini_to_io_err(e))?; Ok(Self::from_ini(path, ini)) } pub fn from_name(name: &str) -> io::Result { let path = Path::new(THEMES_DIRECTORY).join(name); Self::from_path(path) } pub fn list() -> io::Result> { fs::read_dir(THEMES_DIRECTORY).and_then(|d| { Ok(d.filter_map(|p| p.ok()) .filter_map(|e| e.file_name().into_string().ok())) }) } } #[cfg(test)] mod tests { use super::*; #[test] fn from_ini() { let mut ini = Ini::new(); ini.with_section(Some("Plymouth Theme")) .set("Name", "foobar") .set("Description", "A foo and a bar.") .set("ModuleName", "script"); let theme = Theme::from_ini("/dev/null", ini); assert!(theme.name == "foobar"); assert!(theme.description == "A foo and a bar."); assert!(theme.module_name == "script"); } #[test] fn from_ini_default_values() { let mut ini = Ini::new(); // rust-ini does not support creating an empty section, so we add // a fake foo=bar entry to create the section ini.with_section(Some("Plymouth Theme")).set("foo", "bar"); let theme = Theme::from_ini("/dev/null", ini); assert!(theme.name == ""); assert!(theme.description == ""); assert!(theme.module_name == ""); } }