Browse Source

Make ini parsing more robust

It now ignores matches from the wrong section, and should be negligibly
slower, while still introducing no dependency.
master
Emmanuel Gil Peyrot 12 months ago
committed by Manos Pitsidianakis
parent
commit
580a7e461f
Signed by: epilys GPG Key ID: 73627C2F690DF710
  1. 102
      src/lib.rs

102
src/lib.rs

@ -55,6 +55,15 @@ mod tests {
println!("{:?}", query_default_app("video/mp4"));
println!("{:?}", query_default_app("application/pdf"));
}
#[test]
fn ini() {
let ini = Ini(String::from("[foo]\n# comment\nbar=baz\n\n[bar]\nbar=foo"));
for (key, value) in ini.iter_section("foo") {
assert_eq!(key, "bar");
assert_eq!(value, "baz");
}
}
}
use std::collections::HashMap;
@ -76,6 +85,51 @@ macro_rules! split_and_chain {
}
}
struct Ini(String);
impl Ini {
fn from_filename(filename: &Path) -> Result<Ini> {
let mut file: File = File::open(filename)?;
let mut contents: Vec<u8> = vec![];
file.read_to_end(&mut contents)?;
let contents_str =
String::from_utf8(contents).map_err(|err| Error::new(ErrorKind::InvalidData, err))?;
Ok(Ini(contents_str))
}
fn iter_section(&self, section: &str) -> impl Iterator<Item = (&str, &str)> {
let section = format!("[{}]", section);
let mut lines = self.0.lines();
// Eat lines until we find the beginning of our section.
loop {
let line = lines.next();
if let Some(line) = line {
if line == section {
break;
}
} else {
break;
}
}
// Then take all foo=bar lines until the next section.
lines
.filter(|line| !line.starts_with('#'))
.take_while(|line| !line.starts_with('['))
.filter_map(|line| {
let split: Vec<_> = line.splitn(2, '=').collect();
if split.len() != 2 {
None
} else {
Some((split[0], split[1]))
}
})
}
}
/// Returns the path of a binary that is the default application of given MIME type `query`
///
/// # Example
@ -196,47 +250,19 @@ fn check_mimeapps_list<T: AsRef<str>>(
xdg_vars: &HashMap<String, String>,
query: T,
) -> Result<Option<PathBuf>> {
let mut file: File = File::open(filename)?;
let mut contents: Vec<u8> = vec![];
file.read_to_end(&mut contents)?;
let contents_str =
str::from_utf8(&contents).map_err(|err| Error::new(ErrorKind::InvalidData, err))?;
let idx = contents_str.find(query.as_ref());
if !contents_str.contains("[Default Applications]") || idx.is_none() {
return Ok(None);
}
let idx = idx.unwrap();
let mut end_idx = contents_str[idx..].len();
for (cidx, c) in (&contents_str[idx..]).chars().enumerate() {
if c == '\n' {
end_idx = cidx + idx;
break;
}
}
let split_idx = if let Some(p) = contents_str[idx..end_idx].find('=') {
p
} else {
/* Invalid data in in `filename`, but we don't want to abort the entire search for this
* so just return None.
*/
return Ok(None);
} + idx
+ 1;
for v in contents_str[split_idx..end_idx].split(';') {
if v.trim().is_empty() {
let ini = Ini::from_filename(filename)?;
for (key, value) in ini.iter_section("Default Applications") {
if key != query.as_ref() {
continue;
}
for v in value.split(';') {
if v.trim().is_empty() {
continue;
}
if let Some(b) = desktop_file_to_binary(v, xdg_vars)? {
return Ok(Some(b));
if let Some(b) = desktop_file_to_binary(v, xdg_vars)? {
return Ok(Some(b));
}
}
}

Loading…
Cancel
Save