diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Cargo.toml | 10 | ||||
-rw-r--r-- | examples/current-theme.rs | 12 | ||||
-rw-r--r-- | examples/list-themes.rs | 5 | ||||
-rw-r--r-- | src/lib.rs | 14 | ||||
-rw-r--r-- | src/plymouthd.rs | 43 | ||||
-rw-r--r-- | src/theme.rs | 104 |
7 files changed, 190 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b5a3c02 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "plymouth" +version = "0.1.0" +authors = ["Agathe Porte <microjoe@microjoe.org>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rust-ini = "0.15.*"
\ No newline at end of file diff --git a/examples/current-theme.rs b/examples/current-theme.rs new file mode 100644 index 0000000..f52d36d --- /dev/null +++ b/examples/current-theme.rs @@ -0,0 +1,12 @@ +use plymouth::Plymouthd; +use plymouth::Theme; + +fn main() { + let config = Plymouthd::default().expect("could not load default config file"); + println!("current theme is {:?}", config.current_theme()); + + if let Some(name) = config.current_theme() { + let theme = Theme::from_name(name).expect("could not load current theme"); + println!("theme details: {:?}", theme); + } +} diff --git a/examples/list-themes.rs b/examples/list-themes.rs new file mode 100644 index 0000000..d499cb8 --- /dev/null +++ b/examples/list-themes.rs @@ -0,0 +1,5 @@ +use plymouth::Theme; + +fn main() { + Theme::list().unwrap().for_each(|name| println!("{}: {:?}", name, Theme::from_name(&name))); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..54bee5b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,14 @@ +mod plymouthd; +mod theme; + +pub use plymouthd::Plymouthd; +pub use theme::Theme; + +use std::io; + +fn ini_to_io_err(e: ini::ini::Error) -> io::Error { + match e { + ini::ini::Error::Io(x) => x, + ini::ini::Error::Parse(x) => io::Error::new(io::ErrorKind::InvalidData, x), + } +}
\ No newline at end of file diff --git a/src/plymouthd.rs b/src/plymouthd.rs new file mode 100644 index 0000000..9e991e6 --- /dev/null +++ b/src/plymouthd.rs @@ -0,0 +1,43 @@ +use std::path::Path; +use std::io; + +use ini::Ini; + +pub struct Plymouthd { + conf: Ini, +} + +impl Plymouthd { + pub fn from_ini(conf: Ini) -> Self { + Self { conf } + } + + pub fn from_file<P: AsRef<Path>>(filename: P) -> io::Result<Self> { + let conf = Ini::load_from_file(filename).map_err(|e| super::ini_to_io_err(e))?; + Ok(Self { conf }) + } + + pub fn default() -> io::Result<Self> { + Self::from_file("/etc/plymouth/plymouthd.conf") + } + + pub fn current_theme(&self) -> Option<&str> { + self.conf + .section(Some("Daemon")) + .and_then(|s| s.get("Theme")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_theme_from_ini() { + let mut ini = Ini::new(); + ini.with_section(Some("Daemon")).set("Theme", "spinner"); + + let conf = Plymouthd::from_ini(ini); + assert!(conf.current_theme().unwrap() == "spinner"); + } +} diff --git a/src/theme.rs b/src/theme.rs new file mode 100644 index 0000000..2ac93f8 --- /dev/null +++ b/src/theme.rs @@ -0,0 +1,104 @@ +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<PathBuf> { + 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<P: AsRef<Path>>(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<P: AsRef<Path>>(path: P) -> io::Result<Self> { + 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<Self> { + let path = Path::new(THEMES_DIRECTORY).join(name); + Self::from_path(path) + } + + pub fn list() -> io::Result<impl Iterator<Item = String>> { + 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 == ""); + } +} |