111 lines
3.0 KiB
Rust
111 lines
3.0 KiB
Rust
|
/// 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>;
|
||
|
}
|