From 7ecbc51123004a829f934470b38ad82d40000f3c Mon Sep 17 00:00:00 2001 From: Jamy Golden Date: Tue, 22 Oct 2024 23:36:06 +0200 Subject: [PATCH] Add list scheme system --- Cargo.lock | 16 +- tinted-builder-rust/Cargo.toml | 1 + tinted-builder-rust/src/operations/build.rs | 150 ++++++++++++++---- .../tests/fixtures/rendered/list.md | 1 + .../tests/fixtures/templates/list-config.yaml | 3 + .../fixtures/templates/list-template.mustache | 3 + tinted-builder-rust/tests/operation_build.rs | 64 +++++++- tinted-builder-rust/tests/test_utils.rs | 26 ++- tinted-builder-rust/tests/utils.rs | 21 +-- tinted-builder/src/scheme.rs | 2 + tinted-builder/src/scheme/base16.rs | 6 + 11 files changed, 228 insertions(+), 65 deletions(-) create mode 100644 tinted-builder-rust/tests/fixtures/rendered/list.md create mode 100644 tinted-builder-rust/tests/fixtures/templates/list-config.yaml create mode 100644 tinted-builder-rust/tests/fixtures/templates/list-template.mustache diff --git a/Cargo.lock b/Cargo.lock index 88c394b..a58075e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,6 +295,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ribboncurls" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb3028bdec0e690dd492c976f33a482fcb8d910291b2d99ee6d2680c6540121" +dependencies = [ + "html-escape", + "regex", + "serde", + "serde_yaml", + "thiserror", +] + [[package]] name = "ryu" version = "1.0.18" @@ -389,7 +402,7 @@ dependencies = [ "dirs", "quick-xml", "regex", - "ribboncurls", + "ribboncurls 0.2.1", "serde", "serde_yaml", "strip-ansi-escapes", @@ -405,6 +418,7 @@ dependencies = [ "clap", "dirs", "regex", + "ribboncurls 0.4.1", "serde", "serde_yaml", "strip-ansi-escapes", diff --git a/tinted-builder-rust/Cargo.toml b/tinted-builder-rust/Cargo.toml index 086b893..08691f2 100644 --- a/tinted-builder-rust/Cargo.toml +++ b/tinted-builder-rust/Cargo.toml @@ -17,6 +17,7 @@ anyhow = "1.0.80" clap = "4.5.2" dirs = "5.0.1" regex = "1.11.0" +ribboncurls = "0.4.1" serde = { version = "1.0.197", features = ["derive"] } serde_yaml = "0.9.32" diff --git a/tinted-builder-rust/src/operations/build.rs b/tinted-builder-rust/src/operations/build.rs index 732e012..8645840 100644 --- a/tinted-builder-rust/src/operations/build.rs +++ b/tinted-builder-rust/src/operations/build.rs @@ -4,7 +4,7 @@ use anyhow::{anyhow, Context, Result}; use std::collections::HashMap; use std::fs::{self, create_dir_all, read_to_string}; use std::path::{Path, PathBuf}; -use tinted_builder::{Scheme, SchemeSystem, Template}; +use tinted_builder::{Base16Scheme, Scheme, SchemeSystem, Template}; use utils::{get_scheme_files, parse_filename, ParsedFilename, SchemeFile, TemplateConfig}; use crate::helpers::write_to_file; @@ -82,7 +82,7 @@ pub fn build( )); } - let template_config_content = read_to_string(template_config_path)?; + let template_config_content = read_to_string(&template_config_path)?; let template_config: HashMap = serde_yaml::from_str(&template_config_content)?; @@ -105,42 +105,118 @@ pub fn build( // For each template definition in the templates/config.yaml file for (template_item_config_name, template_item_config_value) in template_config.iter() { - let template_item_scheme_files: Vec<(PathBuf, Scheme)> = all_scheme_files - .iter() - .filter_map(|(path, scheme)| { - if template_item_config_value - .supported_systems - .clone() - .unwrap_or(vec![SchemeSystem::default()]) - .contains(&scheme.get_scheme_system()) - { - Some((path.clone(), scheme.clone())) - } else { - None - } - }) - .collect(); - - generate_themes_for_config( - template_item_config_name, - template_item_config_value, - &theme_template_path, - &template_item_scheme_files, - is_quiet, - )?; + let supported_systems = template_item_config_value + .supported_systems + .clone() + .unwrap_or(vec![SchemeSystem::default()]); + + if supported_systems.contains(&SchemeSystem::List) { + render_list( + &theme_template_path, + (template_item_config_name, template_item_config_value), + all_scheme_files.clone(), + is_quiet, + )?; + } else { + let template_item_scheme_files: Vec<(PathBuf, Scheme)> = all_scheme_files + .iter() + .filter_map(|(path, scheme)| { + if supported_systems.contains(&scheme.get_scheme_system()) { + Some((path.clone(), scheme.clone())) + } else { + None + } + }) + .collect(); + + generate_themes_for_config( + template_item_config_name, + template_item_config_value, + &theme_template_path, + &template_item_scheme_files, + is_quiet, + )?; + } } Ok(()) } -fn generate_themes_for_config( - config_name: &str, - config_value: &TemplateConfig, - theme_template_path: impl AsRef, - scheme_files: &Vec<(PathBuf, Scheme)>, +fn render_list( + template_path: impl AsRef, + (config_name, config_value): (&str, &TemplateConfig), + all_scheme_files: Vec<(PathBuf, Scheme)>, is_quiet: bool, ) -> Result<()> { - let filename = match ( + let supported_systems = config_value + .supported_systems + .clone() + .unwrap_or(vec![SchemeSystem::default()]); + let filename = get_filename(config_value, is_quiet)?; + let mustache_template_path = template_path + .as_ref() + .join(format!("templates/{}.mustache", config_name)); + let template_content = read_to_string(&mustache_template_path).context(format!( + "Mustache template missing: {}", + mustache_template_path.display() + ))?; + let mut data: HashMap<&str, Vec> = HashMap::new(); + data.insert( + "schemes", + all_scheme_files + .clone() + .into_iter() + .filter_map(|(_, scheme)| match scheme { + Scheme::Base16(scheme) => { + if supported_systems.contains(&SchemeSystem::Base16) { + Some(scheme) + } else { + None + } + } + Scheme::Base24(scheme) => { + if supported_systems.contains(&SchemeSystem::Base24) { + Some(scheme) + } else { + None + } + } + _ => None, + }) + .collect::>(), + ); + let data = serde_yaml::to_string(&data).unwrap_or_default(); + let output = ribboncurls::render(&template_content, &data, None)?; + let parsed_filename = parse_filename(&template_path, &filename)?; + let output_path = parsed_filename.get_path(); + + if !parsed_filename.directory.exists() { + create_dir_all(&parsed_filename.directory)? + } + + write_to_file(&output_path, &output)?; + + if !is_quiet { + println!( + "Successfully generated \"{}\" list with filename \"{}\"", + supported_systems + .iter() + .filter_map(|item| if *item == SchemeSystem::List { + None + } else { + Some(item.as_str().to_string()) + }) + .collect::>() + .join(", "), + template_path.as_ref().join(filename).display(), + ); + } + + Ok(()) +} + +fn get_filename(config_value: &TemplateConfig, is_quiet: bool) -> Result { + match ( &config_value.filename, #[allow(deprecated)] &config_value.extension, @@ -182,7 +258,17 @@ fn generate_themes_for_config( _ => Err(anyhow!( "Config file is missing \"filepath\" or \"extension\" and \"output\" properties" )), - }?; + } +} + +fn generate_themes_for_config( + config_name: &str, + config_value: &TemplateConfig, + theme_template_path: impl AsRef, + scheme_files: &Vec<(PathBuf, Scheme)>, + is_quiet: bool, +) -> Result<()> { + let filename = get_filename(config_value, is_quiet)?; let mustache_template_path = theme_template_path .as_ref() .join(format!("templates/{}.mustache", config_name)); diff --git a/tinted-builder-rust/tests/fixtures/rendered/list.md b/tinted-builder-rust/tests/fixtures/rendered/list.md new file mode 100644 index 0000000..4c0824d --- /dev/null +++ b/tinted-builder-rust/tests/fixtures/rendered/list.md @@ -0,0 +1 @@ +base16-silk-light - variant: light diff --git a/tinted-builder-rust/tests/fixtures/templates/list-config.yaml b/tinted-builder-rust/tests/fixtures/templates/list-config.yaml new file mode 100644 index 0000000..fdad7d9 --- /dev/null +++ b/tinted-builder-rust/tests/fixtures/templates/list-config.yaml @@ -0,0 +1,3 @@ +list: + filename: list.md + supported-systems: [list, base16] diff --git a/tinted-builder-rust/tests/fixtures/templates/list-template.mustache b/tinted-builder-rust/tests/fixtures/templates/list-template.mustache new file mode 100644 index 0000000..566dd87 --- /dev/null +++ b/tinted-builder-rust/tests/fixtures/templates/list-template.mustache @@ -0,0 +1,3 @@ +{{#schemes}} +{{system}}-{{slug}} - variant: {{variant}} +{{/schemes}} diff --git a/tinted-builder-rust/tests/operation_build.rs b/tinted-builder-rust/tests/operation_build.rs index aa936eb..1e0c40e 100644 --- a/tinted-builder-rust/tests/operation_build.rs +++ b/tinted-builder-rust/tests/operation_build.rs @@ -3,7 +3,7 @@ mod test_utils; use anyhow::{Context, Result}; use std::fs; use std::path::PathBuf; -use test_utils::{run_command, write_to_file}; +use test_utils::{copy_dir_all, run_command, write_to_file}; fn setup(system: &str, scheme_name: &str) -> Result<(String, String, String, String)> { let config_file_path: PathBuf = @@ -275,9 +275,6 @@ fn test_operation_build_base24() -> Result<()> { base24_template_rendered_content_fixture, ) = setup(system, scheme_name)?; - if themes_path.is_dir() { - fs::remove_dir_all(&themes_path)?; - } if template_theme_path.is_dir() { fs::remove_dir_all(&template_theme_path)?; } @@ -355,9 +352,6 @@ fn test_operation_build_mixed() -> Result<()> { base24_template_rendered_content_fixture, ) = setup("base24", base24_scheme_name)?; - if themes_path.is_dir() { - fs::remove_dir_all(&themes_path)?; - } if template_theme_path.is_dir() { fs::remove_dir_all(&template_theme_path)?; } @@ -419,6 +413,62 @@ fn test_operation_build_mixed() -> Result<()> { Ok(()) } +#[test] +fn test_operation_build_list() -> Result<()> { + // ------- + // Arrange + // ------- + let name = "operation_build_list"; + let template_theme_path = PathBuf::from(format!("./template-{}", name)); + let template_templates_path = template_theme_path.join("templates"); + let schemes_path = template_theme_path.join("schemes"); + let rendered_theme_path = template_theme_path.join("list.md"); + + if template_theme_path.is_dir() { + fs::remove_dir_all(&template_theme_path)?; + } + fs::create_dir_all(&template_templates_path)?; + fs::copy( + "./tests/fixtures/templates/list-config.yaml", + template_theme_path.join("templates/config.yaml"), + )?; + fs::copy( + "./tests/fixtures/templates/list-template.mustache", + template_theme_path.join("templates/list.mustache"), + )?; + copy_dir_all("./tests/fixtures/schemes", &schemes_path)?; + + // --- + // Act + // --- + let (stdout, stderr) = run_command(vec![ + "build".to_string(), + template_theme_path.display().to_string(), + format!("--schemes-dir={}", schemes_path.display()), + ]) + .unwrap(); + let rendered_content = fs::read_to_string(rendered_theme_path)?; + let expected_content = fs::read_to_string("./tests/fixtures/rendered/list.md")?; + + // ------ + // Assert + // ------ + assert_eq!(rendered_content, expected_content); + assert!( + stderr.is_empty(), + "stderr does not contain the expected output" + ); + assert_eq!( + stdout, + format!( + "Successfully generated \"base16\" list with filename \"{}\"\n", + template_theme_path.join("list.md").display() + ) + ); + + Ok(()) +} + /// Tests error message when invalid scheme system is provided in config.yaml #[test] fn test_operation_build_invalid_system() -> Result<()> { diff --git a/tinted-builder-rust/tests/test_utils.rs b/tinted-builder-rust/tests/test_utils.rs index 42bf088..1aca7ab 100644 --- a/tinted-builder-rust/tests/test_utils.rs +++ b/tinted-builder-rust/tests/test_utils.rs @@ -1,10 +1,6 @@ +use std::fs::{self, remove_file, File}; use std::io::Write; -use std::{ - error::Error, - fs::{remove_file, File}, - path::Path, - process::Command, -}; +use std::{error::Error, path::Path, process::Command}; use anyhow::{Context, Result}; @@ -44,3 +40,21 @@ pub fn write_to_file(path: impl AsRef, contents: &str) -> Result<()> { Ok(()) } + +#[allow(dead_code)] +pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> Result<()> { + fs::create_dir_all(&dst)?; + + for entry in fs::read_dir(src)? { + let entry = entry?; + let file_type = entry.file_type()?; + let dest_path = dst.as_ref().join(entry.file_name()); + + if file_type.is_dir() { + copy_dir_all(entry.path(), &dest_path)?; + } else { + fs::copy(entry.path(), &dest_path)?; + } + } + Ok(()) +} diff --git a/tinted-builder-rust/tests/utils.rs b/tinted-builder-rust/tests/utils.rs index f081bab..43ba412 100644 --- a/tinted-builder-rust/tests/utils.rs +++ b/tinted-builder-rust/tests/utils.rs @@ -2,8 +2,8 @@ mod test_utils; use anyhow::Result; use std::fs; -use std::path::{Path, PathBuf}; -use test_utils::write_to_file; +use std::path::PathBuf; +use test_utils::{copy_dir_all, write_to_file}; use tinted_builder::{SchemeSystem, SchemeVariant}; use tinted_builder_rust::utils::get_scheme_files; @@ -159,20 +159,3 @@ palette: Ok(()) } - -fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> Result<()> { - fs::create_dir_all(&dst)?; - - for entry in fs::read_dir(src)? { - let entry = entry?; - let file_type = entry.file_type()?; - let dest_path = dst.as_ref().join(entry.file_name()); - - if file_type.is_dir() { - copy_dir_all(entry.path(), &dest_path)?; - } else { - fs::copy(entry.path(), &dest_path)?; - } - } - Ok(()) -} diff --git a/tinted-builder/src/scheme.rs b/tinted-builder/src/scheme.rs index beccb47..30444fe 100644 --- a/tinted-builder/src/scheme.rs +++ b/tinted-builder/src/scheme.rs @@ -79,6 +79,7 @@ pub enum SchemeSystem { Base16, /// Base24 scheme system. Base24, + List, } impl SchemeSystem { @@ -87,6 +88,7 @@ impl SchemeSystem { match self { SchemeSystem::Base16 => "base16", SchemeSystem::Base24 => "base24", + SchemeSystem::List => "list", } } pub fn variants() -> &'static [SchemeSystem] { diff --git a/tinted-builder/src/scheme/base16.rs b/tinted-builder/src/scheme/base16.rs index dbc83c5..bf1fd29 100644 --- a/tinted-builder/src/scheme/base16.rs +++ b/tinted-builder/src/scheme/base16.rs @@ -102,6 +102,12 @@ impl<'de> Deserialize<'de> for Base16Scheme { ))); } } + SchemeSystem::List => { + return Err(serde::de::Error::custom(format!( + "{} is not a valid Scheme system for a specific scheme", + wrapper.system + ))); + } } let palette_result: Result, _> = wrapper