afetch 0.0.1

AFetch is a simple fetch program written in Rust.
Documentation
use afetch::system::infos::Infos;
use afetch::translations::list::{language_code_list, language_list};
use afetch::utils;
use afetch::utils::convert_to_readable_unity;
use colored::Colorize;
use image::GenericImageView;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::process::exit;
use sysinfo::{Cpu, CpuExt, DiskExt, NetworkExt, SystemExt};
use viuer::Config as ViuerConfig;
use whoami::{hostname, username};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct LogoConfig {
    status: String,
    char_type: String,
    picture_path: String,
    color: Vec<u8>,
    color_header: Vec<u8>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Config {
    language: String,
    logo: LogoConfig,
    disabled_entries: Vec<String>,
}

fn main() {
    let afetch_config_path: std::path::PathBuf = dirs::config_dir().unwrap_or_else(|| {
        println!("Your infos.sysinfo_obj is not supported, please open an issue at: https://github.com/Asthowen/AFetch/ so I can add support for your infos.sysinfo_obj.");
        exit(9);
    }).join("afetch").join("config.yaml");
    std::fs::create_dir_all(afetch_config_path.parent().unwrap()).unwrap();

    let yaml_to_parse: String = if afetch_config_path.exists() {
        std::fs::read_to_string(afetch_config_path).unwrap()
    } else {
        let to_write: String = "language: auto # en / fr / auto \nlogo:\n  status: enable # disable / enable\n  char_type: braille # braille / picture\n  picture_path: none # `the file path: eg: ~/pictures/some.png` / none\n  color:\n    - 255\n    - 255\n    - 255\n  color_header:\n    - 133\n    - 218\n    - 249\ndisabled_entries:\n  - ip".to_owned();
        std::fs::write(afetch_config_path, to_write.clone()).unwrap();
        to_write
    };
    let yaml: Config = serde_yaml::from_str(&yaml_to_parse).unwrap_or_else(|e| {
        println!("Your configuration is malformed ({})", e);
        exit(9);
    });

    let language: HashMap<&'static str, &'static str> = if yaml.language == "auto" {
        let locale_value_base: String = sys_locale::get_locale()
            .unwrap_or_else(|| String::from("en-US"))
            .replace('_', "-");
        let locale_value_split: Vec<&str> = locale_value_base.split('-').collect::<Vec<&str>>();
        let locale_value: String = if locale_value_split.is_empty() {
            locale_value_base
        } else {
            locale_value_split[0].to_owned()
        };
        if language_code_list().contains(&locale_value.as_str()) {
            language_list()[locale_value.as_str()].clone()
        } else {
            language_list()["en"].clone()
        }
    } else if language_code_list().contains(&yaml.language.as_str()) {
        language_list()[yaml.language.as_str()].clone()
    } else {
        language_list()["en"].clone()
    };

    let infos: Infos = Infos::init();
    let logo_type: i8 = if yaml.logo.status == "enable" {
        if yaml.logo.char_type == "braille" {
            0
        } else {
            1
        }
    } else {
        2
    };
    let logo: Option<Vec<&str>> = if logo_type == 0 {
        Option::from(infos.get_os_logo().lines().collect::<Vec<&str>>())
    } else {
        None
    };

    let (username, host) = (username(), hostname());
    let mut infos_to_print: Vec<String> = Vec::new();
    let mut output: String = "".to_owned();
    infos_to_print.push(format!("{}@{}", username, host).cyan().bold().to_string());
    infos_to_print.push(format!(
        "\x1b[0m{}",
        "─".repeat(username.len() + host.len() + 1)
    ));

    if !yaml.disabled_entries.contains(&"os".to_owned()) {
        let system_name: String = infos.sysinfo_obj.name().unwrap_or_else(|| "".to_owned());
        if !system_name.is_empty() {
            if system_name.to_lowercase().contains("windows") {
                infos_to_print.push(
                    format!(
                        "{}{} {}",
                        language["label-os"].bold().truecolor(
                            yaml.logo.color_header[0],
                            yaml.logo.color_header[1],
                            yaml.logo.color_header[2]
                        ),
                        system_name,
                        infos
                            .sysinfo_obj
                            .os_version()
                            .unwrap()
                            .split(' ')
                            .collect::<Vec<&str>>()[0]
                    )
                    .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
                    .to_string(),
                );
            } else {
                infos_to_print.push(
                    format!(
                        "{}{}",
                        language["label-os"].bold().truecolor(
                            yaml.logo.color_header[0],
                            yaml.logo.color_header[1],
                            yaml.logo.color_header[2]
                        ),
                        system_name
                    )
                    .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
                    .to_string(),
                );
            }
        }
    }
    if !yaml.disabled_entries.contains(&"host".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-host"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            infos
                .get_host()
                .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
        ));
    }
    if !yaml.disabled_entries.contains(&"kernel".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-kernel"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            infos
                .sysinfo_obj
                .kernel_version()
                .unwrap_or_else(|| "".to_owned())
                .replace('\n', "")
                .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
        ));
    }
    if !yaml.disabled_entries.contains(&"uptime".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-uptime"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            utils::format_time(infos.sysinfo_obj.uptime()).truecolor(
                yaml.logo.color[0],
                yaml.logo.color[1],
                yaml.logo.color[2]
            )
        ));
    }
    if !yaml.disabled_entries.contains(&"packages".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-packages"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2],
            ),
            infos.get_packages_number().truecolor(
                yaml.logo.color[0],
                yaml.logo.color[1],
                yaml.logo.color[2]
            )
        ));
    }
    if !yaml.disabled_entries.contains(&"resolution".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-resolution"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2],
            ),
            infos.get_screens_resolution().truecolor(
                yaml.logo.color[0],
                yaml.logo.color[1],
                yaml.logo.color[2]
            )
        ));
    }
    if !yaml.disabled_entries.contains(&"desktop".to_owned()) {
        let de_infos: (String, String) = infos.get_de();
        infos_to_print.push(format!(
            "{}{}",
            language["label-desktop"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2],
            ),
            format!(
                "{} {}",
                de_infos.0,
                if !yaml
                    .disabled_entries
                    .contains(&"desktop-version".to_owned())
                {
                    de_infos.1
                } else {
                    "".to_owned()
                }
            )
            .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
        ));
    }
    if !yaml.disabled_entries.contains(&"shell".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-shell"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            infos
                .get_shell()
                .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
        ));
    }
    if !yaml.disabled_entries.contains(&"terminal".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-terminal"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            infos.get_terminal().truecolor(
                yaml.logo.color[0],
                yaml.logo.color[1],
                yaml.logo.color[2]
            )
        ));
    }
    if !yaml.disabled_entries.contains(&"memory".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-memory"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            format!(
                "{}/{}",
                convert_to_readable_unity(infos.sysinfo_obj.used_memory() as f64),
                convert_to_readable_unity(infos.sysinfo_obj.total_memory() as f64)
            )
            .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
        ));
    }
    if !yaml.disabled_entries.contains(&"cpu".to_owned()) {
        let cpu_infos: &Cpu = infos.sysinfo_obj.global_cpu_info();
        let cpu_name: String = if !cpu_infos.brand().is_empty() {
            cpu_infos.brand().to_owned()
        } else if !infos.sysinfo_obj.global_cpu_info().vendor_id().is_empty() {
            cpu_infos.vendor_id().to_owned()
        } else {
            "".to_owned()
        };

        if cpu_name.is_empty() {
            infos_to_print.push(format!(
                "{}{:.5}%",
                language["label-cpu"].bold().truecolor(
                    yaml.logo.color_header[0],
                    yaml.logo.color_header[1],
                    yaml.logo.color_header[2]
                ),
                cpu_infos.cpu_usage().to_string().truecolor(
                    yaml.logo.color[0],
                    yaml.logo.color[1],
                    yaml.logo.color[2]
                )
            ));
        } else {
            infos_to_print.push(format!(
                "{}{}",
                language["label-cpu"].bold().truecolor(
                    yaml.logo.color_header[0],
                    yaml.logo.color_header[1],
                    yaml.logo.color_header[2]
                ),
                format!("{} - {:.5}%", cpu_name, cpu_infos.cpu_usage()).truecolor(
                    yaml.logo.color[0],
                    yaml.logo.color[1],
                    yaml.logo.color[2]
                )
            ));
        }
    }
    if !yaml.disabled_entries.contains(&"network".to_owned()) {
        let (mut network_sent, mut network_recv) = (0, 0);
        for (_, data) in infos.sysinfo_obj.networks() {
            network_sent += data.transmitted();
            network_recv += data.received();
        }
        infos_to_print.push(format!(
            "{}{}",
            language["label-network"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            format!(
                "download: {}/s - upload: {}/s",
                convert_to_readable_unity(network_sent as f64),
                convert_to_readable_unity(network_recv as f64)
            )
            .truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2])
        ));
    }
    let print_disk: bool = !yaml.disabled_entries.contains(&"disk".to_owned());
    let print_disks: bool = !yaml.disabled_entries.contains(&"disks".to_owned());

    if print_disks || print_disk {
        let (mut total_disk_used, mut total_disk_total) = (0, 0);
        for disk in infos.sysinfo_obj.disks() {
            let disk_mount_point: String = disk.mount_point().to_str().unwrap().to_owned();
            if !disk_mount_point.contains("/docker") && !disk_mount_point.contains("/boot") {
                total_disk_used += disk.total_space() - disk.available_space();
                total_disk_total += disk.total_space();
                if print_disk {
                    infos_to_print.push(format!(
                        "{}{}{}",
                        language["label-disk"].bold().truecolor(
                            yaml.logo.color_header[0],
                            yaml.logo.color_header[1],
                            yaml.logo.color_header[2]
                        ),
                        format!("({})", disk.mount_point().to_str().unwrap_or(""),).truecolor(
                            yaml.logo.color_header[0],
                            yaml.logo.color_header[1],
                            yaml.logo.color_header[2]
                        ),
                        format!(
                            "{}{}/{}",
                            language["label-disk-1"],
                            convert_to_readable_unity(
                                (disk.total_space() - disk.available_space()) as f64
                            ),
                            convert_to_readable_unity(disk.total_space() as f64)
                        )
                        .truecolor(
                            yaml.logo.color[0],
                            yaml.logo.color[1],
                            yaml.logo.color[2]
                        )
                    ));
                }
            }
        }
        if print_disks {
            infos_to_print.push(format!(
                "{}{}{}{}",
                language["label-disks"].bold().truecolor(
                    yaml.logo.color_header[0],
                    yaml.logo.color_header[1],
                    yaml.logo.color_header[2]
                ),
                convert_to_readable_unity(total_disk_used as f64).truecolor(
                    yaml.logo.color[0],
                    yaml.logo.color[1],
                    yaml.logo.color[2]
                ),
                "/".truecolor(yaml.logo.color[0], yaml.logo.color[1], yaml.logo.color[2]),
                convert_to_readable_unity(total_disk_total as f64).truecolor(
                    yaml.logo.color[0],
                    yaml.logo.color[1],
                    yaml.logo.color[2]
                )
            ));
        }
    }
    if !yaml.disabled_entries.contains(&"publicip".to_owned()) {
        infos_to_print.push(format!(
            "{}{}",
            language["label-public-ip"].bold().truecolor(
                yaml.logo.color_header[0],
                yaml.logo.color_header[1],
                yaml.logo.color_header[2]
            ),
            infos.get_public_ip().truecolor(
                yaml.logo.color[0],
                yaml.logo.color[1],
                yaml.logo.color[2]
            )
        ));
    }

    if let Some(logo) = logo {
        let mut last_index = 0;

        for (i, info) in infos_to_print.into_iter().enumerate() {
            if logo.len() > i {
                output += &format!("{}   {}\n", logo[i], info);
            }
            last_index += 1;
        }
        if last_index < logo.len() {
            for logo_line in &logo[last_index..] {
                output += &format!("{}\n", logo_line);
            }
        }
        println!("{}", output);
    } else if logo_type == 1 {
        println!();
        for info in &infos_to_print {
            output += &format!("{}{}\n", " ".repeat(47), info);
        }
        print!("{}\x1b[{}A", output, infos_to_print.len());

        let image = match image::open(&yaml.logo.picture_path) {
            Ok(image) => image,
            Err(e) => {
                println!("An error occurred while loading the image: {}", e);
                exit(9);
            }
        };
        let dimensions: (u32, u32) = image.dimensions();
        let width_ratio: f64 = dimensions.0 as f64 / 44.0;
        let height_ratio: f64 = dimensions.1 as f64 / 44.0;
        let ratio: f64 = width_ratio.max(height_ratio);
        let new_width: u32 = (dimensions.0 as f64 / ratio) as u32;

        let viuer_config: ViuerConfig = ViuerConfig {
            x: 0,
            width: Some(new_width),
            absolute_offset: false,
            ..Default::default()
        };
        viuer::print_from_file(yaml.logo.picture_path, &viuer_config).ok();

        println!();
    } else {
        println!();
        for info in &infos_to_print {
            output += &format!(" {}\n", info);
        }
        println!("{}", output);
    }
}