fileinfo 0.1.2

parse file info from ls/ftp command
Documentation
use crate::FileInfo;

fn take_n(s: &str, n: usize) -> (&str, &str) {
    (&s[n..], &s[..n])
}

fn take_space(s: &str) -> (&str, &str) {
    take_till(s, |c: char| !c.is_whitespace())
}

fn take_digit(s: &str) -> (&str, &str) {
    take_till(s, |c: char| !c.is_ascii_digit())
}

fn take_till<F: Fn(char) -> bool>(s: &str, check: F) -> (&str, &str) {
    let mut end = 0;
    for (pos, c) in s.char_indices() {
        if check(c) {
            end = pos;
            break;
        }
    }
    (&s[end..], &s[..end])
}

fn take_till_space(s: &str) -> (&str, &str) {
    take_till(s, |c| c.is_whitespace())
}

pub fn parse(input: &str) -> Option<FileInfo> {
    let input = input.trim();

    let (input, permissions) = take_n(input, 10);
    let (input, _) = take_space(input);

    let (input, hard_link_count) = take_digit(input);
    let hard_link_count = hard_link_count.parse::<usize>().ok()?;

    let (input, _) = take_space(input);

    let (input, owner) = take_till_space(input);
    let (input, _) = take_space(input);

    let (input, group) = take_till_space(input);
    let (input, _) = take_space(input);

    let (input, size) = take_digit(input);
    let size = size.parse::<usize>().ok()?;
    let (input, _) = take_space(input);

    let (input, month) = take_till_space(input);
    let (input, _) = take_space(input);

    let (input, day) = take_till_space(input);
    let (input, _) = take_space(input);

    let (input, time) = take_till_space(input);
    let (input, _) = take_space(input);
    let last_modified = format!("{month} {day} {time}");

    let (name, link) = if let Some(i) = input.find(" -> ") {
        (&input[..i], Some(input[i + 4..].to_string()))
    } else {
        (input, None)
    };

    Some(FileInfo {
        permissions: permissions.to_string(),
        hard_link_count,
        owner: owner.to_string(),
        group: group.to_string(),
        size,
        last_modified,
        name: name.to_string(),
        link,
    })
}