use std::{
fs,
path::{Path, PathBuf},
str::FromStr,
};
use crate::{generate_builder_method, LibSDBootConfError};
#[derive(Debug, PartialEq)]
pub enum Token {
Title(String),
Version(String),
MachineID(String),
Efi(PathBuf),
Options(String),
Linux(PathBuf),
Initrd(PathBuf),
}
impl FromStr for Token {
type Err = LibSDBootConfError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, ' ');
let key = parts.next().ok_or(LibSDBootConfError::EntryParseError)?;
let value = parts.next().ok_or(LibSDBootConfError::EntryParseError)?;
Ok(match key {
"title" => Self::Title(value.to_owned()),
"version" => Self::Version(value.to_owned()),
"machine-id" => Self::MachineID(value.to_owned()),
"efi" => Self::Efi(PathBuf::from(value)),
"options" => Self::Options(value.to_owned()),
"linux" => Self::Linux(PathBuf::from(value)),
"initrd" => Self::Initrd(PathBuf::from(value)),
_ => return Err(LibSDBootConfError::InvalidToken(key.to_owned())),
})
}
}
impl ToString for Token {
fn to_string(&self) -> String {
match self {
Self::Title(title) => format!("title {}\n", title),
Self::Version(version) => format!("version {}\n", version),
Self::MachineID(machine_id) => format!("machine-id {}\n", machine_id),
Self::Efi(efi) => format!("efi {}\n", efi.display()),
Self::Options(options) => format!("options {}\n", options),
Self::Linux(linux) => format!("linux {}\n", linux.display()),
Self::Initrd(initrd) => format!("initrd {}\n", initrd.display()),
}
}
}
#[derive(Default, Debug, PartialEq)]
pub struct Entry {
pub id: String,
pub tokens: Vec<Token>,
}
impl FromStr for Entry {
type Err = LibSDBootConfError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut entry = Entry::default();
let lines = s.lines();
for line in lines {
if line.starts_with('#') || line.is_empty() {
continue;
}
entry.tokens.push(line.parse()?);
}
Ok(entry)
}
}
impl ToString for Entry {
fn to_string(&self) -> String {
let mut s = String::new();
for token in &self.tokens {
s.push_str(&token.to_string())
}
s
}
}
impl Entry {
pub fn new<S, T>(id: S, tokens: T) -> Entry
where
S: Into<String>,
T: IntoIterator<Item = Token>,
{
Entry {
id: id.into(),
tokens: tokens.into_iter().collect(),
}
}
pub fn load<P: AsRef<Path>>(path: P) -> Result<Entry, LibSDBootConfError> {
let id = path
.as_ref()
.file_name()
.ok_or_else(|| LibSDBootConfError::InvalidEntryFilename(path.as_ref().to_owned()))?
.to_str()
.ok_or_else(|| LibSDBootConfError::InvalidEntryFilename(path.as_ref().to_owned()))?
.strip_suffix(".conf")
.ok_or_else(|| LibSDBootConfError::InvalidEntryFilename(path.as_ref().to_owned()))?;
let mut entry = Entry::from_str(&fs::read_to_string(path.as_ref())?)?;
entry.id = id.to_owned();
Ok(entry)
}
pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<(), LibSDBootConfError> {
fs::write(path, self.to_string())?;
Ok(())
}
}
#[derive(Default, Debug)]
pub struct EntryBuilder {
inner: Entry,
}
impl EntryBuilder {
pub fn new<S: Into<String>>(id: S) -> Self {
Self {
inner: Entry::new(id, Vec::new()),
}
}
generate_builder_method!(
token Token::Title INNER(inner) title(S: String)
);
generate_builder_method!(
token Token::Version INNER(inner) version(S: String)
);
generate_builder_method!(
token Token::MachineID INNER(inner) machine_id(S: String)
);
generate_builder_method!(
token Token::Efi INNER(inner) efi(P: PathBuf)
);
generate_builder_method!(
token Token::Options INNER(inner) options(S: String)
);
generate_builder_method!(
token Token::Linux INNER(inner) linux(P: PathBuf)
);
generate_builder_method!(
token Token::Initrd INNER(inner) initrd(P: PathBuf)
);
pub fn build(self) -> Entry {
self.inner
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder() {
let entry = EntryBuilder::new("5.12.0-aosc-main")
.title("5.12.0-aosc-main")
.build();
println!("{:?}", &entry);
}
}