Skip to content

Commit dfc700e

Browse files
backwardspyrubyowo
andauthored
feat: add custom hex formatting (#18)
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 <[email protected]>
1 parent eb1958e commit dfc700e

File tree

10 files changed

+299
-192
lines changed

10 files changed

+299
-192
lines changed

Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ pkg-url = "{ repo }/releases/download/v{ version }/whiskers-{ target }{ archive-
2727
pkg-fmt = "bin"
2828

2929
[lints.clippy]
30-
all = "warn"
31-
pedantic = "warn"
32-
nursery = "warn"
30+
all = { level = "warn", priority = -1 }
31+
pedantic = { level = "warn", priority = -1 }
32+
nursery = { level = "warn", priority = -1 }
3333
unwrap_used = "warn"
3434
missing_errors_doc = "allow"
3535
implicit_hasher = "allow"

README.md

+99-73
Large diffs are not rendered by default.

src/filters.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn mix(
2323
.as_f64()
2424
.ok_or_else(|| tera::Error::msg("blend amount must be a number"))?;
2525

26-
let result = Color::mix(&base, &blend, amount);
26+
let result = Color::mix(&base, &blend, amount)?;
2727

2828
Ok(tera::to_value(result)?)
2929
}
@@ -35,16 +35,16 @@ pub fn modify(
3535
let color: Color = tera::from_value(value.clone())?;
3636
if let Some(hue) = args.get("hue") {
3737
let hue = tera::from_value(hue.clone())?;
38-
Ok(tera::to_value(color.mod_hue(hue))?)
38+
Ok(tera::to_value(color.mod_hue(hue)?)?)
3939
} else if let Some(saturation) = args.get("saturation") {
4040
let saturation = tera::from_value(saturation.clone())?;
41-
Ok(tera::to_value(color.mod_saturation(saturation))?)
41+
Ok(tera::to_value(color.mod_saturation(saturation)?)?)
4242
} else if let Some(lightness) = args.get("lightness") {
4343
let lightness = tera::from_value(lightness.clone())?;
44-
Ok(tera::to_value(color.mod_lightness(lightness))?)
44+
Ok(tera::to_value(color.mod_lightness(lightness)?)?)
4545
} else if let Some(opacity) = args.get("opacity") {
4646
let opacity = tera::from_value(opacity.clone())?;
47-
Ok(tera::to_value(color.mod_opacity(opacity))?)
47+
Ok(tera::to_value(color.mod_opacity(opacity)?)?)
4848
} else {
4949
Ok(value.clone())
5050
}
@@ -57,16 +57,16 @@ pub fn add(
5757
let color: Color = tera::from_value(value.clone())?;
5858
if let Some(hue) = args.get("hue") {
5959
let hue = tera::from_value(hue.clone())?;
60-
Ok(tera::to_value(color.add_hue(hue))?)
60+
Ok(tera::to_value(color.add_hue(hue)?)?)
6161
} else if let Some(saturation) = args.get("saturation") {
6262
let saturation = tera::from_value(saturation.clone())?;
63-
Ok(tera::to_value(color.add_saturation(saturation))?)
63+
Ok(tera::to_value(color.add_saturation(saturation)?)?)
6464
} else if let Some(lightness) = args.get("lightness") {
6565
let lightness = tera::from_value(lightness.clone())?;
66-
Ok(tera::to_value(color.add_lightness(lightness))?)
66+
Ok(tera::to_value(color.add_lightness(lightness)?)?)
6767
} else if let Some(opacity) = args.get("opacity") {
6868
let opacity = tera::from_value(opacity.clone())?;
69-
Ok(tera::to_value(color.add_opacity(opacity))?)
69+
Ok(tera::to_value(color.add_opacity(opacity)?)?)
7070
} else {
7171
Ok(value.clone())
7272
}
@@ -79,16 +79,16 @@ pub fn sub(
7979
let color: Color = tera::from_value(value.clone())?;
8080
if let Some(hue) = args.get("hue") {
8181
let hue = tera::from_value(hue.clone())?;
82-
Ok(tera::to_value(color.sub_hue(hue))?)
82+
Ok(tera::to_value(color.sub_hue(hue)?)?)
8383
} else if let Some(saturation) = args.get("saturation") {
8484
let saturation = tera::from_value(saturation.clone())?;
85-
Ok(tera::to_value(color.sub_saturation(saturation))?)
85+
Ok(tera::to_value(color.sub_saturation(saturation)?)?)
8686
} else if let Some(lightness) = args.get("lightness") {
8787
let lightness = tera::from_value(lightness.clone())?;
88-
Ok(tera::to_value(color.sub_lightness(lightness))?)
88+
Ok(tera::to_value(color.sub_lightness(lightness)?)?)
8989
} else if let Some(opacity) = args.get("opacity") {
9090
let opacity = tera::from_value(opacity.clone())?;
91-
Ok(tera::to_value(color.sub_opacity(opacity))?)
91+
Ok(tera::to_value(color.sub_opacity(opacity)?)?)
9292
} else {
9393
Ok(value.clone())
9494
}

src/main.rs

+44-13
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@ use whiskers::{
1616
context::merge_values,
1717
frontmatter, markdown,
1818
matrix::{self, Matrix},
19-
models, templating,
19+
models::{self, HEX_FORMAT},
20+
templating,
2021
};
2122

2223
const FRONTMATTER_OPTIONS_SECTION: &str = "whiskers";
2324

25+
fn default_hex_format() -> String {
26+
"{{r}}{{g}}{{b}}{{z}}".to_string()
27+
}
28+
2429
#[derive(Default, Debug, serde::Deserialize)]
2530
struct TemplateOptions {
2631
version: Option<semver::VersionReq>,
2732
matrix: Option<Matrix>,
2833
filename: Option<String>,
29-
hex_prefix: Option<String>,
30-
#[serde(default)]
31-
capitalize_hex: bool,
34+
hex_format: String,
3235
}
3336

3437
impl TemplateOptions {
@@ -42,6 +45,7 @@ impl TemplateOptions {
4245
version: Option<semver::VersionReq>,
4346
matrix: Option<Vec<tera::Value>>,
4447
filename: Option<String>,
48+
hex_format: Option<String>,
4549
hex_prefix: Option<String>,
4650
#[serde(default)]
4751
capitalize_hex: bool,
@@ -50,20 +54,47 @@ impl TemplateOptions {
5054
if let Some(opts) = frontmatter.get(FRONTMATTER_OPTIONS_SECTION) {
5155
let opts: RawTemplateOptions = tera::from_value(opts.clone())
5256
.context("Frontmatter `whiskers` section is invalid")?;
57+
5358
let matrix = opts
5459
.matrix
5560
.map(|m| matrix::from_values(m, only_flavor))
5661
.transpose()
5762
.context("Frontmatter matrix is invalid")?;
63+
64+
// if there's no hex_format but there is hex_prefix and/or capitalize_hex,
65+
// we can construct a hex_format from those.
66+
let hex_format = if let Some(hex_format) = opts.hex_format {
67+
hex_format
68+
} else {
69+
// throw a deprecation warning for hex_prefix and capitalize_hex
70+
if opts.hex_prefix.is_some() {
71+
eprintln!("Warning: `hex_prefix` is deprecated and will be removed in a future version. Use `hex_format` instead.");
72+
}
73+
74+
if opts.capitalize_hex {
75+
eprintln!("Warning: `capitalize_hex` is deprecated and will be removed in a future version. Use `hex_format` instead.");
76+
}
77+
78+
let prefix = opts.hex_prefix.unwrap_or_default();
79+
let components = default_hex_format();
80+
if opts.capitalize_hex {
81+
format!("{prefix}{}", components.to_uppercase())
82+
} else {
83+
format!("{prefix}{components}")
84+
}
85+
};
86+
5887
Ok(Self {
5988
version: opts.version,
6089
matrix,
6190
filename: opts.filename,
62-
hex_prefix: opts.hex_prefix,
63-
capitalize_hex: opts.capitalize_hex,
91+
hex_format,
6492
})
6593
} else {
66-
Ok(Self::default())
94+
Ok(Self {
95+
hex_format: default_hex_format(),
96+
..Default::default()
97+
})
6798
}
6899
}
69100
}
@@ -126,13 +157,13 @@ fn main() -> anyhow::Result<()> {
126157
ctx.insert(key, &value);
127158
}
128159

160+
HEX_FORMAT
161+
.set(template_opts.hex_format)
162+
.expect("can always set HEX_FORMAT");
163+
129164
// build the palette and add it to the templating context
130-
let palette = models::build_palette(
131-
template_opts.capitalize_hex,
132-
template_opts.hex_prefix.as_deref(),
133-
args.color_overrides.as_ref(),
134-
)
135-
.context("Palette context cannot be built")?;
165+
let palette = models::build_palette(args.color_overrides.as_ref())
166+
.context("Palette context cannot be built")?;
136167

137168
ctx.insert("flavors", &palette.flavors);
138169
if let Some(flavor) = args.flavor {

0 commit comments

Comments
 (0)