Skip to content

Commit 0c2304c

Browse files
committed
Add list scheme system
1 parent 5cc49d0 commit 0c2304c

File tree

12 files changed

+217
-67
lines changed

12 files changed

+217
-67
lines changed

Cargo.lock

+3-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tinted-builder-rust/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ anyhow = "1.0.80"
1717
clap = "4.5.2"
1818
dirs = "5.0.1"
1919
regex = "1.11.0"
20+
ribboncurls = "0.4.1"
2021
serde = { version = "1.0.197", features = ["derive"] }
2122
serde_yaml = "0.9.32"
2223

tinted-builder-rust/src/operations/build.rs

+118-32
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use anyhow::{anyhow, Context, Result};
44
use std::collections::HashMap;
55
use std::fs::{self, create_dir_all, read_to_string};
66
use std::path::{Path, PathBuf};
7-
use tinted_builder::{Scheme, SchemeSystem, Template};
7+
use tinted_builder::{Base16Scheme, Scheme, SchemeSystem, Template};
88
use utils::{get_scheme_files, parse_filename, ParsedFilename, SchemeFile, TemplateConfig};
99

1010
use crate::helpers::write_to_file;
@@ -82,7 +82,7 @@ pub fn build(
8282
));
8383
}
8484

85-
let template_config_content = read_to_string(template_config_path)?;
85+
let template_config_content = read_to_string(&template_config_path)?;
8686
let template_config: HashMap<String, TemplateConfig> =
8787
serde_yaml::from_str(&template_config_content)?;
8888

@@ -105,42 +105,118 @@ pub fn build(
105105

106106
// For each template definition in the templates/config.yaml file
107107
for (template_item_config_name, template_item_config_value) in template_config.iter() {
108-
let template_item_scheme_files: Vec<(PathBuf, Scheme)> = all_scheme_files
109-
.iter()
110-
.filter_map(|(path, scheme)| {
111-
if template_item_config_value
112-
.supported_systems
113-
.clone()
114-
.unwrap_or(vec![SchemeSystem::default()])
115-
.contains(&scheme.get_scheme_system())
116-
{
117-
Some((path.clone(), scheme.clone()))
118-
} else {
119-
None
120-
}
121-
})
122-
.collect();
123-
124-
generate_themes_for_config(
125-
template_item_config_name,
126-
template_item_config_value,
127-
&theme_template_path,
128-
&template_item_scheme_files,
129-
is_quiet,
130-
)?;
108+
let supported_systems = template_item_config_value
109+
.supported_systems
110+
.clone()
111+
.unwrap_or(vec![SchemeSystem::default()]);
112+
113+
if supported_systems.contains(&SchemeSystem::List) {
114+
render_list(
115+
&theme_template_path,
116+
(template_item_config_name, template_item_config_value),
117+
all_scheme_files.clone(),
118+
is_quiet,
119+
)?;
120+
} else {
121+
let template_item_scheme_files: Vec<(PathBuf, Scheme)> = all_scheme_files
122+
.iter()
123+
.filter_map(|(path, scheme)| {
124+
if supported_systems.contains(&scheme.get_scheme_system()) {
125+
Some((path.clone(), scheme.clone()))
126+
} else {
127+
None
128+
}
129+
})
130+
.collect();
131+
132+
generate_themes_for_config(
133+
template_item_config_name,
134+
template_item_config_value,
135+
&theme_template_path,
136+
&template_item_scheme_files,
137+
is_quiet,
138+
)?;
139+
}
131140
}
132141

133142
Ok(())
134143
}
135144

136-
fn generate_themes_for_config(
137-
config_name: &str,
138-
config_value: &TemplateConfig,
139-
theme_template_path: impl AsRef<Path>,
140-
scheme_files: &Vec<(PathBuf, Scheme)>,
145+
fn render_list(
146+
template_path: impl AsRef<Path>,
147+
(config_name, config_value): (&str, &TemplateConfig),
148+
all_scheme_files: Vec<(PathBuf, Scheme)>,
141149
is_quiet: bool,
142150
) -> Result<()> {
143-
let filename = match (
151+
let supported_systems = config_value
152+
.supported_systems
153+
.clone()
154+
.unwrap_or(vec![SchemeSystem::default()]);
155+
let filename = get_filename(config_value, is_quiet)?;
156+
let mustache_template_path = template_path
157+
.as_ref()
158+
.join(format!("templates/{}.mustache", config_name));
159+
let template_content = read_to_string(&mustache_template_path).context(format!(
160+
"Mustache template missing: {}",
161+
mustache_template_path.display()
162+
))?;
163+
let mut data: HashMap<&str, Vec<Base16Scheme>> = HashMap::new();
164+
data.insert(
165+
"schemes",
166+
all_scheme_files
167+
.clone()
168+
.into_iter()
169+
.filter_map(|(_, scheme)| match scheme {
170+
Scheme::Base16(scheme) => {
171+
if supported_systems.contains(&SchemeSystem::Base16) {
172+
Some(scheme)
173+
} else {
174+
None
175+
}
176+
}
177+
Scheme::Base24(scheme) => {
178+
if supported_systems.contains(&SchemeSystem::Base24) {
179+
Some(scheme)
180+
} else {
181+
None
182+
}
183+
}
184+
_ => None,
185+
})
186+
.collect::<Vec<Base16Scheme>>(),
187+
);
188+
let data = serde_yaml::to_string(&data).unwrap_or_default();
189+
let output = ribboncurls::render(&template_content, &data, None)?;
190+
let parsed_filename = parse_filename(&template_path, &filename)?;
191+
let output_path = parsed_filename.get_path();
192+
193+
if !parsed_filename.directory.exists() {
194+
create_dir_all(&parsed_filename.directory)?
195+
}
196+
197+
write_to_file(&output_path, &output)?;
198+
199+
if !is_quiet {
200+
println!(
201+
"Successfully generated \"{}\" list with filename \"{}\"",
202+
supported_systems
203+
.iter()
204+
.filter_map(|item| if *item == SchemeSystem::List {
205+
None
206+
} else {
207+
Some(item.as_str().to_string())
208+
})
209+
.collect::<Vec<String>>()
210+
.join(", "),
211+
template_path.as_ref().join(filename).display(),
212+
);
213+
}
214+
215+
Ok(())
216+
}
217+
218+
fn get_filename(config_value: &TemplateConfig, is_quiet: bool) -> Result<String> {
219+
match (
144220
&config_value.filename,
145221
#[allow(deprecated)]
146222
&config_value.extension,
@@ -182,7 +258,17 @@ fn generate_themes_for_config(
182258
_ => Err(anyhow!(
183259
"Config file is missing \"filepath\" or \"extension\" and \"output\" properties"
184260
)),
185-
}?;
261+
}
262+
}
263+
264+
fn generate_themes_for_config(
265+
config_name: &str,
266+
config_value: &TemplateConfig,
267+
theme_template_path: impl AsRef<Path>,
268+
scheme_files: &Vec<(PathBuf, Scheme)>,
269+
is_quiet: bool,
270+
) -> Result<()> {
271+
let filename = get_filename(config_value, is_quiet)?;
186272
let mustache_template_path = theme_template_path
187273
.as_ref()
188274
.join(format!("templates/{}.mustache", config_name));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
base16-silk-light - variant: light
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
list:
2+
filename: list.md
3+
supported-systems: [list, base16]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{#schemes}}
2+
{{system}}-{{slug}} - variant: {{variant}}
3+
{{/schemes}}

tinted-builder-rust/tests/operation_build.rs

+57-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod test_utils;
33
use anyhow::{Context, Result};
44
use std::fs;
55
use std::path::PathBuf;
6-
use test_utils::{run_command, write_to_file};
6+
use test_utils::{copy_dir_all, run_command, write_to_file};
77

88
fn setup(system: &str, scheme_name: &str) -> Result<(String, String, String, String)> {
99
let config_file_path: PathBuf =
@@ -275,9 +275,6 @@ fn test_operation_build_base24() -> Result<()> {
275275
base24_template_rendered_content_fixture,
276276
) = setup(system, scheme_name)?;
277277

278-
if themes_path.is_dir() {
279-
fs::remove_dir_all(&themes_path)?;
280-
}
281278
if template_theme_path.is_dir() {
282279
fs::remove_dir_all(&template_theme_path)?;
283280
}
@@ -355,9 +352,6 @@ fn test_operation_build_mixed() -> Result<()> {
355352
base24_template_rendered_content_fixture,
356353
) = setup("base24", base24_scheme_name)?;
357354

358-
if themes_path.is_dir() {
359-
fs::remove_dir_all(&themes_path)?;
360-
}
361355
if template_theme_path.is_dir() {
362356
fs::remove_dir_all(&template_theme_path)?;
363357
}
@@ -419,6 +413,62 @@ fn test_operation_build_mixed() -> Result<()> {
419413
Ok(())
420414
}
421415

416+
#[test]
417+
fn test_operation_build_list() -> Result<()> {
418+
// -------
419+
// Arrange
420+
// -------
421+
let name = "operation_build_list";
422+
let template_theme_path = PathBuf::from(format!("./template-{}", name));
423+
let template_templates_path = template_theme_path.join("templates");
424+
let schemes_path = template_theme_path.join("schemes");
425+
let rendered_theme_path = template_theme_path.join("list.md");
426+
427+
if template_theme_path.is_dir() {
428+
fs::remove_dir_all(&template_theme_path)?;
429+
}
430+
fs::create_dir_all(&template_templates_path)?;
431+
fs::copy(
432+
"./tests/fixtures/templates/list-config.yaml",
433+
template_theme_path.join("templates/config.yaml"),
434+
)?;
435+
fs::copy(
436+
"./tests/fixtures/templates/list-template.mustache",
437+
template_theme_path.join("templates/list.mustache"),
438+
)?;
439+
copy_dir_all("./tests/fixtures/schemes", &schemes_path)?;
440+
441+
// ---
442+
// Act
443+
// ---
444+
let (stdout, stderr) = run_command(vec![
445+
"build".to_string(),
446+
template_theme_path.display().to_string(),
447+
format!("--schemes-dir={}", schemes_path.display()),
448+
])
449+
.unwrap();
450+
let rendered_content = fs::read_to_string(rendered_theme_path)?;
451+
let expected_content = fs::read_to_string("./tests/fixtures/rendered/list.md")?;
452+
453+
// ------
454+
// Assert
455+
// ------
456+
assert_eq!(rendered_content, expected_content);
457+
assert!(
458+
stderr.is_empty(),
459+
"stderr does not contain the expected output"
460+
);
461+
assert_eq!(
462+
stdout,
463+
format!(
464+
"Successfully generated \"base16\" list with filename \"{}\"\n",
465+
template_theme_path.join("list.md").display()
466+
)
467+
);
468+
469+
Ok(())
470+
}
471+
422472
/// Tests error message when invalid scheme system is provided in config.yaml
423473
#[test]
424474
fn test_operation_build_invalid_system() -> Result<()> {

tinted-builder-rust/tests/test_utils.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1+
use std::fs::{self, remove_file, File};
12
use std::io::Write;
2-
use std::{
3-
error::Error,
4-
fs::{remove_file, File},
5-
path::Path,
6-
process::Command,
7-
};
3+
use std::{error::Error, path::Path, process::Command};
84

95
use anyhow::{Context, Result};
106

@@ -44,3 +40,21 @@ pub fn write_to_file(path: impl AsRef<Path>, contents: &str) -> Result<()> {
4440

4541
Ok(())
4642
}
43+
44+
#[allow(dead_code)]
45+
pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
46+
fs::create_dir_all(&dst)?;
47+
48+
for entry in fs::read_dir(src)? {
49+
let entry = entry?;
50+
let file_type = entry.file_type()?;
51+
let dest_path = dst.as_ref().join(entry.file_name());
52+
53+
if file_type.is_dir() {
54+
copy_dir_all(entry.path(), &dest_path)?;
55+
} else {
56+
fs::copy(entry.path(), &dest_path)?;
57+
}
58+
}
59+
Ok(())
60+
}

tinted-builder-rust/tests/utils.rs

+2-19
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ mod test_utils;
22

33
use anyhow::Result;
44
use std::fs;
5-
use std::path::{Path, PathBuf};
6-
use test_utils::write_to_file;
5+
use std::path::PathBuf;
6+
use test_utils::{copy_dir_all, write_to_file};
77
use tinted_builder::{SchemeSystem, SchemeVariant};
88
use tinted_builder_rust::utils::get_scheme_files;
99

@@ -159,20 +159,3 @@ palette:
159159

160160
Ok(())
161161
}
162-
163-
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
164-
fs::create_dir_all(&dst)?;
165-
166-
for entry in fs::read_dir(src)? {
167-
let entry = entry?;
168-
let file_type = entry.file_type()?;
169-
let dest_path = dst.as_ref().join(entry.file_name());
170-
171-
if file_type.is_dir() {
172-
copy_dir_all(entry.path(), &dest_path)?;
173-
} else {
174-
fs::copy(entry.path(), &dest_path)?;
175-
}
176-
}
177-
Ok(())
178-
}

tinted-builder/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ clap = "4.5.2"
1717
dirs = "5.0.1"
1818
quick-xml = { version = "0.36.1", features = ["serialize"] }
1919
regex = "1.10.4"
20-
ribboncurls = "0.2.1"
20+
ribboncurls = "0.4.1"
2121
serde = { version = "1.0.197", features = ["derive"] }
2222
serde_yaml = "0.9.32"
2323
thiserror = "1.0.63"

0 commit comments

Comments
 (0)