Skip to content

Commit

Permalink
feat: add custom hex formatting
Browse files Browse the repository at this point in the history
adds a new `hex_format` string option to the template frontmatter.

this string, if set, is used to render all hex-format colours in the
template with tera.

the following context variables are available:

- r: red
- g: green
- b: blue
- a: alpha
- z: alpha, or blank if alpha == 0xFF

these are all lowercase 2-digit hex values, for example '7e'.

all five of these have uppercase counterparts, for example `G`, which
are uppercase 2-digit hex values, for example '7E'.

the default format is `{{r}}{{g}}{{b}}{{z}}`.

Co-authored-by: Ruby <rubyowo284@pm.me>
  • Loading branch information
backwardspy and rubyowo committed Jun 12, 2024
1 parent 7f6c57b commit f12edc6
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 192 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ pkg-url = "{ repo }/releases/download/v{ version }/whiskers-{ target }{ archive-
pkg-fmt = "bin"

[lints.clippy]
all = "warn"
pedantic = "warn"
nursery = "warn"
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
unwrap_used = "warn"
missing_errors_doc = "allow"
implicit_hasher = "allow"
Expand Down
172 changes: 99 additions & 73 deletions README.md

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions src/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn mix(
.as_f64()
.ok_or_else(|| tera::Error::msg("blend amount must be a number"))?;

let result = Color::mix(&base, &blend, amount);
let result = Color::mix(&base, &blend, amount)?;

Ok(tera::to_value(result)?)
}
Expand All @@ -35,16 +35,16 @@ pub fn modify(
let color: Color = tera::from_value(value.clone())?;
if let Some(hue) = args.get("hue") {
let hue = tera::from_value(hue.clone())?;
Ok(tera::to_value(color.mod_hue(hue))?)
Ok(tera::to_value(color.mod_hue(hue)?)?)
} else if let Some(saturation) = args.get("saturation") {
let saturation = tera::from_value(saturation.clone())?;
Ok(tera::to_value(color.mod_saturation(saturation))?)
Ok(tera::to_value(color.mod_saturation(saturation)?)?)
} else if let Some(lightness) = args.get("lightness") {
let lightness = tera::from_value(lightness.clone())?;
Ok(tera::to_value(color.mod_lightness(lightness))?)
Ok(tera::to_value(color.mod_lightness(lightness)?)?)
} else if let Some(opacity) = args.get("opacity") {
let opacity = tera::from_value(opacity.clone())?;
Ok(tera::to_value(color.mod_opacity(opacity))?)
Ok(tera::to_value(color.mod_opacity(opacity)?)?)
} else {
Ok(value.clone())
}
Expand All @@ -57,16 +57,16 @@ pub fn add(
let color: Color = tera::from_value(value.clone())?;
if let Some(hue) = args.get("hue") {
let hue = tera::from_value(hue.clone())?;
Ok(tera::to_value(color.add_hue(hue))?)
Ok(tera::to_value(color.add_hue(hue)?)?)
} else if let Some(saturation) = args.get("saturation") {
let saturation = tera::from_value(saturation.clone())?;
Ok(tera::to_value(color.add_saturation(saturation))?)
Ok(tera::to_value(color.add_saturation(saturation)?)?)
} else if let Some(lightness) = args.get("lightness") {
let lightness = tera::from_value(lightness.clone())?;
Ok(tera::to_value(color.add_lightness(lightness))?)
Ok(tera::to_value(color.add_lightness(lightness)?)?)
} else if let Some(opacity) = args.get("opacity") {
let opacity = tera::from_value(opacity.clone())?;
Ok(tera::to_value(color.add_opacity(opacity))?)
Ok(tera::to_value(color.add_opacity(opacity)?)?)
} else {
Ok(value.clone())
}
Expand All @@ -79,16 +79,16 @@ pub fn sub(
let color: Color = tera::from_value(value.clone())?;
if let Some(hue) = args.get("hue") {
let hue = tera::from_value(hue.clone())?;
Ok(tera::to_value(color.sub_hue(hue))?)
Ok(tera::to_value(color.sub_hue(hue)?)?)
} else if let Some(saturation) = args.get("saturation") {
let saturation = tera::from_value(saturation.clone())?;
Ok(tera::to_value(color.sub_saturation(saturation))?)
Ok(tera::to_value(color.sub_saturation(saturation)?)?)
} else if let Some(lightness) = args.get("lightness") {
let lightness = tera::from_value(lightness.clone())?;
Ok(tera::to_value(color.sub_lightness(lightness))?)
Ok(tera::to_value(color.sub_lightness(lightness)?)?)
} else if let Some(opacity) = args.get("opacity") {
let opacity = tera::from_value(opacity.clone())?;
Ok(tera::to_value(color.sub_opacity(opacity))?)
Ok(tera::to_value(color.sub_opacity(opacity)?)?)
} else {
Ok(value.clone())
}
Expand Down
57 changes: 44 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@ use whiskers::{
context::merge_values,
frontmatter, markdown,
matrix::{self, Matrix},
models, templating,
models::{self, HEX_FORMAT},
templating,
};

const FRONTMATTER_OPTIONS_SECTION: &str = "whiskers";

fn default_hex_format() -> String {
"{{r}}{{g}}{{b}}{{z}}".to_string()
}

#[derive(Default, Debug, serde::Deserialize)]
struct TemplateOptions {
version: Option<semver::VersionReq>,
matrix: Option<Matrix>,
filename: Option<String>,
hex_prefix: Option<String>,
#[serde(default)]
capitalize_hex: bool,
hex_format: String,
}

impl TemplateOptions {
Expand All @@ -42,6 +45,7 @@ impl TemplateOptions {
version: Option<semver::VersionReq>,
matrix: Option<Vec<tera::Value>>,
filename: Option<String>,
hex_format: Option<String>,
hex_prefix: Option<String>,
#[serde(default)]
capitalize_hex: bool,
Expand All @@ -50,20 +54,47 @@ impl TemplateOptions {
if let Some(opts) = frontmatter.get(FRONTMATTER_OPTIONS_SECTION) {
let opts: RawTemplateOptions = tera::from_value(opts.clone())
.context("Frontmatter `whiskers` section is invalid")?;

let matrix = opts
.matrix
.map(|m| matrix::from_values(m, only_flavor))
.transpose()
.context("Frontmatter matrix is invalid")?;

// if there's no hex_format but there is hex_prefix and/or capitalize_hex,
// we can construct a hex_format from those.
let hex_format = if let Some(hex_format) = opts.hex_format {
hex_format
} else {
// throw a deprecation warning for hex_prefix and capitalize_hex
if opts.hex_prefix.is_some() {
eprintln!("Warning: `hex_prefix` is deprecated and will be removed in a future version. Use `hex_format` instead.");
}

if opts.capitalize_hex {
eprintln!("Warning: `capitalize_hex` is deprecated and will be removed in a future version. Use `hex_format` instead.");
}

let prefix = opts.hex_prefix.unwrap_or_default();
let components = default_hex_format();
if opts.capitalize_hex {
format!("{prefix}{}", components.to_uppercase())
} else {
format!("{prefix}{components}")
}
};

Ok(Self {
version: opts.version,
matrix,
filename: opts.filename,
hex_prefix: opts.hex_prefix,
capitalize_hex: opts.capitalize_hex,
hex_format,
})
} else {
Ok(Self::default())
Ok(Self {
hex_format: default_hex_format(),
..Default::default()
})
}
}
}
Expand Down Expand Up @@ -126,13 +157,13 @@ fn main() -> anyhow::Result<()> {
ctx.insert(key, &value);
}

HEX_FORMAT
.set(template_opts.hex_format)
.expect("can always set HEX_FORMAT");

// build the palette and add it to the templating context
let palette = models::build_palette(
template_opts.capitalize_hex,
template_opts.hex_prefix.as_deref(),
args.color_overrides.as_ref(),
)
.context("Palette context cannot be built")?;
let palette = models::build_palette(args.color_overrides.as_ref())
.context("Palette context cannot be built")?;

ctx.insert("flavors", &palette.flavors);
if let Some(flavor) = args.flavor {
Expand Down
Loading

0 comments on commit f12edc6

Please sign in to comment.