use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
#[derive(Debug, PartialEq, Eq)]
pub enum SourcesEntry {
Deb {
uri: String,
dist: String,
comps: Vec<String>,
},
DebSrc {
uri: String,
dist: String,
comps: Vec<String>,
},
}
pub fn parse_sources_list_entry(line: &str) -> Option<SourcesEntry> {
let parts = line.split_whitespace().collect::<Vec<_>>();
if parts.len() < 3 {
return None;
}
let uri = parts[1];
let dist = parts[2];
let comps = parts[3..].iter().map(|x| x.to_string()).collect::<Vec<_>>();
if parts[0] == "deb" {
return Some(SourcesEntry::Deb {
uri: uri.to_string(),
dist: dist.to_string(),
comps,
});
}
if parts[0] == "deb-src" {
return Some(SourcesEntry::DebSrc {
uri: uri.to_string(),
dist: dist.to_string(),
comps,
});
}
None
}
pub struct SourcesList {
list: Vec<SourcesEntry>,
}
impl SourcesList {
pub fn empty() -> SourcesList {
SourcesList { list: vec![] }
}
pub fn iter(&self) -> std::slice::Iter<SourcesEntry> {
self.list.iter()
}
pub fn load(&mut self, path: &Path) {
let f = File::open(path).unwrap();
for line in BufReader::new(f).lines() {
let line = line.unwrap();
if let Some(entry) = parse_sources_list_entry(&line) {
self.list.push(entry);
}
}
}
pub fn from_apt_dir(apt_dir: &Path) -> SourcesList {
let mut sl = SourcesList::empty();
sl.load(&apt_dir.join("sources.list"));
for entry in apt_dir.read_dir().unwrap() {
let entry = entry.unwrap();
if entry.file_type().unwrap().is_file() {
let path = entry.path();
sl.load(&path);
}
}
sl
}
}
impl Default for SourcesList {
fn default() -> Self {
Self::from_apt_dir(Path::new("/etc/apt"))
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_parse_sources_list_entry() {
use super::parse_sources_list_entry;
use super::SourcesEntry;
assert_eq!(
parse_sources_list_entry(
"deb http://archive.ubuntu.com/ubuntu/ bionic main restricted"
),
Some(SourcesEntry::Deb {
uri: "http://archive.ubuntu.com/ubuntu/".to_string(),
dist: "bionic".to_string(),
comps: vec!["main".to_string(), "restricted".to_string()]
})
);
assert_eq!(
parse_sources_list_entry(
"deb-src http://archive.ubuntu.com/ubuntu/ bionic main restricted"
),
Some(SourcesEntry::DebSrc {
uri: "http://archive.ubuntu.com/ubuntu/".to_string(),
dist: "bionic".to_string(),
comps: vec!["main".to_string(), "restricted".to_string()]
})
);
assert_eq!(parse_sources_list_entry(""), None);
assert_eq!(parse_sources_list_entry("# comment"), None);
assert_eq!(parse_sources_list_entry("deb"), None);
assert_eq!(parse_sources_list_entry("deb http://example.com"), None);
assert_eq!(
parse_sources_list_entry("invalid-type http://example.com bionic main"),
None
);
}
#[test]
fn test_sources_list() {
let td = tempfile::tempdir().unwrap();
let path = td.path().join("sources.list");
std::fs::write(
&path,
"deb http://archive.ubuntu.com/ubuntu/ bionic main restricted\n",
)
.unwrap();
let mut sl = super::SourcesList::empty();
sl.load(&path);
assert_eq!(sl.list.len(), 1);
assert_eq!(
sl.list[0],
super::SourcesEntry::Deb {
uri: "http://archive.ubuntu.com/ubuntu/".to_string(),
dist: "bionic".to_string(),
comps: vec!["main".to_string(), "restricted".to_string()]
}
);
}
}