add name and stuff

This commit is contained in:
annieversary 2022-05-13 22:33:13 +01:00
parent 3e5fdde1f3
commit 502d626cb7
6 changed files with 135 additions and 118 deletions

View File

@ -7,4 +7,3 @@ edition = "2021"
[dependencies]
once_cell = "1.10.0"
thiserror = "1.0.31"

View File

@ -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; }"#
);
}
}

View File

@ -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,
}
}
}

49
src/name.rs Normal file
View File

@ -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)
}
}

View File

@ -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,

41
src/rules.rs Normal file
View File

@ -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(),
)
}
}