file-expert 1.1.0

Expert system for recognizing source code files, similar to GitHub/lingust.
Documentation
//
// $COPYRIGHT$: 794d8002d1b6d954e2302879cb69c215d893c60c

// TODO Add handling for multiline `/usr/bin/env\nexec ruby` shebangs
pub fn interpreter(first_line: &str) -> Option<String> {
    if !first_line.starts_with("#!") {
        return None;
    }
    let line = (&first_line[2..]).trim();
    let valid_paths = vec![
        "/bin/",
        "/opt/bin/",
        "/sbin/",
        "/usr/bin/env ",
        "/usr/bin/",
        "/usr/local/bin/",
        "/usr/sbin/",
    ];

    let mut interpreter_starts = None;
    for p in valid_paths {
        if line.starts_with(p) {
            interpreter_starts = Some(p.len());
            break;
        }
    }
    interpreter_starts?;
    let start = interpreter_starts.unwrap();
    if line.len() == start {
        return None;
    }
    return line[start..]
        .split(' ')
        .map(std::string::ToString::to_string)
        .find(|s| !s.starts_with('-') && !s.is_empty() && !s.contains('='));
}

#[cfg(test)]
#[cfg(not(tarpaulin_include))]
mod test {
    use crate::shebang::interpreter;

    #[test]
    fn simple() {
        assert_eq!(interpreter("Lore Ipsum Dolores"), None);
        assert_eq!(interpreter("#!/usr/bin/perl"), Some("perl".to_string()));
        assert_eq!(interpreter("#! /usr/bin/perl"), Some("perl".to_string()));
        assert_eq!(interpreter("#!/usr/bin/jq -fr"), Some("jq".to_string()));
        assert_eq!(interpreter("#!/var/foo/bin/python"), None);
        assert_eq!(interpreter("#! /sbin/"), None);
        assert_eq!(interpreter("#! /sbin/ -fr"), None);
        assert_eq!(interpreter("#!/usr/bin/env perl"), Some("perl".to_string()));
        assert_eq!(
            interpreter("#!/usr/bin/env  perl"),
            Some("perl".to_string())
        );
        assert_eq!(
            interpreter("#!/usr/bin/env  perl -n"),
            Some("perl".to_string())
        );
        assert_eq!(
            interpreter("#!/usr/bin/env -vS ruby -w -Ilib:test"),
            Some("ruby".to_string())
        );
        assert_eq!(
            interpreter("#!/usr/bin/env -vS ruby -wKU"),
            Some("ruby".to_string())
        );
        assert_eq!(
            interpreter("#!/usr/bin/env --split-string sed -f"),
            Some("sed".to_string())
        );
        assert_eq!(
            interpreter("#!/usr/bin/env -S GH_TOKEN=ghp_*** deno run --allow-net"),
            Some("deno".to_string())
        );
        assert_eq!(
            interpreter(
                "#! /usr/bin/env A=003 B=149 C=150 D=xzd E=base64 F=tar G=gz H=head I=tail sh"
            ),
            Some("sh".to_string())
        );
    }
}