add variable support
This commit is contained in:
parent
0f09a5e22d
commit
c5301cfe63
38
src/class.rs
38
src/class.rs
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
media_queries::{ReducedMotion, Responsive},
|
||||
modifiers::Modifiers,
|
||||
|
@ -8,8 +10,7 @@ use crate::{
|
|||
pub(crate) struct Class<'a> {
|
||||
pub property: &'a str,
|
||||
pub value: Option<&'a str>,
|
||||
/// if true, no replacements will be done on `value`
|
||||
pub value_literal: bool,
|
||||
pub value_type: ValueType,
|
||||
pub modifiers: Modifiers<'a>,
|
||||
pub pseudo: Option<&'a str>,
|
||||
/// the original unparsed value
|
||||
|
@ -17,6 +18,16 @@ pub(crate) struct Class<'a> {
|
|||
pub original: &'a str,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) enum ValueType {
|
||||
/// replacements will be performed
|
||||
Normal,
|
||||
/// no replacements will be done. value will be output as-is
|
||||
Literal,
|
||||
/// value will be output as `var(--value)`, without any replacements
|
||||
Variable,
|
||||
}
|
||||
|
||||
impl<'a> Class<'a> {
|
||||
pub(crate) fn selector(&self, z: &Zephyr) -> String {
|
||||
let Class {
|
||||
|
@ -73,14 +84,12 @@ impl<'a> Class<'a> {
|
|||
let selector = self.selector(z);
|
||||
|
||||
if let Some(val) = self.value {
|
||||
let val = if self.value_literal {
|
||||
val.to_string()
|
||||
} else {
|
||||
z.values
|
||||
.get(val)
|
||||
.map(AsRef::as_ref)
|
||||
.unwrap_or(val)
|
||||
.replace('_', " ")
|
||||
let val = match self.value_type {
|
||||
ValueType::Normal => {
|
||||
replace_underscores(z.values.get(val).map(AsRef::as_ref).unwrap_or(val))
|
||||
}
|
||||
ValueType::Literal => val.into(),
|
||||
ValueType::Variable => format!("var(--{val})").into(),
|
||||
};
|
||||
|
||||
if let Some(fun) = z.specials.get(property) {
|
||||
|
@ -109,3 +118,12 @@ impl<'a> Class<'a> {
|
|||
Ok(css)
|
||||
}
|
||||
}
|
||||
|
||||
/// replaces underscores with spaces
|
||||
fn replace_underscores<'a>(s: &'a str) -> Cow<'a, str> {
|
||||
if s.contains('_') {
|
||||
s.replace('_', " ").into()
|
||||
} else {
|
||||
s.into()
|
||||
}
|
||||
}
|
||||
|
|
18
src/lib.rs
18
src/lib.rs
|
@ -72,9 +72,6 @@ impl Zephyr {
|
|||
|
||||
/// generates css rules for all the of the classes that parse correctly
|
||||
pub fn generate_classes<'a>(&self, classes: impl IntoIterator<Item = &'a str>) -> String {
|
||||
// TODO when we have media queries, we can do something to group them by the query,
|
||||
// and then emit those together
|
||||
|
||||
// TODO we could return (css, seen_classes)
|
||||
let mut seen_classes = vec![];
|
||||
|
||||
|
@ -139,7 +136,7 @@ mod tests {
|
|||
modifiers: vec![].into(),
|
||||
pseudo: None,
|
||||
original: "m[1rem]",
|
||||
value_literal: false,
|
||||
value_type: class::ValueType::Normal,
|
||||
};
|
||||
let css = class.generate(&z).unwrap();
|
||||
assert_eq!(css, r#".m\[1rem\]{margin:1rem;}"#);
|
||||
|
@ -150,7 +147,7 @@ mod tests {
|
|||
modifiers: vec!["focus"].into(),
|
||||
pseudo: None,
|
||||
original: "m[1rem]focus",
|
||||
value_literal: false,
|
||||
value_type: class::ValueType::Normal,
|
||||
};
|
||||
let css = class.generate(&z).unwrap();
|
||||
assert_eq!(css, r#".m\[1rem\]focus:focus{margin:1rem;}"#);
|
||||
|
@ -161,7 +158,7 @@ mod tests {
|
|||
modifiers: vec!["focus", "hover", "odd"].into(),
|
||||
pseudo: None,
|
||||
original: "m[1rem]focus,hover,odd",
|
||||
value_literal: false,
|
||||
value_type: class::ValueType::Normal,
|
||||
};
|
||||
let css = class.generate(&z).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -251,4 +248,13 @@ mod tests {
|
|||
r#"@media(min-width:640px){.m\[1rem\]sm{margin:1rem;}}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_variable() {
|
||||
let z = Zephyr::new();
|
||||
|
||||
// the curly brackets indicate that the value should not go through replacements
|
||||
let classes = z.generate_classes(["m(my-margin)"]);
|
||||
assert_eq!(classes, r#".m\(my-margin\){margin:var(--my-margin);}"#);
|
||||
}
|
||||
}
|
||||
|
|
73
src/parse.rs
73
src/parse.rs
|
@ -1,4 +1,7 @@
|
|||
use crate::{class::Class, ZephyrError};
|
||||
use crate::{
|
||||
class::{Class, ValueType},
|
||||
ZephyrError,
|
||||
};
|
||||
|
||||
pub(crate) fn parse_class<'a>(original: &'a str) -> Result<Class<'a>, ZephyrError> {
|
||||
// this code is kinda repetitive but idk
|
||||
|
@ -22,10 +25,13 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Result<Class<'a>, ZephyrErro
|
|||
modifiers: mods.into(),
|
||||
pseudo,
|
||||
original,
|
||||
value_literal: false,
|
||||
value_type: ValueType::Normal,
|
||||
});
|
||||
}
|
||||
|
||||
// first try braces, then parenthesis, then square brakcets
|
||||
// i know it's kinda ugly and repetitive, but it works for now
|
||||
|
||||
match (pos(class, '{'), pos(class, '}')) {
|
||||
(Some(start), Some(end)) if start <= end => {
|
||||
let mods = if end + 1 == class.len() {
|
||||
|
@ -40,7 +46,7 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Result<Class<'a>, ZephyrErro
|
|||
modifiers: mods.into(),
|
||||
pseudo,
|
||||
original,
|
||||
value_literal: true,
|
||||
value_type: ValueType::Literal,
|
||||
});
|
||||
}
|
||||
// go to [...] case
|
||||
|
@ -51,6 +57,28 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Result<Class<'a>, ZephyrErro
|
|||
}
|
||||
};
|
||||
|
||||
match (pos(class, '('), pos(class, ')')) {
|
||||
(Some(start), Some(end)) if start <= end => {
|
||||
let mods = if end + 1 == class.len() {
|
||||
vec![]
|
||||
} else {
|
||||
class[end + 1..].split(',').collect()
|
||||
};
|
||||
|
||||
return Ok(Class {
|
||||
property: &class[0..start],
|
||||
value: Some(&class[start + 1..end]),
|
||||
modifiers: mods.into(),
|
||||
pseudo,
|
||||
original,
|
||||
value_type: ValueType::Variable,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
match (pos(class, '['), pos(class, ']')) {
|
||||
(Some(start), Some(end)) if start <= end => {
|
||||
let mods = if end + 1 == class.len() {
|
||||
|
@ -65,7 +93,7 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Result<Class<'a>, ZephyrErro
|
|||
modifiers: mods.into(),
|
||||
pseudo,
|
||||
original,
|
||||
value_literal: false,
|
||||
value_type: ValueType::Normal,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
|
@ -75,7 +103,7 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Result<Class<'a>, ZephyrErro
|
|||
modifiers: vec![].into(),
|
||||
pseudo,
|
||||
original,
|
||||
value_literal: false,
|
||||
value_type: ValueType::Normal,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -101,13 +129,19 @@ mod tests {
|
|||
modifiers: modifiers.into(),
|
||||
pseudo,
|
||||
original: class,
|
||||
value_literal: false,
|
||||
value_type: ValueType::Normal,
|
||||
})
|
||||
);
|
||||
}
|
||||
fn check_literal(
|
||||
fn check_with_type(
|
||||
class: &str,
|
||||
(property, value, modifiers, pseudo): (&str, Option<&str>, Vec<&str>, Option<&str>),
|
||||
(property, value, modifiers, pseudo, value_type): (
|
||||
&str,
|
||||
Option<&str>,
|
||||
Vec<&str>,
|
||||
Option<&str>,
|
||||
ValueType,
|
||||
),
|
||||
) {
|
||||
assert_eq!(
|
||||
parse_class(class),
|
||||
|
@ -117,7 +151,7 @@ mod tests {
|
|||
modifiers: modifiers.into(),
|
||||
pseudo,
|
||||
original: class,
|
||||
value_literal: true,
|
||||
value_type,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -143,12 +177,29 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_literal_values() {
|
||||
// testing out weird unicode stuffs
|
||||
check_literal(
|
||||
check_with_type(
|
||||
"hello{hey_hello}",
|
||||
("hello", Some("hey_hello"), vec![], None),
|
||||
("hello", Some("hey_hello"), vec![], None, ValueType::Literal),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_variable_values() {
|
||||
// testing out weird unicode stuffs
|
||||
check_with_type(
|
||||
"hello(hey_hello)",
|
||||
(
|
||||
"hello",
|
||||
Some("hey_hello"),
|
||||
vec![],
|
||||
None,
|
||||
ValueType::Variable,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO add more tests for interactions between all of those
|
||||
|
||||
#[test]
|
||||
fn parse_modifier() {
|
||||
check("a[b]hover", ("a", Some("b"), vec!["hover"], None));
|
||||
|
|
Loading…
Reference in New Issue