groups 0.1.1

Crate for obtaining group information on a system
Documentation
//! This library parses `/etc/group` for information about groups on a system
//!
//! Groups are represented in `/etc/group` in the form of:
//!
//! `group_name:password:GID:user_list`

use std::fs;
use std::io::{BufReader,BufRead};

/// Structure used to wrap parsed information from groups database
#[derive(Debug,PartialEq,Clone)]
pub struct Group {
    /// Group's name
    pub name: String,
    /// Group's GID
    pub gid: u32,
    /// List of users for group
    pub user_list: Option<Vec<String>>,
}


// Parses a line from `/etc/group`
// Ignores the 'password' in `group_name:password:GID:user_list`
fn parse_line(line: String) -> Option<Group> {
    let mut split: Vec<&str> =  line.split(':').collect();
    if split.is_empty() { return None; };
    // Remove 'x' (password)
    split.remove(1);
    let mut users = Vec::new();
    if !split[2].is_empty() {
        let owned_users: Vec<&str> = split[2].split(',').collect();
        for user in owned_users {
            users.push(user.to_string());
        }
    }
    let group = Group {
        name: String::from(split[0]),
        gid: split[1].parse::<u32>().unwrap(),
        user_list:
            if !users.is_empty() { Some(users) }
            else { None }
    };
    return Some(group);
}

// Reads the `/etc/group` file and calls `parse_line` per each line
fn read_group() -> Vec<Group> {
    let mut groups = Vec::new();
    let file = match fs::File::open("/etc/group") {
        Ok(s) => { s },
        Err(e) => { panic!(e.to_string()) }
    };
    let file_buffer = BufReader::new(&file);
    for line in file_buffer.lines() {
        match line {
            Ok(l) => {
                groups.push(parse_line(l).unwrap());
            },
            Err(e) => { panic!(e.to_string()) }
        }

    }
    return groups;
}

/// Gets a group by its GID
///
/// #Example
///
/// ```
/// use groups;
///
/// let group = groups::get_group_by_gid(1).unwrap();
/// assert_eq!(group.gid, 1);
pub fn get_group_by_gid(gid: u32) -> Option<Group> {
    for group in read_group() {
        if group.gid == gid {
            return Some(group);
        }
    }
    return None;
}

/// Gets a group by its name
///
/// #Example
///
/// ```
/// use groups;
///
/// let group = groups::get_group_by_name("bin").unwrap();
/// assert_eq!(group.name, "bin".to_string());
pub fn get_group_by_name(name: &str) -> Option<Group> {
    for group in read_group() {
        if group.name == name {
            return Some(group);
        }
    }
    return None;
}

/// Returns all groups within `/etc/group`
pub fn get_groups() -> Vec<Group> {
    return read_group();
}

/// Searches all groups for any user given or their GID
///
/// #Example
///
/// ```
/// use groups;
///
/// let mut user = String::new();
/// for (key, value) in std::env::vars() {
///     if key == "USER" { user = value }
/// }
/// let list = groups::get_group_list(&user, None);
pub fn get_group_list(user_name: &str, gid: Option<u32>) -> Option<Vec<Group>> {
    let mut list = Vec::new();
    for group in read_group().iter() {
        if gid.is_some() {
            if group.gid == gid.unwrap() && group.name != user_name {
                list.push(group.clone());
            }
        }
        if group.user_list.is_some() {
            let users = group.user_list.clone().unwrap();
            for user in users {
                if user == user_name { list.push(group.clone()) }
            }
        }
    }
    if list.is_empty() { return None; }
    else { return Some(list); }
}

#[test]
fn test_parse_line() {
    let group = parse_line("hell:x:666:trump".to_string()).unwrap();

    assert_eq!(group.name, "hell".to_string());
    assert_eq!(group.gid, 666);
    assert_eq!(group.user_list, Some(vec!["trump".to_string()]));
}

#[test]
fn test_read_group() {
    let groups = read_group();
    for group in groups {
        println!("{:?}", group);
    }
}

#[test]
fn test_get_group_by_name() {
    match get_group_by_name("bin") {
        Some(group) => {
            assert_eq!(group.name, "bin".to_string());
        },
        None => { }
    };
}

#[test]
fn test_get_group_by_gid() {
    match get_group_by_gid(1) {
        Some(group) => {
            assert_eq!(group.gid, 1);
        },
        None => { }
    };
}

#[test]
fn test_get_group_list() {
    let mut user = String::new();
    for (key, value) in std::env::vars() {
        if key == "USER" { user = value }
    }
    match get_group_list(&user, None) {
        Some(list) => {
            for item in list { println!("{:?}", item); }
        },
        None => { }
    };
}