Skip to content

Commit

Permalink
feat(whiskers): tidy up output formats, general clean up (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
backwardspy authored and sgoudham committed Jun 3, 2024
1 parent 4844edf commit 9ad3f14
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 201 deletions.
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,30 +167,30 @@ These types are designed to closely match the [palette.json](https://github.com/

### Functions

| Name | Description | Examples |
|------|-------------|----------|
| `if` | Return one value if a condition is true, and another if it's false | `if(cond=true, t=1, f=0)` => `1` |
| `object` | Create an object from the input | `object(a=1, b=2)` => `{a: 1, b: 2}` |
| `css_rgb` | Convert a color to an RGB CSS string | `css_rgb(color=red)` => `rgb(210, 15, 57)` |
| `css_rgba` | Convert a color to an RGBA CSS string | `css_rgba(color=red)` => `rgba(210, 15, 57, 1.00)` |
| `css_hsl` | Convert a color to an HSL CSS string | `css_hsl(color=red)` => `hsl(347, 87%, 44%)` |
| `css_hsla` | Convert a color to an HSLA CSS string | `css_hsla(color=red)` => `hsla(347, 87%, 44%, 1.00)` |
| `read_file` | Read and include the contents of a file, path is relative to the template file | `read_file(path="abc.txt")` => `abc` |
| Name | Description | Examples |
| ----------- | ------------------------------------------------------------------------------ | ----------------------------------------------------- |
| `if` | Return one value if a condition is true, and another if it's false | `if(cond=true, t=1, f=0)` `1` |
| `object` | Create an object from the input | `object(a=1, b=2)` `{a: 1, b: 2}` |
| `css_rgb` | Convert a color to an RGB CSS string | `css_rgb(color=red)` `rgb(210, 15, 57)` |
| `css_rgba` | Convert a color to an RGBA CSS string | `css_rgba(color=red)` `rgba(210, 15, 57, 1.00)` |
| `css_hsl` | Convert a color to an HSL CSS string | `css_hsl(color=red)` `hsl(347, 87%, 44%)` |
| `css_hsla` | Convert a color to an HSLA CSS string | `css_hsla(color=red)` `hsla(347, 87%, 44%, 1.00)` |
| `read_file` | Read and include the contents of a file, path is relative to the template file | `read_file(path="abc.txt")` `abc` |

### Filters

| Name | Description | Examples |
|------|-------------|----------|
| `add` | Add a value to a color | `red \| add(hue=30)` => `#ff6666` |
| `sub` | Subtract a value from a color | `red \| sub(hue=30)` => `#d30f9b` |
| `mod` | Modify a color | `red \| mod(lightness=80)` => `#f8a0b3` |
| `mix` | Mix two colors together | `red \| mix(color=base, amount=0.5)` => `#e08097` |
| `urlencode_lzma` | Serialize an object into a URL-safe string with LZMA compression | `red \| urlencode_lzma` => `#ff6666` |
| `trunc` | Truncate a number to a certain number of places | `1.123456 \| trunc(places=3)` => `1.123` |
| `css_rgb` | Convert a color to an RGB CSS string | `red \| css_rgb` => `rgb(210, 15, 57)` |
| `css_rgba` | Convert a color to an RGBA CSS string | `red \| css_rgba` => `rgba(210, 15, 57, 1.00)` |
| `css_hsl` | Convert a color to an HSL CSS string | `red \| css_hsl` => `hsl(347, 87%, 44%)` |
| `css_hsla` | Convert a color to an HSLA CSS string | `red \| css_hsla` => `hsla(347, 87%, 44%, 1.00)` |
| Name | Description | Examples |
| ---------------- | ---------------------------------------------------------------- | -------------------------------------------------- |
| `add` | Add a value to a color | `red \| add(hue=30)` `#ff6666` |
| `sub` | Subtract a value from a color | `red \| sub(hue=30)` `#d30f9b` |
| `mod` | Modify a color | `red \| mod(lightness=80)` `#f8a0b3` |
| `mix` | Mix two colors together | `red \| mix(color=base, amount=0.5)` `#e08097` |
| `urlencode_lzma` | Serialize an object into a URL-safe string with LZMA compression | `red \| urlencode_lzma` `#ff6666` |
| `trunc` | Truncate a number to a certain number of places | `1.123456 \| trunc(places=3)` `1.123` |
| `css_rgb` | Convert a color to an RGB CSS string | `red \| css_rgb` `rgb(210, 15, 57)` |
| `css_rgba` | Convert a color to an RGBA CSS string | `red \| css_rgba` `rgba(210, 15, 57, 1.00)` |
| `css_hsl` | Convert a color to an HSL CSS string | `red \| css_hsl` `hsl(347, 87%, 44%)` |
| `css_hsla` | Convert a color to an HSLA CSS string | `red \| css_hsla` `hsla(347, 87%, 44%, 1.00)` |

> [!NOTE]
> You also have access to all of Tera's own built-in filters and functions.
Expand Down
5 changes: 4 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,11 @@ pub enum OutputFormat {
Json,
Yaml,
Markdown,
MarkdownTable,
Plain,

/// Deprecated, now equivalent to `Markdown`
#[clap(hide = true)]
MarkdownTable,
}

fn json_map<T>(s: &str) -> Result<T, Error>
Expand Down
172 changes: 113 additions & 59 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
env,
io::{Read, Write as _},
path::{Path, PathBuf},
process,
process::{self, exit},
};

use anyhow::{anyhow, Context as _};
Expand Down Expand Up @@ -71,21 +71,7 @@ impl TemplateOptions {
fn main() -> anyhow::Result<()> {
// parse command-line arguments & template frontmatter
let args = Args::parse();

if args.list_functions {
list_functions(args.output_format);
return Ok(());
}

if args.list_flavors {
list_flavors(args.output_format);
return Ok(());
}

if args.list_accents {
list_accents(args.output_format);
return Ok(());
}
handle_list_flags(&args);

let template = args
.template
Expand Down Expand Up @@ -201,6 +187,23 @@ fn main() -> anyhow::Result<()> {
Ok(())
}

fn handle_list_flags(args: &Args) {
if args.list_functions {
list_functions(args.output_format);
exit(0);
}

if args.list_flavors {
list_flavors(args.output_format);
exit(0);
}

if args.list_accents {
list_accents(args.output_format);
exit(0);
}
}

fn override_matrix(
matrix: &mut Matrix,
value: &tera::Value,
Expand Down Expand Up @@ -228,15 +231,16 @@ fn override_matrix(
Ok(())
}

#[allow(clippy::too_many_lines)]
fn list_functions(format: OutputFormat) {
let functions = templating::all_functions();
let filters = templating::all_filters();
println!(
"{}",
match format {
OutputFormat::Json | OutputFormat::Yaml => {
let output = serde_json::json!({
"functions": templating::all_functions(),
"filters": templating::all_filters()
"functions": functions,
"filters": filters,
});

if matches!(format, OutputFormat::Json) {
Expand All @@ -245,19 +249,20 @@ fn list_functions(format: OutputFormat) {
serde_yaml::to_string(&output).expect("output is guaranteed to be valid")
}
}
OutputFormat::Markdown => {
markdown::display_functions_as_list()
}
OutputFormat::MarkdownTable => {
markdown::display_functions_as_table()
OutputFormat::Markdown | OutputFormat::MarkdownTable => {
format!(
"{}\n\n{}",
markdown::display_as_table(&functions, "Functions"),
markdown::display_as_table(&filters, "Filters")
)
}
OutputFormat::Plain => {
let mut list = templating::all_filters()
let mut list = filters
.iter()
.map(|f| f.name.clone())
.collect::<Vec<String>>();

list.extend(templating::all_functions().iter().map(|f| f.name.clone()));
list.extend(functions.iter().map(|f| f.name.clone()));

list.join("\n")
}
Expand All @@ -266,71 +271,120 @@ fn list_functions(format: OutputFormat) {
}

fn list_flavors(format: OutputFormat) {
let format = match format {
OutputFormat::Markdown | OutputFormat::MarkdownTable => {
eprintln!("warning: Markdown output is not yet supported for listing flavors, reverting to `plain`");
OutputFormat::Plain
// we want all the flavor info minus the colors
#[derive(serde::Serialize)]
struct FlavorInfo {
identifier: String,
name: String,
emoji: char,
order: u32,
dark: bool,
}

impl markdown::TableDisplay for FlavorInfo {
fn table_headings() -> Box<[String]> {
vec![
"Identifier".to_string(),
"Name".to_string(),
"Dark".to_string(),
"Emoji".to_string(),
]
.into_boxed_slice()
}
other => other,
};

let output = catppuccin::PALETTE
fn table_row(&self) -> Box<[String]> {
vec![
self.identifier.clone(),
self.name.clone(),
self.dark.to_string(),
self.emoji.to_string(),
]
.into_boxed_slice()
}
}

let flavors = catppuccin::PALETTE
.all_flavors()
.map(catppuccin::Flavor::identifier);
.into_iter()
.map(|f| FlavorInfo {
identifier: f.identifier().to_string(),
name: f.name.to_string(),
emoji: f.emoji,
order: f.order,
dark: f.dark,
})
.collect::<Vec<_>>();

println!(
"{}",
match format {
// for structured data, we output the full flavor info objects
OutputFormat::Json | OutputFormat::Yaml => {
let output = serde_json::json!(output);

if matches!(format, OutputFormat::Json) {
serde_json::to_string_pretty(&output).expect("output is guaranteed to be valid")
serde_json::to_string_pretty(&flavors)
.expect("flavors are guaranteed to be valid json")
} else {
serde_yaml::to_string(&output).expect("output is guaranteed to be valid")
serde_yaml::to_string(&flavors)
.expect("flavors are guaranteed to be valid yaml")
}
}
// for plain output, we just list the flavor identifiers
OutputFormat::Plain => {
output.join("\n")
flavors.iter().map(|f| &f.identifier).join("\n")
}
// and finally for human-readable markdown, we list the flavor names
OutputFormat::Markdown | OutputFormat::MarkdownTable => {
markdown::display_as_table(&flavors, "Flavors")
}
_ => todo!(),
}
);
}

fn list_accents(format: OutputFormat) {
let format = match format {
OutputFormat::Markdown | OutputFormat::MarkdownTable => {
eprintln!("warning: Markdown output is not yet supported for listing accents, reverting to `plain`");
OutputFormat::Plain
}
other => other,
};

let output = catppuccin::PALETTE
let accents = catppuccin::PALETTE
.latte
.colors
.all_colors()
.into_iter()
.filter_map(|c| if c.accent { Some(c.identifier()) } else { None })
.filter(|c| c.accent)
.collect::<Vec<_>>();

println!(
"{}",
match format {
// for structured data, we can include both name and identifier of each color
OutputFormat::Json | OutputFormat::Yaml => {
let output = serde_json::json!(output);

let accents = accents
.into_iter()
.map(|c| {
serde_json::json!({
"name": c.name,
"identifier": c.identifier(),
})
})
.collect::<Vec<_>>();
if matches!(format, OutputFormat::Json) {
serde_json::to_string_pretty(&output).expect("output is guaranteed to be valid")
serde_json::to_string_pretty(&accents)
.expect("accents are guaranteed to be valid json")
} else {
serde_yaml::to_string(&output).expect("output is guaranteed to be valid")
serde_yaml::to_string(&accents)
.expect("accents are guaranteed to be valid yaml")
}
}
// for plain output, we just list the identifiers
OutputFormat::Plain => {
output.join("\n")
accents
.into_iter()
.map(catppuccin::Color::identifier)
.join("\n")
}
// and finally for human-readable markdown, we list the names
OutputFormat::Markdown | OutputFormat::MarkdownTable => {
markdown::display_as_list(
&accents.into_iter().map(|c| c.name).collect::<Vec<_>>(),
"Accents",
)
}
_ => todo!(),
}
);
}
Expand Down Expand Up @@ -380,7 +434,7 @@ fn template_is_compatible(template_opts: &TemplateOptions) -> bool {
true
}

fn write_template(dry_run: bool, filename: String, result: String) -> Result<(), anyhow::Error> {
fn write_template(dry_run: bool, filename: &str, result: String) -> Result<(), anyhow::Error> {
let filename = Path::new(&filename);

if dry_run || cfg!(test) {
Expand Down Expand Up @@ -413,7 +467,7 @@ fn render_single_output(
if let Some(path) = check {
check_result_with_file(&path, &result).context("Check mode failed")?;
} else if let Some(filename) = filename {
write_template(dry_run, filename, result)?;
write_template(dry_run, &filename, result)?;
} else {
print!("{result}");
}
Expand Down Expand Up @@ -464,7 +518,7 @@ fn render_multi_output(
if args.check.is_some() {
check_result_with_file(&filename, &result).context("Check mode failed")?;
} else {
write_template(args.dry_run, filename, result)?;
write_template(args.dry_run, &filename, result)?;
}
}

Expand Down
Loading

0 comments on commit 9ad3f14

Please sign in to comment.