1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use itertools::Itertools;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::path::Path;
#[derive(Debug, Error)]
pub enum EntryError {
#[error("error reading line in entry file")]
Line(#[source] io::Error),
#[error("title field is missing")]
MisisngTitle,
#[error("entry is not a file")]
NotAFile,
#[error("entry does not have a file name")]
NoFilename,
#[error("initrd was defined without a value")]
NoValueForInitrd,
#[error("linux was defined without a value")]
NoValueForLinux,
#[error("error opening entry file")]
Open(#[source] io::Error),
#[error("entry has a file name that is not UTF-8")]
Utf8Filename,
}
#[derive(Debug, Default, Clone)]
pub struct Entry {
pub id: Box<str>,
pub initrd: Option<Box<str>>,
pub linux: Box<str>,
pub options: Vec<Box<str>>,
pub title: Box<str>,
}
impl Entry {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, EntryError> {
let path = path.as_ref();
if !path.is_file() {
return Err(EntryError::NotAFile);
}
let file_name = match path.file_stem() {
Some(file_name) => match file_name.to_str() {
Some(file_name) => file_name.to_owned(),
None => return Err(EntryError::Utf8Filename),
},
None => return Err(EntryError::NoFilename),
};
let file = File::open(path).map_err(EntryError::Open)?;
let mut entry = Entry::default();
entry.id = file_name.into();
for line in BufReader::new(file).lines() {
let line = line.map_err(EntryError::Line)?;
let mut fields = line.split_whitespace();
match fields.next() {
Some("title") => entry.title = fields.join(" ").into(),
Some("linux") => match fields.next() {
Some(value) => entry.linux = value.into(),
None => return Err(EntryError::NoValueForLinux),
},
Some("initrd") => match fields.next() {
Some(value) => entry.initrd = Some(value.into()),
None => return Err(EntryError::NoValueForInitrd),
},
Some("options") => entry.options = fields.map(Box::from).collect(),
_ => (),
}
}
if entry.title.is_empty() {
return Err(EntryError::MisisngTitle);
}
Ok(entry)
}
pub fn is_current(&self) -> bool {
let initrd = self
.initrd
.as_ref()
.map(|x| ["initrd=", &x.replace('/', "\\")].concat());
let initrd = initrd.as_ref().map(String::as_str);
let options = self.options.iter().map(Box::as_ref);
let expected_cmdline = initrd.iter().cloned().chain(options);
crate::kernel_cmdline()
.iter()
.cloned()
.zip(expected_cmdline)
.all(|(a, b)| a == b)
}
}