rsftch 0.6.4

Aesthetically pleasing and lightning fast hardware fetch tool written in rust with configuration.
use colored::Color;
use colored::Colorize;
use std::env;
use std::mem;

mod ascii;
mod color_config;
mod fns;
mod info_config;

use crate::ascii::*;
use crate::color_config::*;
use crate::fns::*;
use crate::info_config::*;

const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");

fn main() {
    let mut args: Vec<String> = env::args().collect();
    let mut overriden_ascii: Option<String> = None;
    let mut info_custom_config: Option<String> = None;
    let mut color_custom_config: Option<String> = None;
    let mut get_only_info: Option<String> = None;
    let mut use_info_custom_config = true;
    let mut use_color_custom_config = true;
    let mut margin: i8 = 1;

    for count in 0..args.len() {
        match args[count].to_lowercase().as_str() {
            "-h" | "--help" | "--usage" => return help(),
            "-v" | "--version" => {
                return println!("Rsftch {}\nMade by charklie", VERSION.unwrap_or_default())
            }
            "--ignore-color-config" => use_color_custom_config = false,
            "--ignore-info-config" => use_info_custom_config = false,
            "--ignore-config" => {
                use_color_custom_config = false;
                use_info_custom_config = false;
            }
            "-m" | "--margin" => {
                if count + 1 < args.len() {
                    margin = args[count + 1].parse().unwrap();
                } else {
                    println!("[{}] Missing argument for margin.\n", "ERROR".red());
                    return help();
                }
            }
            "--info" => {
                if count + 1 < args.len() && !args[count + 1].starts_with("-") {
                    get_only_info = Some(mem::take(&mut args[count + 1]));
                } else {
                    println!("[{}] Missing argument for info.\n", "ERROR".red());
                    return help();
                }
            }
            "-o" | "--override" => {
                if count + 1 < args.len() && !args[count + 1].starts_with("-") {
                    overriden_ascii = Some(mem::take(&mut args[count + 1]));
                } else {
                    println!(
                        "[{}] Missing argument for override, showing all possible.",
                        "WARNING".yellow()
                    );
                    return ascii_test();
                }
            }
            "-i" | "--info-config" => {
                if count + 1 < args.len() && !args[count + 1].starts_with("-") {
                    info_custom_config = Some(mem::take(&mut args[count + 1]));
                } else {
                    println!(
                        "[{}] Missing argument for custom info config file.\n",
                        "ERROR".red()
                    );
                    return help();
                }
            }
            "-c" | "--color-config" => {
                if count + 1 < args.len() && !args[count + 1].starts_with("-") {
                    color_custom_config = Some(mem::take(&mut args[count + 1]));
                } else {
                    println!(
                        "[{}] Missing argument for custom color config file.\n",
                        "ERROR".red()
                    );
                    return help();
                }
            }
            _ => {}
        };
    }

    println!(
        "{}",
        info(
            overriden_ascii,
            margin,
            use_info_custom_config,
            !use_color_custom_config,
            info_custom_config,
            color_custom_config,
            get_only_info,
        )
    );
}

#[derive(Clone, Debug)]
struct InfoItem {
    title: &'static str,
    alignment_space: i8,
    icon: &'static str,
    value: String,
}

fn print_ascii(
    ascii_art: String,
    color: Color,
    overriden_colors: bool,
    custom_color_config_file: Option<String>,
) -> String {
    if !overriden_colors {
        ascii_art.color(color).bold().to_string()
    } else {
        print_ascii(
            ascii_art,
            get_color_config(
                "color0".to_string(),
                !overriden_colors,
                custom_color_config_file.clone(),
            ),
            false,
            custom_color_config_file,
        )
    }
}

fn print_data(infos: &InfoItem, color: Color, connector: &'static str) -> String {
    let arrow = "~>";
    let coloreds = (
        connector.color(color).to_string(),
        infos.icon.color(color).to_string(),
        arrow.color(color).to_string(),
    );
    let alignment_space = " ".repeat(infos.alignment_space as usize);

    format!(
        "{}{}  {}{} {}  {}",
        coloreds.0, coloreds.1, infos.title, alignment_space, coloreds.2, infos.value
    )
    .to_string()
}

fn info(
    overriden_ascii: Option<String>,
    margin: i8,
    use_custom_info_config: bool,
    use_custom_color_config: bool,
    custom_info_config_file: Option<String>,
    custom_color_config_file: Option<String>,
    get_only_info: Option<String>,
) -> String {
    let distro = InfoItem {
        title: "distro",
        alignment_space: 2,
        icon: "",
        value: get_os_release_pretty_name(overriden_ascii.clone(), "NAME")
            .unwrap_or(uname_s(overriden_ascii.clone())),
    };

    let hostname = InfoItem {
        title: "host",
        alignment_space: 4,
        icon: "󱩛",
        value: uname_n(),
    };

    let shell = InfoItem {
        title: "shell",
        alignment_space: 3,
        icon: "",
        value: shell_name(),
    };

    let kernel = InfoItem {
        title: "kernel",
        alignment_space: 2,
        icon: "",
        value: uname_r(),
    };

    let packs = InfoItem {
        title: "packs",
        alignment_space: 3,
        icon: "󰿺",
        value: get_packages(),
    };

    let user = InfoItem {
        title: "user",
        alignment_space: 4,
        icon: "",
        value: whoami(),
    };

    let term = InfoItem {
        title: "term",
        alignment_space: 4,
        icon: "",
        value: get_terminal(),
    };

    let de = InfoItem {
        title: "de/wm",
        alignment_space: 3,
        icon: "",
        value: get_wm(),
    };

    let cpu = InfoItem {
        title: "cpu",
        alignment_space: 5,
        icon: "󰍛",
        value: get_cpu_info(),
    };

    let mem = InfoItem {
        title: "mem",
        alignment_space: 5,
        icon: "",
        value: get_mem(),
    };

    let res = InfoItem {
        title: "res",
        alignment_space: 5,
        icon: "",
        value: get_res(),
    };

    let uptime = InfoItem {
        title: "uptime",
        alignment_space: 2,
        icon: "󰄉",
        value: match get_uptime() {
            Err(_err) => "".to_string(),
            Ok(time) => time,
        },
    };

    let gpu = InfoItem {
        title: "gpu",
        alignment_space: 5,
        icon: "󰍹",
        value: match get_gpu_info() {
            Err(_err) => "".to_string(),
            Ok(gpu_info) => gpu_info,
        },
    };

    let disk = InfoItem {
        title: "disk",
        alignment_space: 4,
        icon: "",
        value: get_disk_usage(),
    };

    let timezone = InfoItem {
        title: "timezone",
        alignment_space: 0,
        icon: "󰥔",
        value: get_timezone(),
    };

    let empty = InfoItem {
        title: "empty",
        alignment_space: 0,
        icon: "",
        value: String::new(),
    };

    let parse_info = |name: String| {
        return match name.to_lowercase().as_str() {
            "os" | "distro" => &distro,
            "host" | "hostname" => &hostname,
            "shell" => &shell,
            "kernel" => &kernel,
            "packs" | "packages" => &packs,
            "user" | "username" => &user,
            "term" | "terminal" => &term,
            "de" | "dewm" | "wm" => &de,
            "cpu" | "processor" => &cpu,
            "gpu" | "graphics" => &gpu,
            "mem" | "memory" => &mem,
            "uptime" => &uptime,
            "res" | "display" | "resolution" => &res,
            "time" | "timezone" => &timezone,
            "disk" | "diskusage" => &disk,
            _ => &empty,
        };
    };

    let parse_json_lists = |set| {
        let mut info_set: Vec<InfoItem> = vec![];
        for i in get_info(set, use_custom_info_config, custom_info_config_file.clone()) {
            info_set.push(parse_info(i).clone());
        }
        info_set
    };

    if get_only_info.is_some() {
        return format!("{}", parse_info(get_only_info.unwrap()).value.trim_matches('"'));
    }

    let info_set1 = parse_json_lists("info1");
    let info_set2 = parse_json_lists("info2");
    let info_set3 = parse_json_lists("info3");

    let margin_spaces = " ".repeat(margin as usize);
    let distroascii = print_ascii(
        get_distro_ascii(overriden_ascii),
        get_color_config(
            "color0".to_string(),
            use_custom_color_config,
            custom_color_config_file.clone(),
        ),
        !use_custom_color_config,
        custom_color_config_file.clone(),
    );
    let infos1 = (1, info_set1);
    let infos2 = (2, info_set2);
    let infos3 = (3, info_set3);
    let mut info_sets = vec![infos1, infos2, infos3];

    println!("{}\n", distroascii);

    for (idx, infos) in info_sets.iter_mut().enumerate() {
        if idx > 0 {
            println!();
        }

        loop_over_data(
            &mut infos.1,
            margin_spaces.clone(),
            infos.0,
            use_custom_color_config,
            custom_color_config_file.clone(),
        );
    }

    String::new()
}

fn loop_over_data(
    list: &mut Vec<InfoItem>,
    margin: String,
    section: i8,
    use_custom_config: bool,
    custom_color_config_file: Option<String>,
) {
    list.retain(|s| !s.value.is_empty());
    let len = list.len();

    for (idx, item) in list.clone().iter().enumerate() {
        let color = get_color_config(
            format!("color{}", section),
            use_custom_config,
            custom_color_config_file.clone(),
        );

        let connector = if idx == 0 {
            "╭─"
        } else if idx == len - 1 {
            "╰─"
        } else {
            "├─"
        };

        println!("{}{}", margin, print_data(item, color, connector));
    }
}