add name and stuff
This commit is contained in:
parent
3e5fdde1f3
commit
502d626cb7
|
@ -7,4 +7,3 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
once_cell = "1.10.0"
|
||||
thiserror = "1.0.31"
|
||||
|
|
100
src/lib.rs
100
src/lib.rs
|
@ -1,13 +1,12 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use std::{collections::HashMap, path::Path};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::parse::*;
|
||||
use crate::{parse::*, rules::*};
|
||||
use std::path::Path;
|
||||
|
||||
mod modifiers;
|
||||
mod name;
|
||||
mod parse;
|
||||
mod rules;
|
||||
|
||||
pub fn generate_and_write(classes: &[&str], path: impl AsRef<Path>) -> Result<(), Error> {
|
||||
pub fn generate_and_write(classes: &[&str], path: impl AsRef<Path>) -> Result<(), std::io::Error> {
|
||||
let out = generate_css(classes);
|
||||
std::fs::write(path, out)?;
|
||||
|
||||
|
@ -24,75 +23,12 @@ pub fn generate_css(classes: &[&str]) -> String {
|
|||
|
||||
pub fn generate_class(class: &str) -> Option<String> {
|
||||
let class = parse_class(class)?;
|
||||
let rule = RULES.get(&class.name)?;
|
||||
let rule = RULES
|
||||
.get(&class.name.as_str())
|
||||
.unwrap_or(&(&General as &dyn Rule));
|
||||
Some(rule.generate(&class))
|
||||
}
|
||||
|
||||
static RULES: Lazy<HashMap<&str, &dyn Rule>> = Lazy::new(|| {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("m", &Margin as &dyn Rule);
|
||||
m.insert("mt", &MarginTop as &dyn Rule);
|
||||
m.insert("color", &Color as &dyn Rule);
|
||||
m.insert("content", &Content as &dyn Rule);
|
||||
m
|
||||
});
|
||||
|
||||
// TODO maybe we can skip rules and make it just be a general rewritter
|
||||
|
||||
trait Rule: Sync {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String;
|
||||
}
|
||||
|
||||
struct Margin;
|
||||
impl Rule for Margin {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String {
|
||||
format!(
|
||||
"{selector} {{ margin: {value}; }}",
|
||||
selector = class.selector(),
|
||||
value = class.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct MarginTop;
|
||||
impl Rule for MarginTop {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String {
|
||||
format!(
|
||||
"{selector} {{ margin-top: {value}; }}",
|
||||
selector = class.selector(),
|
||||
value = class.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct Color;
|
||||
impl Rule for Color {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String {
|
||||
format!(
|
||||
"{selector} {{ color: {value}; }}",
|
||||
selector = class.selector(),
|
||||
value = class.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct Content;
|
||||
impl Rule for Content {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String {
|
||||
format!(
|
||||
"{selector} {{ content: {value}; }}",
|
||||
selector = class.selector(),
|
||||
value = class.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("io error")]
|
||||
Disconnect(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -100,36 +36,36 @@ mod tests {
|
|||
#[test]
|
||||
fn generate_margin_works() {
|
||||
let class = Class {
|
||||
name: "m",
|
||||
name: "m".into(),
|
||||
value: "1rem",
|
||||
modifiers: vec![].into(),
|
||||
pseudo: None,
|
||||
original: "m[1rem]",
|
||||
};
|
||||
let css = Margin.generate(&class);
|
||||
assert_eq!(css, ".m[1rem] { margin: 1rem; }");
|
||||
let css = General.generate(&class);
|
||||
assert_eq!(css, r#".m\[1rem\] { margin: 1rem; }"#);
|
||||
|
||||
let class = Class {
|
||||
name: "m",
|
||||
name: "m".into(),
|
||||
value: "1rem",
|
||||
modifiers: vec!["focus"].into(),
|
||||
pseudo: None,
|
||||
original: "m[1rem]focus",
|
||||
};
|
||||
let css = Margin.generate(&class);
|
||||
assert_eq!(css, ".m[1rem]focus:focus { margin: 1rem; }");
|
||||
let css = General.generate(&class);
|
||||
assert_eq!(css, r#".m\[1rem\]focus:focus { margin: 1rem; }"#);
|
||||
|
||||
let class = Class {
|
||||
name: "m",
|
||||
name: "m".into(),
|
||||
value: "1rem",
|
||||
modifiers: vec!["focus", "hover", "odd"].into(),
|
||||
pseudo: None,
|
||||
original: "m[1rem]focus,hover,odd",
|
||||
};
|
||||
let css = Margin.generate(&class);
|
||||
let css = General.generate(&class);
|
||||
assert_eq!(
|
||||
css,
|
||||
".m[1rem]focus,hover,odd:focus:hover:nth-child(odd) { margin: 1rem; }"
|
||||
r#".m\[1rem\]focus,hover,odd:focus:hover:nth-child\(odd\) { margin: 1rem; }"#
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -139,7 +75,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
classes,
|
||||
".m[3rem]hover,focus$placeholder:hover:focus::placeholder { margin: 3rem; }"
|
||||
r#".m\[3rem\]hover,focus\$placeholder:hover:focus::placeholder { margin: 3rem; }"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
static REPLACEMENTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
[
|
||||
("odd", "nth-child(odd)"),
|
||||
("even", "nth-child(even)"),
|
||||
("first", "first-child"),
|
||||
("last", "last-child"),
|
||||
("only", "only-child"),
|
||||
]
|
||||
.into()
|
||||
});
|
||||
|
||||
#[derive(Default, PartialEq, Debug)]
|
||||
pub(crate) struct Modifiers<'a>(Vec<Modifier<'a>>);
|
||||
|
||||
|
@ -9,7 +23,7 @@ impl<'a> Modifiers<'a> {
|
|||
Some(
|
||||
self.0
|
||||
.iter()
|
||||
.map(Modifier::value)
|
||||
.map(Modifier::as_str)
|
||||
.collect::<Vec<_>>()
|
||||
.join(":"),
|
||||
)
|
||||
|
@ -27,10 +41,6 @@ impl<'a> From<Vec<&'a str>> for Modifiers<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO something like this
|
||||
// i wanna be able to have both replaced variables for common modifiers
|
||||
// eg: odd -> :nth-child(odd)
|
||||
// but i also wanna be able to keep it relaxed so you can type whatever
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Modifier<'a> {
|
||||
Converted { from: &'a str, to: &'static str },
|
||||
|
@ -39,36 +49,17 @@ enum Modifier<'a> {
|
|||
|
||||
impl<'a> Modifier<'a> {
|
||||
fn new(s: &'a str) -> Self {
|
||||
match s {
|
||||
"odd" => Self::Converted {
|
||||
from: s,
|
||||
to: "nth-child(odd)",
|
||||
},
|
||||
"even" => Self::Converted {
|
||||
from: s,
|
||||
to: "nth-child(even)",
|
||||
},
|
||||
"first" => Self::Converted {
|
||||
from: s,
|
||||
to: "first-child",
|
||||
},
|
||||
"last" => Self::Converted {
|
||||
from: s,
|
||||
to: "last-child",
|
||||
},
|
||||
"only" => Self::Converted {
|
||||
from: s,
|
||||
to: "only-child",
|
||||
},
|
||||
// TODO add more
|
||||
_ => Self::Unknown(s),
|
||||
if let Some(to) = REPLACEMENTS.get(s) {
|
||||
Self::Converted { from: s, to }
|
||||
} else {
|
||||
Self::Unknown(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&self) -> &str {
|
||||
fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Modifier::Converted { from, to } => to,
|
||||
Modifier::Unknown(v) => v,
|
||||
Self::Converted { to, .. } => to,
|
||||
Self::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
static REPLACEMENTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
[
|
||||
("m", "margin"),
|
||||
("mt", "margin-top"),
|
||||
("mb", "margin-bottom"),
|
||||
("ml", "margin-left"),
|
||||
("mr", "margin-right"),
|
||||
("p", "padding"),
|
||||
("pt", "padding-top"),
|
||||
("pb", "padding-bottom"),
|
||||
("pl", "padding-left"),
|
||||
("pr", "padding-right"),
|
||||
("bg", "background"),
|
||||
("bgc", "background-color"),
|
||||
]
|
||||
.into()
|
||||
});
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum Name<'a> {
|
||||
Converted { from: &'a str, to: &'static str },
|
||||
Unknown(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> Name<'a> {
|
||||
pub(crate) fn new(s: &'a str) -> Self {
|
||||
if let Some(to) = REPLACEMENTS.get(s) {
|
||||
Self::Converted { from: s, to }
|
||||
} else {
|
||||
Self::Unknown(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Converted { to, .. } => to,
|
||||
Self::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Name<'a> {
|
||||
fn from(s: &'a str) -> Self {
|
||||
Name::new(s)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::modifiers::Modifiers;
|
||||
use crate::{modifiers::Modifiers, name::Name};
|
||||
|
||||
pub(crate) fn parse_class<'a>(original: &'a str) -> Option<Class<'a>> {
|
||||
let (class, pseudo) = if let Some((class, pseudo)) = original.split_once('$') {
|
||||
|
@ -21,7 +21,7 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Option<Class<'a>> {
|
|||
};
|
||||
|
||||
Some(Class {
|
||||
name: &class[0..start],
|
||||
name: Name::new(&class[0..start]),
|
||||
value: &class[start + 1..end],
|
||||
modifiers: mods.into(),
|
||||
pseudo,
|
||||
|
@ -31,7 +31,7 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Option<Class<'a>> {
|
|||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) struct Class<'a> {
|
||||
pub name: &'a str,
|
||||
pub name: Name<'a>,
|
||||
pub value: &'a str,
|
||||
pub modifiers: Modifiers<'a>,
|
||||
pub pseudo: Option<&'a str>,
|
||||
|
@ -68,6 +68,7 @@ impl<'a> Class<'a> {
|
|||
.replace('$', "\\$")
|
||||
.replace('\'', "\\'")
|
||||
.replace('*', "\\*")
|
||||
.replace('%', "\\%")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +84,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
parse_class(class),
|
||||
Some(Class {
|
||||
name,
|
||||
name: Name::new(name),
|
||||
value,
|
||||
modifiers: modifiers.into(),
|
||||
pseudo,
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::parse::Class;
|
||||
|
||||
pub(crate) static RULES: Lazy<HashMap<&str, &dyn Rule>> = Lazy::new(|| {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("flex", &Flex as &dyn Rule);
|
||||
m
|
||||
});
|
||||
|
||||
pub(crate) trait Rule: Sync {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String;
|
||||
}
|
||||
|
||||
/// fallback general replacer
|
||||
/// this is the one that will be used the most, as it emits a css rule with a single property
|
||||
pub(crate) struct General;
|
||||
impl Rule for General {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String {
|
||||
format!(
|
||||
"{selector} {{ {name}: {value}; }}",
|
||||
selector = class.selector(),
|
||||
name = class.name.as_str(),
|
||||
value = class.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// the rest of the rules go here
|
||||
// these ones are not a simple replacement
|
||||
|
||||
struct Flex;
|
||||
impl Rule for Flex {
|
||||
fn generate<'a>(&self, class: &Class<'a>) -> String {
|
||||
format!(
|
||||
"{selector} {{ display: flex; }}",
|
||||
selector = class.selector(),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue