diff --git a/Cargo.toml b/Cargo.toml index 2d49b59..43ef9d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,14 @@ name = "zephyr" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] + +inventory = ["dep:inventory"] [dependencies] -once_cell = "1.10.0" +inventory = { version = "0.3", optional = true } + +[[example]] +name = "inventory" +required-features = ["inventory"] diff --git a/examples/html.rs b/examples/html.rs index be266e2..b473076 100644 --- a/examples/html.rs +++ b/examples/html.rs @@ -1,6 +1,6 @@ fn main() { - // this list would ideally be generated on the fly out of the written html, - // but i don't want to unneeded dependencies to this crate + // this list of used classes would ideally be parsed out of the written html, + // but i don't want to over complicate this example let classes = [ "mt[10rem]", "color[#e20f00]", @@ -11,7 +11,7 @@ fn main() { ]; let z = zephyr::Zephyr::new(); - let css = z.generate_css(&classes); + let css = z.generate_classes(classes); let html = format!( r#" diff --git a/examples/inventory.rs b/examples/inventory.rs new file mode 100644 index 0000000..d54ef11 --- /dev/null +++ b/examples/inventory.rs @@ -0,0 +1,62 @@ +//! for situations where you have a set of components, +//! and you want to register the classes you use from different files without too much complexity + +use zephyr::{register_class, Zephyr}; + +fn main() { + let z = Zephyr::new(); + let generated_css = z.generate_from_inventory(); + + let head = head(&generated_css); + + let header = header(); + let body = body(); + + let html = format!( + r#" + + + {head} + + {header} + {body} + + +"# + ); + + std::fs::write("./examples/index.html", html).unwrap(); +} + +fn head(generated_css: &str) -> String { + format!( + r#" + + + + + "# + ) +} + +fn header() -> String { + let class = register_class!("color[#e20f00] color[green]hover content['*']$before"); + format!( + r#" +

+ this text is red, but green on hover +

+ "# + ) +} + +fn body() -> String { + let class = register_class!("mt[10rem] content[attr(after)]$after color[red]$after"); + format!( + r#" +

+ this text has a lot of margin +

+ "# + ) +} diff --git a/src/inventory.rs b/src/inventory.rs new file mode 100644 index 0000000..b3a8251 --- /dev/null +++ b/src/inventory.rs @@ -0,0 +1,36 @@ +pub use inventory::submit; + +use crate::Zephyr; + +pub struct AddClassToInventory { + class: &'static str, +} + +impl AddClassToInventory { + pub const fn new(class: &'static str) -> Self { + Self { class } + } +} + +inventory::collect!(AddClassToInventory); + +impl Zephyr { + pub fn generate_from_inventory(&self) -> String { + self.generate_classes( + inventory::iter:: + .into_iter() + .map(|a| a.class), + ) + } +} + +#[macro_export] +macro_rules! register_class { + ($c:literal) => {{ + const C: &'static str = $c; + $crate::inventory::submit! { + $crate::inventory::AddClassToInventory::new(C) + } + C + }}; +} diff --git a/src/lib.rs b/src/lib.rs index 0fcf71d..0b0d6d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,10 @@ mod class; mod defaults; mod parse; +#[cfg(feature = "inventory")] +#[macro_use] +pub mod inventory; + pub struct Zephyr { /// for non-value rules pub rules: HashMap, @@ -41,12 +45,13 @@ impl Zephyr { } } - pub fn generate_css(&self, classes: &[&str]) -> String { - // TODO when we have media queries, we can do something to group them together + pub fn generate_classes<'a>(&self, classes: impl IntoIterator) -> String { + // TODO when we have media queries, we can do something to group them by the query, and then emit those together classes .into_iter() .flat_map(|s| s.split_ascii_whitespace()) + // TODO skip duplicates, use hashset or smth .flat_map(|c| self.generate_class(c)) .collect::>() .join("") @@ -104,19 +109,19 @@ mod tests { fn generate_classes_works() { let z = Zephyr::new(); - let classes = z.generate_css(&["flex-row"]); + let classes = z.generate_classes(["flex-row"]); assert_eq!( classes, r#".flex-row { display: flex; flex-direction: row; }"# ); - let classes = z.generate_css(&["m[3rem]hover,focus$placeholder"]); + let classes = z.generate_classes(["m[3rem]hover,focus$placeholder"]); assert_eq!( classes, r#".m\[3rem\]hover,focus\$placeholder:hover:focus::placeholder { margin: 3rem; }"# ); - let classes = z.generate_css(&["flex|hover,focus$placeholder"]); + let classes = z.generate_classes(["flex|hover,focus$placeholder"]); assert_eq!( classes, r#".flex\|hover,focus\$placeholder:hover:focus::placeholder { display: flex; }"# @@ -127,8 +132,8 @@ mod tests { fn generate_multiple_works() { let z = Zephyr::new(); - let classes_joined = z.generate_css(&["flex-row mt[1rem]"]); - let classes_separate = z.generate_css(&["flex-row", "mt[1rem]"]); + let classes_joined = z.generate_classes(["flex-row mt[1rem]"]); + let classes_separate = z.generate_classes(["flex-row", "mt[1rem]"]); assert_eq!( classes_joined, r#".flex-row { display: flex; flex-direction: row; }.mt\[1rem\] { margin-top: 1rem; }"#