    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>;
    }