🏡 index : ~doyle/pisshoff.git

use crate::command::Arg;
use bitflags::bitflags;

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    struct ToPrint: u8 {
        const KERNEL_NAME      = 0b0000_0001;
        const NODE_NAME        = 0b0000_0010;
        const KERNEL_RELEASE   = 0b0000_0100;
        const KERNEL_VERSION   = 0b0000_1000;
        const MACHINE          = 0b0001_0000;
        const PROCESSOR        = 0b0010_0000;
        const PLATFORM         = 0b0100_0000;
        const OPERATING_SYSTEM = 0b1000_0000;
    }
}

const VERSION_STRING: &str = "uname (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by David MacKenzie.
";

pub const HELP_STRING: &str = "Usage: uname [OPTION]...
Print certain system information.  With no OPTION, same as -s.

  -a, --all                print all information, in the following order,
                             except omit -p and -i if unknown:
  -s, --kernel-name        print the kernel name
  -n, --nodename           print the network node hostname
  -r, --kernel-release     print the kernel release
  -v, --kernel-version     print the kernel version
  -m, --machine            print the machine hardware name
  -p, --processor          print the processor type (non-portable)
  -i, --hardware-platform  print the hardware platform (non-portable)
  -o, --operating-system   print the operating system
      --help     display this help and exit
      --version  output version information and exit

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/uname>
or available locally via: info '(coreutils) uname invocation'
";

pub fn execute(params: &[String]) -> String {
    let mut to_print = ToPrint::empty();
    let mut filter_unknown = false;

    for param in super::argparse(params) {
        to_print |= match param {
            Arg::Short('a') | Arg::Long("all") => {
                filter_unknown = true;
                ToPrint::all()
            }
            Arg::Short('s') | Arg::Long("kernel-name") => ToPrint::KERNEL_NAME,
            Arg::Short('n') | Arg::Long("nodename") => ToPrint::NODE_NAME,
            Arg::Short('r') | Arg::Long("kernel-release") => ToPrint::KERNEL_RELEASE,
            Arg::Short('v') | Arg::Long("kernel-version") => ToPrint::KERNEL_VERSION,
            Arg::Short('m') | Arg::Long("machine") => ToPrint::MACHINE,
            Arg::Short('p') | Arg::Long("processor") => ToPrint::PROCESSOR,
            Arg::Short('i') | Arg::Long("hardware-platform") => ToPrint::PLATFORM,
            Arg::Short('o') | Arg::Long("operating-system") => ToPrint::OPERATING_SYSTEM,
            Arg::Long("help") => return HELP_STRING.to_string(),
            Arg::Long("version") => return VERSION_STRING.to_string(),
            Arg::Operand(operand) => {
                return format!(
                    "uname: extra operand '{operand}'\nTry 'uname --help' for more information.\n"
                );
            }
            Arg::Short(s) => {
                return format!(
                    "uname: invalid option -- '{s}'\nTry 'uname --help' for more information.\n"
                );
            }
            Arg::Long(s) => return format!(
                "uname: unrecognized option '--{s}'\nTry 'uname --help' for more information.\n"
            ),
        };
    }

    let mut out = String::with_capacity(105);

    macro_rules! write {
        ($v:expr) => {
            if !out.is_empty() {
                out.push(' ');
            }

            out.push_str($v);
        };
    }

    if to_print.contains(ToPrint::KERNEL_NAME) {
        write!("Linux");
    }

    if to_print.contains(ToPrint::NODE_NAME) {
        write!("cd5079c0d642");
    }

    if to_print.contains(ToPrint::KERNEL_RELEASE) {
        write!("5.15.49");
    }

    if to_print.contains(ToPrint::KERNEL_VERSION) {
        write!("#1 SMP PREEMPT Tue Sep 13 07:51:32 UTC 2022");
    }

    if to_print.contains(ToPrint::MACHINE) {
        write!("x86_64");
    }

    if to_print.contains(ToPrint::PROCESSOR) && !filter_unknown {
        write!("unknown");
    }

    if to_print.contains(ToPrint::PLATFORM) && !filter_unknown {
        write!("unknown");
    }

    if to_print.contains(ToPrint::OPERATING_SYSTEM) {
        write!("GNU/Linux");
    }

    out.push('\n');

    out
}

#[cfg(test)]
mod test {
    use crate::command::uname::execute;
    use test_case::test_case;

    #[test_case("-a"; "all")]
    #[test_case("-snrvmpio"; "all separate")]
    #[test_case("-asnrvmpio"; "all separate with all")]
    #[test_case("-sn"; "subset")]
    #[test_case("-sn --fake"; "unknown long arg param")]
    #[test_case("-sn -z"; "unknown short arg param")]
    #[test_case("-sn oper"; "unknown operand")]
    fn snapshot(input: &str) {
        let input_parsed = shlex::split(input).unwrap();
        let output = execute(&input_parsed);

        insta::assert_display_snapshot!(input, output);
    }
}