diff --git a/src/config/deserialize.rs b/src/config/deserialize.rs index 5ffc80d..d7f4aad 100644 --- a/src/config/deserialize.rs +++ b/src/config/deserialize.rs @@ -120,7 +120,74 @@ impl<'de: 'static> serde::Deserialize<'de> for super::Config { None }; + let entries = config + .info + .map(|info| { + info.into_iter() + .map(|info| match info { + Entry::Info { + kind, + header, + value: format, + separator, + } => { + let header = + header.unwrap_or_else(|| language_func(kind.default_header())); + let format = format.unwrap_or_else(|| kind.default_format()); + + super::Entry::Info { + kind, + fields: kind + .get_fields() + .iter() + .filter(|field| { + let field_str = field.as_str(); + header.contains(field_str) || format.contains(field_str) + }) + .copied() + .collect::>(), + header, + format, + separator: separator.unwrap_or_else(|| language_func("_colon_")), + } + } + Entry::Separator { + separator: content, + sizing: Some(sizing @ SeparatorSizing::Fixed(size)), + } => super::Entry::Separator { + content: content.chars().cycle().take(size).collect(), + sizing, + }, + Entry::Separator { separator, sizing } => super::Entry::Separator { + content: separator, + sizing: sizing.unwrap_or_default(), + }, + Entry::ColorBlocks { + color_block_style, + display, + } => super::Entry::ColorBlocks { + content: match color_block_style { + ColorBlockStyle::Circle => "● ", + ColorBlockStyle::Classic => "███", + ColorBlockStyle::Diamond => "◆ ", + ColorBlockStyle::Triangle => "▲ ", + ColorBlockStyle::Square => "■ ", + ColorBlockStyle::Star => "★ ", + ColorBlockStyle::Custom(content) => content, + }, + display: match display { + Some(ColorBlockDisplay::Normal) => super::ColorBlockDisplay::Normal, + Some(ColorBlockDisplay::Bright) => super::ColorBlockDisplay::Bright, + _ => super::ColorBlockDisplay::Both, + }, + }, + }) + .collect::>() + }) + .unwrap_or_else(|| super::default_entries(config.language)); + Ok(Self { + info: super::group_fields_by_kind(&entries), logo: config.logo, language: config.language.into(), colors: super::ColorOption { @@ -137,75 +204,7 @@ impl<'de: 'static> serde::Deserialize<'de> for super::Config { logo_color, ), }, - entries: config - .info - .map(|info| { - info.into_iter() - .map(|info| match info { - Entry::Info { - kind, - header, - value: format, - separator, - } => { - let header = - header.unwrap_or_else(|| language_func(kind.default_header())); - let format = format.unwrap_or_else(|| kind.default_format()); - super::Entry::Info { - kind, - fields: kind - .get_fields() - .iter() - .filter(|field| { - let field_str = field.as_str(); - header.contains(field_str) || format.contains(field_str) - }) - .copied() - .collect::>(), - header, - format, - separator: separator - .unwrap_or_else(|| language_func("_colon_")), - } - } - Entry::Separator { - separator: content, - sizing: Some(sizing @ SeparatorSizing::Fixed(size)), - } => super::Entry::Separator { - content: content.chars().cycle().take(size).collect(), - sizing, - }, - Entry::Separator { separator, sizing } => super::Entry::Separator { - content: separator, - sizing: sizing.unwrap_or_default(), - }, - Entry::ColorBlocks { - color_block_style, - display, - } => super::Entry::ColorBlocks { - content: match color_block_style { - ColorBlockStyle::Circle => "● ", - ColorBlockStyle::Classic => "███", - ColorBlockStyle::Diamond => "◆ ", - ColorBlockStyle::Triangle => "▲ ", - ColorBlockStyle::Square => "■ ", - ColorBlockStyle::Star => "★ ", - ColorBlockStyle::Custom(content) => content, - }, - display: match display { - Some(ColorBlockDisplay::Normal) => { - super::ColorBlockDisplay::Normal - } - Some(ColorBlockDisplay::Bright) => { - super::ColorBlockDisplay::Bright - } - _ => super::ColorBlockDisplay::Both, - }, - }, - }) - .collect::>() - }) - .unwrap_or_else(|| super::default_entries(config.language)), + entries, parameters: config .parameters .map(|info| super::InfoConfig { diff --git a/src/config/mod.rs b/src/config/mod.rs index 55cb819..4d47b3c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -3,13 +3,14 @@ pub mod deserialize; use crate::{ - error::FetchInfosError, + error::FetchInfoError, system::{InfoField, InfoKind}, translations::get_language, util::colored::ColorWrapper, }; use bitcode::{Decode, Encode}; use serde::Deserialize; +use std::collections::{HashMap, HashSet}; const FALLBACK_COLOR: Option = Some(ColorWrapper::Rgb { r: 255, @@ -19,6 +20,7 @@ const FALLBACK_COLOR: Option = Some(ColorWrapper::Rgb { #[derive(Debug, Decode, Encode)] pub struct Config { + pub info: HashMap>, pub language: &'static str, pub entries: Vec>, pub colors: ColorOption, @@ -180,9 +182,11 @@ impl From for &str { impl Default for Config { fn default() -> Self { + let entries = default_entries(Locale::default()); Self { + info: group_fields_by_kind(&entries), language: Locale::default().into(), - entries: default_entries(Locale::default()), + entries, colors: ColorOption::default(), logo: LogoStyle::default(), parameters: InfoConfig::default(), @@ -194,7 +198,7 @@ pub fn load_config() -> Config { let cache_path = dirs::cache_dir() .map(|p| p.join("afetch.bin")) .ok_or_else(|| { - FetchInfosError::error_exit( + FetchInfoError::error_exit( "An error occurred while retrieving the cache folder, \ please open an issue at: https://github.com/Asthowen/AFetch/issues/new \ so that we can solve your issue.", @@ -211,7 +215,7 @@ pub fn load_config() -> Config { let config_path = dirs::config_dir() .map(|p| p.join("afetch").join("config.json")) .ok_or_else(|| { - FetchInfosError::error_exit( + FetchInfoError::error_exit( "An error occurred while retrieving the config folder, \ please open an issue at: https://github.com/Asthowen/AFetch/issues/new \ so that we can solve your issue.", @@ -265,3 +269,17 @@ fn default_entries(locale: Locale) -> Vec> { }, ] } + +fn group_fields_by_kind(entries: &[Entry]) -> HashMap> { + let mut info = HashMap::new(); + + for entry in entries { + if let Entry::Info { kind, fields, .. } = entry { + info.entry(kind).or_insert_with(HashSet::new).extend(fields); + } + } + + info.into_iter() + .map(|(kind, fields)| (*kind, fields.into_iter().collect())) + .collect() +} diff --git a/src/error.rs b/src/error.rs index 1d79c5a..7b81882 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,9 +10,9 @@ pub enum ErrorType { } #[derive(Debug)] -pub struct FetchInfosError(pub ErrorType); +pub struct FetchInfoError(pub ErrorType); -impl FetchInfosError { +impl FetchInfoError { pub const fn missing() -> Self { Self(ErrorType::Missing) } @@ -28,13 +28,13 @@ impl FetchInfosError { } } -impl From for FetchInfosError { +impl From for FetchInfoError { fn from(_: VarError) -> Self { Self::missing() } } -impl From for FetchInfosError { +impl From for FetchInfoError { fn from(error: std::io::Error) -> Self { match error.kind() { ErrorKind::NotFound | ErrorKind::PermissionDenied => Self::missing(), diff --git a/src/main.rs b/src/main.rs index 59cc2b4..898fb17 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use afetch::config::{Config, Entry, LogoStyle, SeparatorSizing, load_config}; -use afetch::error::{ErrorType, FetchInfosError}; +use afetch::error::{ErrorType, FetchInfoError}; use afetch::logos::get_logo; use afetch::system::battery::get_battery; use afetch::system::cpu::get_cpu; @@ -10,7 +10,7 @@ use afetch::system::kernel::get_kernel; use afetch::system::loadavg::get_loadavg; use afetch::system::memory::get_memory; use afetch::system::uptime::get_uptime; -use afetch::system::{InfoFunction, InfoGroup, InfoKind, InfosResult}; +use afetch::system::{InfoGroup, InfoKind, InfoResult}; use afetch::translations::get_language; use afetch::util::colored::{ColorWrapper, ColorizeExt}; use afetch::util::count_str_length; @@ -18,31 +18,33 @@ use afetch::util::count_str_length; use afetch::util::print_picture; use colored::Colorize; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use std::collections::HashMap; use std::fmt::Write; use supports_unicode::supports_unicode; -fn main() -> Result<(), FetchInfosError> { +fn main() -> Result<(), FetchInfoError> { let config: Config = load_config(); let language_func = get_language(config.language); - let results: Vec> = config - .entries + let results: HashMap> = config + .info .par_iter() - .filter_map(|element| match element { - Entry::Info { kind, fields, .. } => match kind { - InfoKind::Battery => Some((get_battery as InfoFunction, fields)), - InfoKind::Cpu => Some((get_cpu as InfoFunction, fields)), - InfoKind::Disk => Some((get_disk as InfoFunction, fields)), - InfoKind::Disks => Some((get_disks as InfoFunction, fields)), - InfoKind::Host => Some((get_hostname as InfoFunction, fields)), - InfoKind::Kernel => Some((get_kernel as InfoFunction, fields)), - InfoKind::Uptime => Some((get_uptime as InfoFunction, fields)), - InfoKind::Memory => Some((get_memory as InfoFunction, fields)), - InfoKind::Loadavg => Some((get_loadavg as InfoFunction, fields)), - }, - _ => None, + .map(|(kind, fields)| { + ( + *kind, + match kind { + InfoKind::Battery => get_battery(language_func, fields, &config), + InfoKind::Cpu => get_cpu(language_func, fields, &config), + InfoKind::Disk => get_disk(language_func, fields, &config), + InfoKind::Disks => get_disks(language_func, fields, &config), + InfoKind::Host => get_hostname(language_func, fields, &config), + InfoKind::Kernel => get_kernel(language_func, fields, &config), + InfoKind::Uptime => get_uptime(language_func, fields, &config), + InfoKind::Memory => get_memory(language_func, fields, &config), + InfoKind::Loadavg => get_loadavg(language_func, fields, &config), + }, + ) }) - .map(|(f, fields)| f(language_func, fields, &config)) .collect(); let logo = if supports_unicode() { @@ -83,7 +85,7 @@ fn main() -> Result<(), FetchInfosError> { let mut output: String = String::default(); let mut last_info_len = 0; let mut i = 0; - let mut i2 = 0; + for entry in &config.entries { let mut write_entry = |entry: String| { #[cfg(feature = "image")] @@ -115,7 +117,7 @@ fn main() -> Result<(), FetchInfosError> { separator, .. } => { - let result = match &results[i2] { + let result = match &results[kind] { Ok(result) => result, Err(error) => { match &error.0 { @@ -152,11 +154,9 @@ fn main() -> Result<(), FetchInfosError> { }; match result { - InfosResult::Single(single) => format_and_write(single), - InfosResult::Several(elements) => elements.iter().for_each(format_and_write), + InfoResult::Single(single) => format_and_write(single), + InfoResult::Several(elements) => elements.iter().for_each(format_and_write), } - - i2 += 1; } Entry::Separator { content, sizing } => { let formatted_separator = match sizing { diff --git a/src/system/battery.rs b/src/system/battery.rs index 54ec7fe..2d7348d 100644 --- a/src/system/battery.rs +++ b/src/system/battery.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use crate::util::format_time; use starship_battery::units::time::second; @@ -9,11 +9,11 @@ pub fn get_battery( languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { +) -> Result { let mut batteries = starship_battery::Manager::new() - .map_err(|error| FetchInfosError::error(error.to_string()))? + .map_err(|error| FetchInfoError::error(error.to_string()))? .batteries() - .map_err(|error| FetchInfosError::error(error.to_string()))?; + .map_err(|error| FetchInfoError::error(error.to_string()))?; let mut batteries_info: Vec = Vec::new(); @@ -128,5 +128,5 @@ pub fn get_battery( batteries_info.push(info_group); } - Ok(InfosResult::Several(batteries_info)) + Ok(InfoResult::Several(batteries_info)) } diff --git a/src/system/cpu.rs b/src/system/cpu.rs index 20df837..344d530 100644 --- a/src/system/cpu.rs +++ b/src/system/cpu.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use std::collections::HashSet; use sysinfo::{CpuRefreshKind, RefreshKind, System}; @@ -9,7 +9,7 @@ pub fn get_cpu( _languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { +) -> Result { let system = System::new_with_specifics(RefreshKind::nothing().with_cpu(CpuRefreshKind::everything())); @@ -35,5 +35,5 @@ pub fn get_cpu( }); } - Ok(InfosResult::Several(cpu_info)) + Ok(InfoResult::Several(cpu_info)) } diff --git a/src/system/disk.rs b/src/system/disk.rs index 474ccf0..7ea1d75 100644 --- a/src/system/disk.rs +++ b/src/system/disk.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use crate::util::convert_to_readable_unity; use sysinfo::Disks; @@ -9,7 +9,7 @@ pub fn get_disk( _languages_func: fn(&str) -> &str, fields: &[InfoField], config: &Config, -) -> Result { +) -> Result { let mut disks_info: Vec = Vec::new(); for disk in Disks::new_with_refreshed_list().list() { @@ -33,7 +33,7 @@ pub fn get_disk( ( InfoField::DiskName, disk.name().to_os_string().into_string().map_err(|_| { - FetchInfosError::error("Failed to convert disk name to String") + FetchInfoError::error("Failed to convert disk name to String") })? ), ( @@ -71,5 +71,5 @@ pub fn get_disk( }); } - Ok(InfosResult::Several(disks_info)) + Ok(InfoResult::Several(disks_info)) } diff --git a/src/system/disks.rs b/src/system/disks.rs index 8feea27..84c849b 100644 --- a/src/system/disks.rs +++ b/src/system/disks.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use crate::util::convert_to_readable_unity; use sysinfo::{DiskRefreshKind, Disks}; @@ -9,7 +9,7 @@ pub fn get_disks( _languages_func: fn(&str) -> &str, fields: &[InfoField], config: &Config, -) -> Result { +) -> Result { let mut available_space = 0; let mut total_space = 0; let mut count = 0; @@ -34,7 +34,7 @@ pub fn get_disks( count += 1; } - Ok(InfosResult::Single(InfoGroup { + Ok(InfoResult::Single(InfoGroup { values: filtered_values!( fields, [ diff --git a/src/system/host.rs b/src/system/host.rs index 06f94d1..84e7aef 100644 --- a/src/system/host.rs +++ b/src/system/host.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use whoami::fallible::hostname; use whoami::username; @@ -9,8 +9,8 @@ pub fn get_hostname( _languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { - Ok(InfosResult::Single(InfoGroup { +) -> Result { + Ok(InfoResult::Single(InfoGroup { values: filtered_values!( fields, [ diff --git a/src/system/kernel.rs b/src/system/kernel.rs index fb5ecbb..93338e1 100644 --- a/src/system/kernel.rs +++ b/src/system/kernel.rs @@ -1,21 +1,21 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use sysinfo::System; pub fn get_kernel( _languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { - Ok(InfosResult::Single(InfoGroup { +) -> Result { + Ok(InfoResult::Single(InfoGroup { values: filtered_values!( fields, [ ( InfoField::KernelVersion, - System::kernel_version().ok_or_else(FetchInfosError::missing)? + System::kernel_version().ok_or_else(FetchInfoError::missing)? ), (InfoField::KernelLongVersion, System::kernel_long_version()), ] diff --git a/src/system/loadavg.rs b/src/system/loadavg.rs index a18adc8..dfe525b 100644 --- a/src/system/loadavg.rs +++ b/src/system/loadavg.rs @@ -1,16 +1,16 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use sysinfo::System; pub fn get_loadavg( _languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { +) -> Result { let loadavg = System::load_average(); - Ok(InfosResult::Single(InfoGroup { + Ok(InfoResult::Single(InfoGroup { values: filtered_values!( fields, [ diff --git a/src/system/memory.rs b/src/system/memory.rs index 45e881f..0b3b82d 100644 --- a/src/system/memory.rs +++ b/src/system/memory.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use crate::util::convert_to_readable_unity; use sysinfo::{MemoryRefreshKind, RefreshKind, System}; @@ -9,11 +9,11 @@ pub fn get_memory( _languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { +) -> Result { let system = System::new_with_specifics( RefreshKind::nothing().with_memory(MemoryRefreshKind::everything()), ); - Ok(InfosResult::Single(InfoGroup { + Ok(InfoResult::Single(InfoGroup { values: filtered_values!( fields, [ diff --git a/src/system/mod.rs b/src/system/mod.rs index f4822fd..850c074 100755 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -1,5 +1,5 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use bitcode::{Decode, Encode}; use serde::Deserialize; @@ -14,7 +14,7 @@ pub mod memory; pub mod uptime; pub type InfoFunction = - fn(fn(&str) -> &str, &[InfoField], &Config) -> Result; + fn(fn(&str) -> &str, &[InfoField], &Config) -> Result; #[macro_export] macro_rules! filtered_values { @@ -32,7 +32,7 @@ macro_rules! filtered_values { }}; } -#[derive(Deserialize, Clone, Copy, Debug, Decode, Encode)] +#[derive(Deserialize, Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum InfoKind { Battery, @@ -85,6 +85,7 @@ impl InfoKind { InfoField::DiskKind, InfoField::DiskWrittenSinceBoot, InfoField::DiskReadSinceBoot, + InfoField::DiskFileSystem, ], Self::Disks => &[ InfoField::DisksCount, @@ -141,7 +142,7 @@ impl InfoKind { } } -#[derive(Debug, PartialEq, Clone, Copy, Decode, Encode)] +#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash, Decode, Encode)] pub enum InfoField { BatteryModel, BatteryCycleCount, @@ -269,7 +270,7 @@ pub struct InfoGroup { pub values: Vec, } -pub enum InfosResult { +pub enum InfoResult { Single(InfoGroup), Several(Vec), } diff --git a/src/system/uptime.rs b/src/system/uptime.rs index 210d9d2..6a332d2 100644 --- a/src/system/uptime.rs +++ b/src/system/uptime.rs @@ -1,7 +1,7 @@ use crate::config::Config; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; use crate::filtered_values; -use crate::system::{InfoField, InfoGroup, InfoValue, InfosResult}; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; use crate::util::format_time; use sysinfo::System; @@ -9,14 +9,14 @@ pub fn get_uptime( languages_func: fn(&str) -> &str, fields: &[InfoField], _config: &Config, -) -> Result { - Ok(InfosResult::Single(InfoGroup { +) -> Result { + Ok(InfoResult::Single(InfoGroup { values: filtered_values!( fields, [( InfoField::Uptime, format_time(System::uptime(), languages_func) - .ok_or_else(FetchInfosError::missing)? + .ok_or_else(FetchInfoError::missing)? ),] ), })) diff --git a/src/util/mod.rs b/src/util/mod.rs index ef8a086..5014659 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,6 +1,6 @@ pub mod colored; -use crate::error::FetchInfosError; +use crate::error::FetchInfoError; #[cfg(feature = "image")] use image::{GenericImageView, ImageReader}; use std::fmt::Write; @@ -23,7 +23,7 @@ pub fn command_exist(program: &str) -> bool { which::which(program).is_ok() } -pub fn str_from_command(command: &mut Command) -> Result { +pub fn str_from_command(command: &mut Command) -> Result { Ok(String::from_utf8_lossy(&command.output()?.stdout).into_owned()) } @@ -84,20 +84,20 @@ pub fn convert_to_readable_unity>(size: T) -> String { pub fn print_picture(path: &str) { let file = match File::open(path) { Ok(f) => f, - Err(error) => FetchInfosError::error_exit(format!( + Err(error) => FetchInfoError::error_exit(format!( "An error occurred while reading the image: {error}" )), }; let reader: ImageReader> = match ImageReader::new(BufReader::new(file)).with_guessed_format() { Ok(r) => r, - Err(error) => FetchInfosError::error_exit(format!( + Err(error) => FetchInfoError::error_exit(format!( "An error occurred while guessing the image format: {error}" )), }; let image = match reader.decode() { Ok(i) => i, - Err(error) => FetchInfosError::error_exit(format!( + Err(error) => FetchInfoError::error_exit(format!( "An error occurred while decoding the image: {error}" )), }; @@ -118,7 +118,7 @@ pub fn print_picture(path: &str) { ..ViuerConfig::default() }; if let Err(error) = viuer::print(&image, &config) { - FetchInfosError::error_exit(format!( + FetchInfoError::error_exit(format!( "An error occurred while printing the image: {error}", )) }