add name and stuff
This commit is contained in:
parent
3e5fdde1f3
commit
502d626cb7
|
@ -7,4 +7,3 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = "1.10.0"
|
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 crate::{parse::*, rules::*};
|
||||||
use std::{collections::HashMap, path::Path};
|
use std::path::Path;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::parse::*;
|
|
||||||
|
|
||||||
mod modifiers;
|
mod modifiers;
|
||||||
|
mod name;
|
||||||
mod parse;
|
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);
|
let out = generate_css(classes);
|
||||||
std::fs::write(path, out)?;
|
std::fs::write(path, out)?;
|
||||||
|
|
||||||
|
@ -24,75 +23,12 @@ pub fn generate_css(classes: &[&str]) -> String {
|
||||||
|
|
||||||
pub fn generate_class(class: &str) -> Option<String> {
|
pub fn generate_class(class: &str) -> Option<String> {
|
||||||
let class = parse_class(class)?;
|
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))
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -100,36 +36,36 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_margin_works() {
|
fn generate_margin_works() {
|
||||||
let class = Class {
|
let class = Class {
|
||||||
name: "m",
|
name: "m".into(),
|
||||||
value: "1rem",
|
value: "1rem",
|
||||||
modifiers: vec![].into(),
|
modifiers: vec![].into(),
|
||||||
pseudo: None,
|
pseudo: None,
|
||||||
original: "m[1rem]",
|
original: "m[1rem]",
|
||||||
};
|
};
|
||||||
let css = Margin.generate(&class);
|
let css = General.generate(&class);
|
||||||
assert_eq!(css, ".m[1rem] { margin: 1rem; }");
|
assert_eq!(css, r#".m\[1rem\] { margin: 1rem; }"#);
|
||||||
|
|
||||||
let class = Class {
|
let class = Class {
|
||||||
name: "m",
|
name: "m".into(),
|
||||||
value: "1rem",
|
value: "1rem",
|
||||||
modifiers: vec!["focus"].into(),
|
modifiers: vec!["focus"].into(),
|
||||||
pseudo: None,
|
pseudo: None,
|
||||||
original: "m[1rem]focus",
|
original: "m[1rem]focus",
|
||||||
};
|
};
|
||||||
let css = Margin.generate(&class);
|
let css = General.generate(&class);
|
||||||
assert_eq!(css, ".m[1rem]focus:focus { margin: 1rem; }");
|
assert_eq!(css, r#".m\[1rem\]focus:focus { margin: 1rem; }"#);
|
||||||
|
|
||||||
let class = Class {
|
let class = Class {
|
||||||
name: "m",
|
name: "m".into(),
|
||||||
value: "1rem",
|
value: "1rem",
|
||||||
modifiers: vec!["focus", "hover", "odd"].into(),
|
modifiers: vec!["focus", "hover", "odd"].into(),
|
||||||
pseudo: None,
|
pseudo: None,
|
||||||
original: "m[1rem]focus,hover,odd",
|
original: "m[1rem]focus,hover,odd",
|
||||||
};
|
};
|
||||||
let css = Margin.generate(&class);
|
let css = General.generate(&class);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
css,
|
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!(
|
assert_eq!(
|
||||||
classes,
|
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)]
|
#[derive(Default, PartialEq, Debug)]
|
||||||
pub(crate) struct Modifiers<'a>(Vec<Modifier<'a>>);
|
pub(crate) struct Modifiers<'a>(Vec<Modifier<'a>>);
|
||||||
|
|
||||||
|
@ -9,7 +23,7 @@ impl<'a> Modifiers<'a> {
|
||||||
Some(
|
Some(
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(Modifier::value)
|
.map(Modifier::as_str)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(":"),
|
.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)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum Modifier<'a> {
|
enum Modifier<'a> {
|
||||||
Converted { from: &'a str, to: &'static str },
|
Converted { from: &'a str, to: &'static str },
|
||||||
|
@ -39,36 +49,17 @@ enum Modifier<'a> {
|
||||||
|
|
||||||
impl<'a> Modifier<'a> {
|
impl<'a> Modifier<'a> {
|
||||||
fn new(s: &'a str) -> Self {
|
fn new(s: &'a str) -> Self {
|
||||||
match s {
|
if let Some(to) = REPLACEMENTS.get(s) {
|
||||||
"odd" => Self::Converted {
|
Self::Converted { from: s, to }
|
||||||
from: s,
|
} else {
|
||||||
to: "nth-child(odd)",
|
Self::Unknown(s)
|
||||||
},
|
|
||||||
"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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value(&self) -> &str {
|
fn as_str(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Modifier::Converted { from, to } => to,
|
Self::Converted { to, .. } => to,
|
||||||
Modifier::Unknown(v) => v,
|
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>> {
|
pub(crate) fn parse_class<'a>(original: &'a str) -> Option<Class<'a>> {
|
||||||
let (class, pseudo) = if let Some((class, pseudo)) = original.split_once('$') {
|
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 {
|
Some(Class {
|
||||||
name: &class[0..start],
|
name: Name::new(&class[0..start]),
|
||||||
value: &class[start + 1..end],
|
value: &class[start + 1..end],
|
||||||
modifiers: mods.into(),
|
modifiers: mods.into(),
|
||||||
pseudo,
|
pseudo,
|
||||||
|
@ -31,7 +31,7 @@ pub(crate) fn parse_class<'a>(original: &'a str) -> Option<Class<'a>> {
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub(crate) struct Class<'a> {
|
pub(crate) struct Class<'a> {
|
||||||
pub name: &'a str,
|
pub name: Name<'a>,
|
||||||
pub value: &'a str,
|
pub value: &'a str,
|
||||||
pub modifiers: Modifiers<'a>,
|
pub modifiers: Modifiers<'a>,
|
||||||
pub pseudo: Option<&'a str>,
|
pub pseudo: Option<&'a str>,
|
||||||
|
@ -68,6 +68,7 @@ impl<'a> Class<'a> {
|
||||||
.replace('$', "\\$")
|
.replace('$', "\\$")
|
||||||
.replace('\'', "\\'")
|
.replace('\'', "\\'")
|
||||||
.replace('*', "\\*")
|
.replace('*', "\\*")
|
||||||
|
.replace('%', "\\%")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_class(class),
|
parse_class(class),
|
||||||
Some(Class {
|
Some(Class {
|
||||||
name,
|
name: Name::new(name),
|
||||||
value,
|
value,
|
||||||
modifiers: modifiers.into(),
|
modifiers: modifiers.into(),
|
||||||
pseudo,
|
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