From 19726c262b689305b1920979882b915b66270de5 Mon Sep 17 00:00:00 2001 From: Birger Schacht Date: Fri, 28 Jul 2023 20:03:17 +0200 Subject: [PATCH] refactor: major refactoring of the code base `opts` was renamed to `cli` to conform to upstreams (clap) naming convention `lib` was renamed to `utils` The whole style module was split up - the styles as defined in the theme are now part of the theme module. The `utils` module contains a simple method to convert the style definition from the theme to a string. `theme` is now a submodule of `config` The `DateProperty` is now part of the theme as well. The `date`, `calendar` and `agenda` modules where moved to a new `output` module. --- src/{opts.rs => cli.rs} | 34 +-- src/config/carlstyle.rs | 261 ------------------ src/config/mod.rs | 107 +------ .../components.rs} | 61 +++- src/config/theme/mod.rs | 114 ++++++++ src/context.rs | 13 +- src/events/mod.rs | 2 +- src/main.rs | 20 +- src/{ => output}/agenda.rs | 7 +- src/{ => output}/calendar.rs | 3 +- src/{ => output}/date.rs | 26 +- src/output/mod.rs | 7 + src/{lib => utils}/date_extensions.rs | 0 src/utils/helpers.rs | 190 +++++++++++++ src/{lib => utils}/mod.rs | 5 +- src/{lib => utils}/types.rs | 0 16 files changed, 415 insertions(+), 435 deletions(-) rename src/{opts.rs => cli.rs} (92%) delete mode 100644 src/config/carlstyle.rs rename src/config/{themecomponents.rs => theme/components.rs} (63%) create mode 100644 src/config/theme/mod.rs rename src/{ => output}/agenda.rs (91%) rename src/{ => output}/calendar.rs (99%) rename src/{ => output}/date.rs (92%) create mode 100644 src/output/mod.rs rename src/{lib => utils}/date_extensions.rs (100%) create mode 100644 src/utils/helpers.rs rename src/{lib => utils}/mod.rs (66%) rename src/{lib => utils}/types.rs (100%) diff --git a/src/opts.rs b/src/cli.rs similarity index 92% rename from src/opts.rs rename to src/cli.rs index a9a34ad..7909cea 100644 --- a/src/opts.rs +++ b/src/cli.rs @@ -2,14 +2,14 @@ // // SPDX-License-Identifier: MIT -use crate::lib::types::ChronoDate; +use crate::utils::ChronoDate; use anyhow::{anyhow, Result}; use chrono::prelude::*; use clap::{crate_authors, crate_name, crate_version, Parser}; #[derive(Parser)] #[clap(version = crate_version!(), author = crate_authors!(","), about = "Display a calendar")] -pub struct Opts { +pub struct Cli { #[clap(short = '1', long = "one", help = "show only current month (default)", conflicts_with_all = &["three", "year"])] pub one: bool, #[clap( @@ -39,9 +39,9 @@ pub struct Opts { pub date: Vec, } -impl Default for Opts { +impl Default for Cli { fn default() -> Self { - Opts { + Cli { one: true, three: false, year: false, @@ -56,7 +56,7 @@ impl Default for Opts { } } -impl Opts { +impl Cli { pub fn validate_date(&self) -> Result { let mut today: ChronoDate = Local::now().date_naive(); let mut year: i32 = today.year(); @@ -148,13 +148,13 @@ mod tests { #[test] fn test_validate_date_defaults_to_now() { let today: ChronoDate = Local::now().date_naive(); - let o: Opts = Opts::default(); + let o: Cli = Cli::default(); assert_eq!(today, o.validate_date().unwrap()); } #[test] fn test_validate_date_default_to_now_with_custom_year() { let today: ChronoDate = Local::now().date_naive().with_year(2007).unwrap(); - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("2007")]; assert_eq!(today, o.validate_date().unwrap()); } @@ -166,7 +166,7 @@ mod tests { .unwrap() .with_month(1) .unwrap(); - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("2007"), String::from("1")]; assert_eq!(today, o.validate_date().unwrap()); } @@ -180,14 +180,14 @@ mod tests { .unwrap() .with_day(28) .unwrap(); - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("2007"), String::from("1"), String::from("28")]; assert_eq!(today, o.validate_date().unwrap()); } #[test] fn test_validate_date_defaults_to_now_with_ambiguous_arguments() { let today: ChronoDate = Local::now().date_naive(); - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![ String::from("2007"), String::from("1"), @@ -198,19 +198,19 @@ mod tests { } #[test] fn test_validate_date_errors_with_wrong_month() { - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("2007"), String::from("13"), String::from("28")]; assert!(o.validate_date().is_err()); } #[test] fn test_validate_date_errors_with_wrong_day() { - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("2007"), String::from("11"), String::from("33")]; assert!(o.validate_date().is_err()); } #[test] fn test_validate_date_errors_with_wrong_year() { - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![ String::from("999999"), String::from("11"), @@ -220,13 +220,13 @@ mod tests { } #[test] fn test_validate_date_errors_with_unparsable_year() { - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("foo"), String::from("13"), String::from("28")]; assert!(o.validate_date().is_err()); } #[test] fn test_validate_date_errors_with_unparsable_month() { - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![ String::from("2007"), String::from("foo"), @@ -236,7 +236,7 @@ mod tests { } #[test] fn test_validate_date_errors_with_unparsable_day() { - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![ String::from("2007"), String::from("11"), @@ -247,7 +247,7 @@ mod tests { #[test] fn test_validate_date_errors_with_non_existent_date() { let today: ChronoDate = Local::now().date_naive(); - let mut o: Opts = Opts::default(); + let mut o: Cli = Cli::default(); o.date = vec![String::from("2007"), String::from("2"), String::from("30")]; assert_eq!(today, o.validate_date().unwrap()); } diff --git a/src/config/carlstyle.rs b/src/config/carlstyle.rs deleted file mode 100644 index ed1fc6e..0000000 --- a/src/config/carlstyle.rs +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2023 Birger Schacht -// -// SPDX-License-Identifier: MIT - -use anstyle::Ansi256Color; -use anstyle::AnsiColor::*; -use anstyle::RgbColor; -use anstyle::Style; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub enum AnsiTermStyle { - Bold, - Dimmed, - Italic, - Underline, - Blink, - Reverse, - Hidden, - Strikethrough, - - FGBlack, - FGRed, - FGGreen, - FGYellow, - FGBlue, - FGPurple, - FGCyan, - FGWhite, - #[serde(rename = "FGRGB")] - FGrgb { - r: u8, - g: u8, - b: u8, - }, - FGFixed(u8), - - BGBlack, - BGRed, - BGGreen, - BGYellow, - BGBlue, - BGPurple, - BGCyan, - BGWhite, - #[serde(rename = "BGRGB")] - BGrgb { - r: u8, - g: u8, - b: u8, - }, - BGFixed(u8), -} - -type AnsiTermStyleVector = Vec; - -pub trait StyleConversion { - fn to_style(&self) -> Style; -} - -impl StyleConversion for AnsiTermStyleVector { - fn to_style(&self) -> Style { - let mut style = Style::default(); - for ansistyle in self { - style = match ansistyle { - AnsiTermStyle::Bold => style.bold(), - AnsiTermStyle::Dimmed => style.dimmed(), - AnsiTermStyle::Italic => style.italic(), - AnsiTermStyle::Underline => style.underline(), - AnsiTermStyle::Blink => style.blink(), - AnsiTermStyle::Reverse => style.effects(anstyle::Effects::INVERT), - AnsiTermStyle::Hidden => style.hidden().bg_color(None), - AnsiTermStyle::Strikethrough => style.strikethrough(), - - AnsiTermStyle::FGBlack => style.fg_color(Some(Black.into())), - AnsiTermStyle::FGRed => style.fg_color(Some(Red.into())), - AnsiTermStyle::FGGreen => style.fg_color(Some(Green.into())), - AnsiTermStyle::FGYellow => style.fg_color(Some(Yellow.into())), - AnsiTermStyle::FGBlue => style.fg_color(Some(Blue.into())), - AnsiTermStyle::FGPurple => style.fg_color(Some(Magenta.into())), - AnsiTermStyle::FGCyan => style.fg_color(Some(Cyan.into())), - AnsiTermStyle::FGWhite => style.fg_color(Some(White.into())), - AnsiTermStyle::FGrgb { r, g, b } => { - style.fg_color(Some(RgbColor(*r, *g, *b).into())) - } - AnsiTermStyle::FGFixed(x) => style.fg_color(Some(Ansi256Color(*x).into())), - - AnsiTermStyle::BGBlack => style.bg_color(Some(Black.into())), - AnsiTermStyle::BGRed => style.bg_color(Some(Red.into())), - AnsiTermStyle::BGGreen => style.bg_color(Some(Green.into())), - AnsiTermStyle::BGYellow => style.bg_color(Some(Yellow.into())), - AnsiTermStyle::BGBlue => style.bg_color(Some(Blue.into())), - AnsiTermStyle::BGPurple => style.bg_color(Some(Magenta.into())), - AnsiTermStyle::BGCyan => style.bg_color(Some(Cyan.into())), - AnsiTermStyle::BGWhite => style.bg_color(Some(White.into())), - AnsiTermStyle::BGrgb { r, g, b } => { - style.bg_color(Some(RgbColor(*r, *g, *b).into())) - } - AnsiTermStyle::BGFixed(x) => style.bg_color(Some(Ansi256Color(*x).into())), - } - } - style - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_to_style1() { - let a = vec![ - AnsiTermStyle::Bold, - AnsiTermStyle::FGBlack, - AnsiTermStyle::BGBlack, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.bold() - .fg_color(Some(Black.into())) - .bg_color(Some(Black.into())) - ]; - } - #[test] - fn test_to_style2() { - let a = vec![ - AnsiTermStyle::Dimmed, - AnsiTermStyle::FGRed, - AnsiTermStyle::BGRed, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.dimmed() - .fg_color(Some(Red.into())) - .bg_color(Some(Red.into())) - ]; - } - #[test] - fn test_to_style3() { - let a = vec![ - AnsiTermStyle::Italic, - AnsiTermStyle::FGGreen, - AnsiTermStyle::BGGreen, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.italic() - .fg_color(Some(Green.into())) - .bg_color(Some(Green.into())) - ]; - } - #[test] - fn test_to_style4() { - let a = vec![ - AnsiTermStyle::Underline, - AnsiTermStyle::FGYellow, - AnsiTermStyle::BGYellow, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.underline() - .fg_color(Some(Yellow.into())) - .bg_color(Some(Yellow.into())) - ]; - } - #[test] - fn test_to_style5() { - let a = vec![ - AnsiTermStyle::Blink, - AnsiTermStyle::FGBlue, - AnsiTermStyle::BGBlue, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.blink() - .fg_color(Some(Blue.into())) - .bg_color(Some(Blue.into())) - ]; - } - #[test] - fn test_to_style6() { - let a = vec![ - AnsiTermStyle::Reverse, - AnsiTermStyle::FGPurple, - AnsiTermStyle::BGPurple, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.effects(anstyle::Effects::INVERT) - .fg_color(Some(Magenta.into())) - .bg_color(Some(Magenta.into())) - ]; - } - #[test] - fn test_to_style7() { - let a = vec![AnsiTermStyle::BGRed, AnsiTermStyle::Hidden]; - let b = Style::default(); - assert_eq![a.to_style(), b.hidden()]; - } - #[test] - fn test_to_style8() { - let a = vec![ - AnsiTermStyle::Strikethrough, - AnsiTermStyle::FGCyan, - AnsiTermStyle::BGCyan, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.strikethrough() - .fg_color(Some(Cyan.into())) - .bg_color(Some(Cyan.into())) - ]; - } - #[test] - fn test_to_style9() { - let a = vec![AnsiTermStyle::FGWhite, AnsiTermStyle::BGWhite]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.fg_color(Some(White.into())).bg_color(Some(White.into())) - ]; - } - #[test] - fn test_to_style10() { - let a = vec![AnsiTermStyle::FGFixed(17), AnsiTermStyle::BGFixed(71)]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.bg_color(Some(Ansi256Color(71).into())) - .fg_color(Some(Ansi256Color(17).into())) - ]; - } - #[test] - fn test_to_style11() { - let a = vec![ - AnsiTermStyle::FGrgb { - r: 17, - g: 18, - b: 19, - }, - AnsiTermStyle::BGrgb { - r: 17, - g: 18, - b: 19, - }, - ]; - let b = Style::default(); - assert_eq![ - a.to_style(), - b.fg_color(Some(RgbColor(17, 18, 19).into())) - .bg_color(Some(RgbColor(17, 18, 19).into())) - ]; - } -} diff --git a/src/config/mod.rs b/src/config/mod.rs index 84c2036..783a8bb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,14 +2,10 @@ // // SPDX-License-Identifier: MIT -mod carlstyle; -mod themecomponents; +mod theme; -pub use carlstyle::{AnsiTermStyle, StyleConversion}; -pub use themecomponents::{DateStyle, Style, StyleType}; - -use crate::date::DateProperty::*; -use AnsiTermStyle::*; +pub use theme::StyleName::*; +pub use theme::{DateProperty, DateStyle, Style, StyleName, StyleType, Theme}; extern crate xdg; use clap::crate_name; @@ -54,83 +50,6 @@ impl Config { } } -#[derive(Deserialize, Serialize, Debug, Clone)] -#[serde(default)] -pub struct Theme { - pub date: Vec, -} - -impl Theme { - #[cfg(not(tarpaulin_include))] - pub fn read(theme: &Option) -> Theme { - if let Some(themename) = theme { - match xdg::BaseDirectories::with_prefix(crate_name!()) { - Ok(xdg_dirs) => { - if let Some(theme_path) = - xdg_dirs.find_config_file(format!("{}.theme", themename)) - { - let theme_content = fs::read_to_string(&theme_path).unwrap_or_default(); - match toml::from_str(&theme_content) { - Ok(theme) => return theme, - Err(e) => eprintln!("Could not parse theme file: {}", e), - } - } else { - eprintln!( - "Could not load theme file {}.toml, using builtin theme.", - themename - ); - } - } - Err(e) => eprintln!("Cannot determine XDG base directories: {}", e), - } - } - Theme::default() - } -} - -impl Default for Theme { - fn default() -> Self { - Theme { - date: default_datestyle_vector(), - } - } -} - -fn default_datestyle_vector() -> Vec { - vec![ - DateStyle { - properties: vec![CurrentDate], - style: Style { - stylenames: vec![Bold, Underline], - ..Default::default() - }, - }, - DateStyle { - properties: vec![BeforeCurrentDate], - style: Style { - stylenames: vec![Dimmed], - ..Default::default() - }, - }, - DateStyle { - properties: vec![BeforeFirstDayOfMonth], - style: Style { - stylenames: vec![Hidden], - weight: 1, - ..Default::default() - }, - }, - DateStyle { - properties: vec![AfterLastDayOfMonth], - style: Style { - stylenames: vec![Hidden], - weight: 1, - ..Default::default() - }, - }, - ] -} - #[derive(Deserialize, Serialize, Debug)] #[serde(default)] pub struct IcalStyle { @@ -172,26 +91,6 @@ mod tests { assert![a.theme.is_none()]; } - #[test] - fn test_default_theme() { - let a = Theme::default(); - assert![!a.date.is_empty()]; - } - - #[test] - fn test_theme_read_none() { - let a = Theme::read(&None); - assert![!a.date.is_empty()]; - } - - #[test] - fn test_theme_read_filename() { - env::set_var("XDG_CONFIG_DIRS", "/nonexistent"); - env::set_var("XDG_CONFIG_HOME", "/nonexistent"); - let a = Theme::read(&Some(String::from("nonexistent"))); - assert![!a.date.is_empty()]; - } - #[test] fn test_default_icalstyle() { let a = IcalStyle::default(); diff --git a/src/config/themecomponents.rs b/src/config/theme/components.rs similarity index 63% rename from src/config/themecomponents.rs rename to src/config/theme/components.rs index 7ea2cf4..d447ec5 100644 --- a/src/config/themecomponents.rs +++ b/src/config/theme/components.rs @@ -4,9 +4,6 @@ use serde::{Deserialize, Serialize}; -use crate::config::AnsiTermStyle; -use crate::date::DateProperty; - #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(default)] pub struct DateStyle { @@ -33,7 +30,7 @@ pub enum StyleType { #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(default)] pub struct Style { - pub stylenames: Vec, + pub stylenames: Vec, pub weight: u32, pub styletype: StyleType, } @@ -47,6 +44,62 @@ impl Default for Style { } } +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum StyleName { + Bold, + Dimmed, + Italic, + Underline, + Blink, + Reverse, + Hidden, + Strikethrough, + + FGBlack, + FGRed, + FGGreen, + FGYellow, + FGBlue, + FGPurple, + FGCyan, + FGWhite, + #[serde(rename = "FGRGB")] + FGrgb { + r: u8, + g: u8, + b: u8, + }, + FGFixed(u8), + + BGBlack, + BGRed, + BGGreen, + BGYellow, + BGBlue, + BGPurple, + BGCyan, + BGWhite, + #[serde(rename = "BGRGB")] + BGrgb { + r: u8, + g: u8, + b: u8, + }, + BGFixed(u8), +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum DateProperty { + FirstDayOfMonth, + BeforeFirstDayOfMonth, + BeforeCurrentDate, + CurrentDate, + AfterCurrentDate, + AfterLastDayOfMonth, + LastDayOfMonth, + IsEvent, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/config/theme/mod.rs b/src/config/theme/mod.rs new file mode 100644 index 0000000..5002a16 --- /dev/null +++ b/src/config/theme/mod.rs @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: 2021-2023 Birger Schacht +// +// SPDX-License-Identifier: MIT +mod components; + +pub use crate::config::theme::components::DateProperty::*; +pub use crate::config::theme::components::StyleName::*; +pub use crate::config::theme::components::{DateProperty, DateStyle, Style, StyleName, StyleType}; +use clap::crate_name; +use serde::{Deserialize, Serialize}; +use std::fs; + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(default)] +pub struct Theme { + pub date: Vec, +} + +impl Theme { + #[cfg(not(tarpaulin_include))] + pub fn read(theme: &Option) -> Theme { + if let Some(themename) = theme { + match xdg::BaseDirectories::with_prefix(crate_name!()) { + Ok(xdg_dirs) => { + if let Some(theme_path) = + xdg_dirs.find_config_file(format!("{}.theme", themename)) + { + let theme_content = fs::read_to_string(&theme_path).unwrap_or_default(); + match toml::from_str(&theme_content) { + Ok(theme) => return theme, + Err(e) => eprintln!("Could not parse theme file: {}", e), + } + } else { + eprintln!( + "Could not load theme file {}.toml, using builtin theme.", + themename + ); + } + } + Err(e) => eprintln!("Cannot determine XDG base directories: {}", e), + } + } + Theme::default() + } +} + +impl Default for Theme { + fn default() -> Self { + Theme { + date: default_datestyle_vector(), + } + } +} + +fn default_datestyle_vector() -> Vec { + vec![ + DateStyle { + properties: vec![CurrentDate], + style: Style { + stylenames: vec![Bold, Underline], + ..Default::default() + }, + }, + DateStyle { + properties: vec![BeforeCurrentDate], + style: Style { + stylenames: vec![Dimmed], + ..Default::default() + }, + }, + DateStyle { + properties: vec![BeforeFirstDayOfMonth], + style: Style { + stylenames: vec![Hidden], + weight: 1, + ..Default::default() + }, + }, + DateStyle { + properties: vec![AfterLastDayOfMonth], + style: Style { + stylenames: vec![Hidden], + weight: 1, + ..Default::default() + }, + }, + ] +} + +#[cfg(test)] +mod tests { + use super::*; + use std::env; + + #[test] + fn test_default_theme() { + let a = Theme::default(); + assert![!a.date.is_empty()]; + } + + #[test] + fn test_theme_read_none() { + let a = Theme::read(&None); + assert![!a.date.is_empty()]; + } + + #[test] + fn test_theme_read_filename() { + env::set_var("XDG_CONFIG_DIRS", "/nonexistent"); + env::set_var("XDG_CONFIG_HOME", "/nonexistent"); + let a = Theme::read(&Some(String::from("nonexistent"))); + assert![!a.date.is_empty()]; + } +} diff --git a/src/context.rs b/src/context.rs index 1e49aa7..72bd213 100644 --- a/src/context.rs +++ b/src/context.rs @@ -2,10 +2,11 @@ // // SPDX-License-Identifier: MIT -use crate::config::{Config, Style, StyleType, Theme}; +use crate::cli::Cli; +use crate::config::{Config, Theme}; +use crate::config::{Style, StyleType}; use crate::events::Event; -use crate::lib::types::ChronoDate; -use crate::opts::Opts; +use crate::utils::ChronoDate; use anyhow::Result; use chrono::prelude::*; use clap::Parser; @@ -13,7 +14,7 @@ use clap::Parser; // A struct storing the combined settings of config file, theme, options, ... pub struct Context { pub usersetdate: ChronoDate, - pub opts: Opts, + pub opts: Cli, pub config: Config, pub eventstuple: Vec<(Event, Style)>, pub theme: Theme, @@ -22,7 +23,7 @@ pub struct Context { impl Context { pub fn new() -> Result { - let opts: Opts = Opts::parse(); + let opts: Cli = Cli::parse(); let config: Config = Config::read(); let theme: Theme = if opts.theme.is_some() { Theme::read(&opts.theme) @@ -57,7 +58,7 @@ impl Default for Context { fn default() -> Self { Context { usersetdate: NaiveDate::default(), - opts: Opts::default(), + opts: Cli::default(), config: Config::default(), eventstuple: vec![], theme: Theme::default(), diff --git a/src/events/mod.rs b/src/events/mod.rs index 948eb00..9783a1e 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -7,7 +7,7 @@ extern crate ical; mod ics; pub use ics::ReadFromIcsFile; -use crate::lib::types::{ChronoDate, ChronoDateTime}; +use crate::utils::{ChronoDate, ChronoDateTime}; use chrono::prelude::*; use chrono::Days; use std::fmt; diff --git a/src/main.rs b/src/main.rs index 7bfe99a..2907895 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,14 +2,12 @@ // // SPDX-License-Identifier: MIT -mod agenda; -mod calendar; +mod cli; mod config; mod context; -mod date; mod events; -mod lib; -mod opts; +mod output; +mod utils; extern crate clap; extern crate serde; @@ -18,14 +16,12 @@ extern crate xdg; use chrono::Duration; use std::process; -use agenda::Agenda; -use calendar::Calendar; use context::Context; -use date::Date; -use events::Events; -use events::ReadFromIcsFile; -use lib::types::ChronoDate; -use lib::DateExtensions; +use events::{Events, ReadFromIcsFile}; +use output::agenda::Agenda; +use output::calendar::Calendar; +use output::date::Date; +use utils::{ChronoDate, DateExtensions}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/src/agenda.rs b/src/output/agenda.rs similarity index 91% rename from src/agenda.rs rename to src/output/agenda.rs index 285db25..0aa63a3 100644 --- a/src/agenda.rs +++ b/src/output/agenda.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -use crate::config::StyleConversion; +use crate::utils::convertstyle; use crate::Context; use chrono::Datelike; @@ -32,9 +32,8 @@ impl fmt::Display for Agenda<'_> { }); for (event, style) in eventstuple { ret += format!( - "{}·{} {}\n", - style.stylenames.to_style().render(), - style.stylenames.to_style().render_reset(), + "{} {}\n", + convertstyle(style.stylenames.to_vec(), "·"), event ) .as_str(); diff --git a/src/calendar.rs b/src/output/calendar.rs similarity index 99% rename from src/calendar.rs rename to src/output/calendar.rs index 58e9e77..0cfbaf0 100644 --- a/src/calendar.rs +++ b/src/output/calendar.rs @@ -2,8 +2,7 @@ // // SPDX-License-Identifier: MIT -use crate::lib::types::ChronoDate; -use crate::lib::{DateExtensions, MonthFullWeeksIter}; +use crate::utils::{ChronoDate, DateExtensions, MonthFullWeeksIter}; use crate::Context; use crate::Date; use chrono::{Duration, NaiveDate}; diff --git a/src/date.rs b/src/output/date.rs similarity index 92% rename from src/date.rs rename to src/output/date.rs index 77266d7..80c8a74 100644 --- a/src/date.rs +++ b/src/output/date.rs @@ -2,26 +2,12 @@ // // SPDX-License-Identifier: MIT -use crate::config::{Style, StyleConversion, StyleType}; -use crate::lib::types::ChronoDate; -use crate::lib::DateExtensions; +use crate::config::{DateProperty, Style, StyleType}; +use crate::utils::{convertstyle, ChronoDate, DateExtensions}; use crate::Context; -use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Deserialize, Serialize, Debug, Clone)] -pub enum DateProperty { - FirstDayOfMonth, - BeforeFirstDayOfMonth, - BeforeCurrentDate, - CurrentDate, - AfterCurrentDate, - AfterLastDayOfMonth, - LastDayOfMonth, - IsEvent, -} - pub struct Date<'a> { pub date: ChronoDate, pub ctx: &'a Context, @@ -95,13 +81,7 @@ impl fmt::Display for Date<'_> { stylenames.append(&mut style.stylenames); } - write!( - f, - "{}{}{}", - stylenames.to_style().render(), - &dateformat, - stylenames.to_style().render_reset() - ) + write!(f, "{}", convertstyle(stylenames, &dateformat)) } } diff --git a/src/output/mod.rs b/src/output/mod.rs new file mode 100644 index 0000000..c4495b7 --- /dev/null +++ b/src/output/mod.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2021-2023 Birger Schacht +// +// SPDX-License-Identifier: MIT + +pub mod agenda; +pub mod calendar; +pub mod date; diff --git a/src/lib/date_extensions.rs b/src/utils/date_extensions.rs similarity index 100% rename from src/lib/date_extensions.rs rename to src/utils/date_extensions.rs diff --git a/src/utils/helpers.rs b/src/utils/helpers.rs new file mode 100644 index 0000000..5534544 --- /dev/null +++ b/src/utils/helpers.rs @@ -0,0 +1,190 @@ +// SPDX-FileCopyrightText: 2021-2023 Birger Schacht +// +// SPDX-License-Identifier: MIT + +use crate::config::StyleName; +use anstyle::Ansi256Color; +use anstyle::AnsiColor::*; +use anstyle::RgbColor; +use anstyle::Style; + +pub fn tostyle(styles: Vec) -> Style { + let mut style = Style::default(); + for ansistyle in styles { + style = match ansistyle { + StyleName::Bold => style.bold(), + StyleName::Dimmed => style.dimmed(), + StyleName::Italic => style.italic(), + StyleName::Underline => style.underline(), + StyleName::Blink => style.blink(), + StyleName::Reverse => style.effects(anstyle::Effects::INVERT), + StyleName::Hidden => style.hidden().bg_color(None), + StyleName::Strikethrough => style.strikethrough(), + + StyleName::FGBlack => style.fg_color(Some(Black.into())), + StyleName::FGRed => style.fg_color(Some(Red.into())), + StyleName::FGGreen => style.fg_color(Some(Green.into())), + StyleName::FGYellow => style.fg_color(Some(Yellow.into())), + StyleName::FGBlue => style.fg_color(Some(Blue.into())), + StyleName::FGPurple => style.fg_color(Some(Magenta.into())), + StyleName::FGCyan => style.fg_color(Some(Cyan.into())), + StyleName::FGWhite => style.fg_color(Some(White.into())), + StyleName::FGrgb { r, g, b } => style.fg_color(Some(RgbColor(r, g, b).into())), + StyleName::FGFixed(x) => style.fg_color(Some(Ansi256Color(x).into())), + + StyleName::BGBlack => style.bg_color(Some(Black.into())), + StyleName::BGRed => style.bg_color(Some(Red.into())), + StyleName::BGGreen => style.bg_color(Some(Green.into())), + StyleName::BGYellow => style.bg_color(Some(Yellow.into())), + StyleName::BGBlue => style.bg_color(Some(Blue.into())), + StyleName::BGPurple => style.bg_color(Some(Magenta.into())), + StyleName::BGCyan => style.bg_color(Some(Cyan.into())), + StyleName::BGWhite => style.bg_color(Some(White.into())), + StyleName::BGrgb { r, g, b } => style.bg_color(Some(RgbColor(r, g, b).into())), + StyleName::BGFixed(x) => style.bg_color(Some(Ansi256Color(x).into())), + } + } + style +} + +pub fn convertstyle(styles: Vec, s: &str) -> String { + let style = tostyle(styles); + format!("{}{}{}", style.render(), s, style.render_reset()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_to_style1() { + let a = vec![StyleName::Bold, StyleName::FGBlack, StyleName::BGBlack]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.bold() + .fg_color(Some(Black.into())) + .bg_color(Some(Black.into())) + ]; + } + #[test] + fn test_to_style2() { + let a = vec![StyleName::Dimmed, StyleName::FGRed, StyleName::BGRed]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.dimmed() + .fg_color(Some(Red.into())) + .bg_color(Some(Red.into())) + ]; + } + #[test] + fn test_to_style3() { + let a = vec![StyleName::Italic, StyleName::FGGreen, StyleName::BGGreen]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.italic() + .fg_color(Some(Green.into())) + .bg_color(Some(Green.into())) + ]; + } + #[test] + fn test_to_style4() { + let a = vec![ + StyleName::Underline, + StyleName::FGYellow, + StyleName::BGYellow, + ]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.underline() + .fg_color(Some(Yellow.into())) + .bg_color(Some(Yellow.into())) + ]; + } + #[test] + fn test_to_style5() { + let a = vec![StyleName::Blink, StyleName::FGBlue, StyleName::BGBlue]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.blink() + .fg_color(Some(Blue.into())) + .bg_color(Some(Blue.into())) + ]; + } + #[test] + fn test_to_style6() { + let a = vec![StyleName::Reverse, StyleName::FGPurple, StyleName::BGPurple]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.effects(anstyle::Effects::INVERT) + .fg_color(Some(Magenta.into())) + .bg_color(Some(Magenta.into())) + ]; + } + #[test] + fn test_to_style7() { + let a = vec![StyleName::BGRed, StyleName::Hidden]; + let b = Style::default(); + assert_eq![tostyle(a), b.hidden()]; + } + #[test] + fn test_to_style8() { + let a = vec![ + StyleName::Strikethrough, + StyleName::FGCyan, + StyleName::BGCyan, + ]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.strikethrough() + .fg_color(Some(Cyan.into())) + .bg_color(Some(Cyan.into())) + ]; + } + #[test] + fn test_to_style9() { + let a = vec![StyleName::FGWhite, StyleName::BGWhite]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.fg_color(Some(White.into())).bg_color(Some(White.into())) + ]; + } + #[test] + fn test_to_style10() { + let a = vec![StyleName::FGFixed(17), StyleName::BGFixed(71)]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.bg_color(Some(Ansi256Color(71).into())) + .fg_color(Some(Ansi256Color(17).into())) + ]; + } + #[test] + fn test_to_style11() { + let a = vec![ + StyleName::FGrgb { + r: 17, + g: 18, + b: 19, + }, + StyleName::BGrgb { + r: 17, + g: 18, + b: 19, + }, + ]; + let b = Style::default(); + assert_eq![ + tostyle(a), + b.fg_color(Some(RgbColor(17, 18, 19).into())) + .bg_color(Some(RgbColor(17, 18, 19).into())) + ]; + } +} diff --git a/src/lib/mod.rs b/src/utils/mod.rs similarity index 66% rename from src/lib/mod.rs rename to src/utils/mod.rs index 094d50a..39a5937 100644 --- a/src/lib/mod.rs +++ b/src/utils/mod.rs @@ -3,6 +3,9 @@ // SPDX-License-Identifier: MIT mod date_extensions; -pub mod types; +mod helpers; +mod types; pub use date_extensions::{DateExtensions, MonthFullWeeksIter}; +pub use helpers::convertstyle; +pub use types::{ChronoDate, ChronoDateTime}; diff --git a/src/lib/types.rs b/src/utils/types.rs similarity index 100% rename from src/lib/types.rs rename to src/utils/types.rs