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 104 105 106 107 108 109 110 111
/// Generic `Iterator` over implementor's of
/// [`Entry`](trait.Entry.html)'s.
///
/// # Examples
///
/// #### Iterate over /etc/passwd printing usernames
///
/// ```
/// use std::path::Path;
/// use pgs_files::passwd::PasswdEntry;
/// use pgs_files::Entries;
///
/// for entry in Entries::<PasswdEntry>::new(&Path::new("/etc/passwd")) {
/// println!("{}", entry.name);
/// }
/// ```
use std::{
io::{BufRead,BufReader,Write},
fs::{OpenOptions,File},
path::Path,
marker::PhantomData,
num::ParseIntError,
};
pub struct Entries<T> {
path: String,
cursor: BufReader<File>,
marker: PhantomData<T>,
}
impl<T> Entries<T> {
pub fn new(file_path: &str) -> Entries<T> {
let file = Path::new(&file_path);
if ! file.exists() {
File::create(file).unwrap_or_else(|e|{
eprintln!("Error file: {}",e);
std::process::exit(2)
});
}
let reader = BufReader::new(File::open(file).ok().unwrap());
Entries {
path: file_path.to_owned(),
cursor: reader,
marker: PhantomData,
}
}
pub fn append(&self, entry: String) -> anyhow::Result<()> {
let file = Path::new(&self.path);
if ! file.exists() {
std::fs::write(file,format!("{}\n",entry).as_bytes())?;
} else {
let target_file = OpenOptions::new()
.write(true)
.append(true) // This is needed to append to file
.open(&file);
if let Ok(mut file) = target_file {
file.write_all( format!("{}\n",entry).as_bytes())?;
}
}
Ok(())
}
pub fn write(&self, entries: &Vec<String>) -> anyhow::Result<()> {
let file = Path::new(&self.path);
std::fs::write(file,format!("{}\n",entries.clone().join("\n")).as_bytes())?;
// let target_file = OpenOptions::new()
// .write(true)
// .open(&file);
// if let Ok(mut file) = target_file {
// file.write_all(entries.clone().join("\n").as_bytes())?;
// }
Ok(())
}
}
impl<T: Entry> Iterator for Entries<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
let mut line = String::new();
loop {
// We might need to make multiple loops to drain off
// comment lines. Start with an empty string per loop.
line.clear();
match self.cursor.read_line(&mut line){
Ok(0) => return None,
Ok(_) => (),
_ => return None,
}
if line.starts_with("#") {
continue;
}
match T::from_line(&line) {
Ok(entry) => return Some(entry),
// Parse Error. Just ignore this entry.
_ => (),
}
}
}
}
/// A Trait to represent an entry of data from an
/// /etc/{`passwd`,`group`,`shadow`} file.
pub trait Entry: Sized {
fn from_line(line: &str) -> anyhow::Result<Self, ParseIntError>;
}