css parsing mostly working
This commit is contained in:
commit
3584048116
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,322 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "stylance"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stylance-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"core",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eae3c679c56dc214320b67a1bc04ef3dfbd6411f6443974b5e4893231298e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["cli", "core", "macros"]
|
||||
|
||||
[workspace.dependencies]
|
||||
core = { path = "./core" }
|
||||
macros = { path = "./macros" }
|
||||
|
||||
[package]
|
||||
name = "stylance"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
macros = { path = "./macros" }
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "stylance-cli"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
walkdir = "2.4.0"
|
||||
core = { workspace = true }
|
||||
clap = { version = "4.4.12", features = ["derive"] }
|
||||
anyhow = "1.0.79"
|
|
@ -0,0 +1,48 @@
|
|||
use std::{
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
manifest_dir: PathBuf,
|
||||
|
||||
#[arg(short, long)]
|
||||
output: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
println!("yep");
|
||||
// Iterate over the directory and its subdirectories
|
||||
|
||||
let mut modified_css_files = Vec::new();
|
||||
|
||||
for (entry, meta) in WalkDir::new(&cli.manifest_dir)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter_map(|entry| entry.metadata().ok().map(|meta| (entry, meta)))
|
||||
{
|
||||
if meta.is_file() {
|
||||
if let Some(extension) = entry.path().extension() {
|
||||
if extension == "scss" && entry.path().to_string_lossy().ends_with(".scss") {
|
||||
println!("{}", entry.path().display());
|
||||
modified_css_files
|
||||
.push(core::load_and_modify_css(&cli.manifest_dir, entry.path())?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = File::create(cli.output)?;
|
||||
|
||||
file.write_all(modified_css_files.join("\n\n").as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.79"
|
||||
winnow = "0.5.31"
|
|
@ -0,0 +1,85 @@
|
|||
mod parse;
|
||||
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
fs,
|
||||
hash::{Hash as _, Hasher as _},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
pub fn hash_string(input: &str) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
input.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub struct Class {
|
||||
pub original_name: String,
|
||||
pub hashed_name: String,
|
||||
}
|
||||
|
||||
fn make_hash(manifest_dir: &Path, css_file: &Path) -> anyhow::Result<String> {
|
||||
let manifest_dir = manifest_dir.canonicalize()?;
|
||||
let css_file = css_file.canonicalize()?;
|
||||
|
||||
let mut relative_path_str = css_file.strip_prefix(manifest_dir)?.to_string_lossy();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let relative_path_str = relative_path_str.replace('\\', "/");
|
||||
|
||||
println!("{}", relative_path_str);
|
||||
|
||||
let hash = hash_string(&relative_path_str);
|
||||
let mut hash_str = format!("{hash:x}");
|
||||
hash_str.truncate(7);
|
||||
Ok(hash_str)
|
||||
}
|
||||
|
||||
fn modify_class(class: &str, hash_str: &str) -> String {
|
||||
format!("{class}-{hash_str}")
|
||||
}
|
||||
|
||||
pub fn load_and_modify_css(manifest_dir: &Path, css_file: &Path) -> anyhow::Result<String> {
|
||||
let hash_str = make_hash(manifest_dir, css_file)?;
|
||||
let css_file_contents = fs::read_to_string(css_file)?;
|
||||
|
||||
let classes = parse::get_css_classes(&css_file_contents).map_err(|e| anyhow!("{e}"))?;
|
||||
|
||||
let mut new_file = String::with_capacity(css_file_contents.len() * 2);
|
||||
let mut cursor = css_file_contents.as_str();
|
||||
|
||||
for class in classes {
|
||||
let (before, after) = cursor.split_at(class.as_ptr() as usize - cursor.as_ptr() as usize);
|
||||
cursor = &after[class.len()..];
|
||||
new_file.push_str(before);
|
||||
new_file.push_str(&modify_class(class, &hash_str));
|
||||
}
|
||||
|
||||
new_file.push_str(cursor);
|
||||
|
||||
Ok(new_file)
|
||||
}
|
||||
|
||||
pub fn get_classes(manifest_dir: &Path, css_file: &Path) -> Result<(String, Vec<Class>), ()> {
|
||||
let hash_str = make_hash(manifest_dir, css_file).map_err(|_| ())?;
|
||||
|
||||
let css_file_contents = fs::read_to_string(css_file).map_err(|_| ())?;
|
||||
|
||||
let mut classes = parse::get_css_classes(&css_file_contents).map_err(|_| ())?;
|
||||
|
||||
classes.sort();
|
||||
classes.dedup();
|
||||
|
||||
Ok((
|
||||
hash_str.clone(),
|
||||
classes
|
||||
.into_iter()
|
||||
.map(|class| Class {
|
||||
original_name: class.to_owned(),
|
||||
hashed_name: modify_class(class, &hash_str),
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
use winnow::{
|
||||
combinator::{alt, cut_err, fold_repeat, preceded, terminated},
|
||||
error::{ContextError, ParseError},
|
||||
stream::{AsChar, ContainsToken, Range},
|
||||
token::{none_of, one_of, tag, take_till, take_until0, take_while},
|
||||
PResult, Parser,
|
||||
};
|
||||
|
||||
pub fn get_css_classes(input: &str) -> Result<Vec<&str>, ParseError<&str, ContextError>> {
|
||||
style_rule_list.parse(input)
|
||||
}
|
||||
|
||||
pub fn recognize_repeat<'s, O>(
|
||||
range: impl Into<Range>,
|
||||
f: impl Parser<&'s str, O, ContextError>,
|
||||
) -> impl Parser<&'s str, &'s str, ContextError> {
|
||||
fold_repeat(range, f, || (), |_, _| ()).recognize()
|
||||
}
|
||||
|
||||
fn ws<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
recognize_repeat(
|
||||
0..,
|
||||
alt((
|
||||
line_comment,
|
||||
block_comment,
|
||||
take_while(1.., (AsChar::is_space, '\n', '\r')),
|
||||
)),
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn line_comment<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
("//", take_while(0.., |c| c != '\n'))
|
||||
.recognize()
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn block_comment<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
("/*", cut_err(terminated(take_until0("*/"), "*/")))
|
||||
.recognize()
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn identifier<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
(
|
||||
one_of(('_', '-', AsChar::is_alpha)),
|
||||
take_while(0.., ('_', '-', AsChar::is_alphanum)),
|
||||
)
|
||||
.recognize()
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn class<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
preceded('.', identifier).parse_next(input)
|
||||
}
|
||||
|
||||
fn string_dq<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
let str_char = alt((none_of(['"']).map(|_| ()), tag("\\\"").map(|_| ())));
|
||||
let str_chars = recognize_repeat(0.., str_char);
|
||||
|
||||
preceded('"', cut_err(terminated(str_chars, '"'))).parse_next(input)
|
||||
}
|
||||
|
||||
fn string_sq<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
let str_char = alt((none_of(['\'']).map(|_| ()), tag("\\'").map(|_| ())));
|
||||
let str_chars = recognize_repeat(0.., str_char);
|
||||
|
||||
preceded('\'', cut_err(terminated(str_chars, '\''))).parse_next(input)
|
||||
}
|
||||
|
||||
fn string<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
alt((string_dq, string_sq)).parse_next(input)
|
||||
}
|
||||
|
||||
/// Behaves like take_till except it finds and parses strings and
|
||||
/// comments (allowing those to contain the end condition characters).
|
||||
pub fn stuff_till<'s>(
|
||||
range: impl Into<Range>,
|
||||
list: impl ContainsToken<char>,
|
||||
) -> impl Parser<&'s str, &'s str, ContextError> {
|
||||
fold_repeat(
|
||||
range,
|
||||
alt((
|
||||
string.map(|_| ()),
|
||||
block_comment.map(|_| ()),
|
||||
line_comment.map(|_| ()),
|
||||
'/'.map(|_| ()),
|
||||
take_till(1.., ('\'', '"', '/', list)).map(|_| ()),
|
||||
)),
|
||||
|| (),
|
||||
|_, _| (),
|
||||
)
|
||||
.recognize()
|
||||
}
|
||||
|
||||
fn selector<'s>(input: &mut &'s str) -> PResult<Vec<&'s str>> {
|
||||
fold_repeat(
|
||||
1..,
|
||||
alt((
|
||||
class.map(Some),
|
||||
stuff_till(1.., ('.', ';', '{', '}')).map(|_| None),
|
||||
)),
|
||||
Vec::new,
|
||||
|mut acc, item| {
|
||||
if let Some(item) = item {
|
||||
acc.push(item);
|
||||
}
|
||||
acc
|
||||
},
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn declaration<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
(
|
||||
identifier,
|
||||
ws,
|
||||
':',
|
||||
terminated(stuff_till(1.., (';', '{', '}')), ';'),
|
||||
)
|
||||
.recognize()
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn style_rule_block<'s>(input: &mut &'s str) -> PResult<Vec<&'s str>> {
|
||||
let content = alt((
|
||||
declaration.map(|_| None), //
|
||||
at_rule.map(Some),
|
||||
style_rule.map(Some),
|
||||
));
|
||||
let contents = fold_repeat(0.., (ws, content), Vec::new, |mut acc, item| {
|
||||
if let Some(mut item) = item.1 {
|
||||
acc.append(&mut item);
|
||||
}
|
||||
acc
|
||||
});
|
||||
|
||||
preceded('{', cut_err(terminated(contents, (ws, '}')))).parse_next(input)
|
||||
}
|
||||
|
||||
fn style_rule<'s>(input: &mut &'s str) -> PResult<Vec<&'s str>> {
|
||||
let (mut classes, mut nested_classes) = (selector, style_rule_block).parse_next(input)?;
|
||||
classes.append(&mut nested_classes);
|
||||
Ok(classes)
|
||||
}
|
||||
|
||||
fn at_rule<'s>(input: &mut &'s str) -> PResult<Vec<&'s str>> {
|
||||
let (identifier, char) = preceded(
|
||||
'@',
|
||||
cut_err((
|
||||
terminated(identifier, stuff_till(0.., ('{', '}', ';'))),
|
||||
one_of(('{', ';')),
|
||||
)),
|
||||
)
|
||||
.parse_next(input)?;
|
||||
|
||||
if char == ';' {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
if identifier == "media" {
|
||||
cut_err(terminated(style_rule_list, '}')).parse_next(input)
|
||||
} else {
|
||||
cut_err(terminated(unknown_block_contents, '}')).parse_next(input)?;
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn unknown_block_contents<'s>(input: &mut &'s str) -> PResult<&'s str> {
|
||||
recognize_repeat(
|
||||
0..,
|
||||
alt((
|
||||
stuff_till(1.., ('{', '}')).map(|_| ()),
|
||||
('{', cut_err((unknown_block_contents, '}'))).map(|_| ()),
|
||||
)),
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn style_rule_list<'s>(input: &mut &'s str) -> PResult<Vec<&'s str>> {
|
||||
terminated(
|
||||
fold_repeat(0.., style_rule, Vec::new, |mut acc, mut item| {
|
||||
acc.append(&mut item);
|
||||
acc
|
||||
}),
|
||||
ws,
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_class() {
|
||||
let mut input = "._x1a2b Hello";
|
||||
|
||||
let r = class.parse_next(&mut input);
|
||||
assert_eq!(r, Ok("_x1a2b"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selector() {
|
||||
let mut input = ".foo.bar [value=\"fa.sdasd\"] /* .banana */ // .apple \n \t .cry {";
|
||||
|
||||
let r = selector.parse_next(&mut input);
|
||||
assert_eq!(r, Ok(vec!["foo", "bar", "cry"]));
|
||||
|
||||
let mut input = "{";
|
||||
|
||||
let r = selector.recognize().parse_next(&mut input);
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_declaration() {
|
||||
let mut input = "background-color \t : red;";
|
||||
|
||||
let r = declaration.parse_next(&mut input);
|
||||
assert_eq!(r, Ok("background-color \t : red;"));
|
||||
|
||||
let r = declaration.parse_next(&mut input);
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_rule() {
|
||||
let mut input = ".foo.bar {
|
||||
background-color: red;
|
||||
.baz {
|
||||
color: blue;
|
||||
}
|
||||
@some-at-rule blah blah;
|
||||
@media blah .blah {
|
||||
.moo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}END";
|
||||
|
||||
let r = style_rule.parse_next(&mut input);
|
||||
assert_eq!(r, Ok(vec!["foo", "bar", "baz", "moo"]));
|
||||
|
||||
assert_eq!(input, "END");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_rule_list() {
|
||||
let mut input =
|
||||
".foo.bar { background-color \t\r\n : red; color: blue; } .baz.moo { color: red; .rad { color: red; } } ";
|
||||
|
||||
let r = style_rule_list.parse_next(&mut input);
|
||||
assert_eq!(r, Ok(vec!["foo", "bar", "baz", "moo", "rad"]));
|
||||
|
||||
assert!(input.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_at_rule_simple() {
|
||||
let mut input = "@simple-rule blah \"asd;asd\" blah;";
|
||||
|
||||
let r = at_rule.parse_next(&mut input);
|
||||
assert_eq!(r, Ok(vec![]));
|
||||
|
||||
assert!(input.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_at_rule_unknown() {
|
||||
let mut input = "@unknown blah \"asdasd\" blah {
|
||||
bunch of stuff {
|
||||
// things inside {
|
||||
blah
|
||||
' { '
|
||||
}
|
||||
|
||||
.bar {
|
||||
color: blue;
|
||||
|
||||
.baz {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
let r = at_rule.parse_next(&mut input);
|
||||
assert_eq!(r, Ok(vec![]));
|
||||
|
||||
assert!(input.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_at_rule_media() {
|
||||
let mut input = "@media blah \"asdasd\" blah {
|
||||
.foo {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.bar {
|
||||
color: blue;
|
||||
|
||||
.baz {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
let r = at_rule.parse_next(&mut input);
|
||||
assert_eq!(r, Ok(vec!["foo", "bar", "baz"]));
|
||||
|
||||
assert!(input.is_empty());
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
mod module;
|
||||
|
||||
use stylance::import_style;
|
||||
|
||||
use crate::module::style;
|
||||
|
||||
import_style!(style1, "examples/usage/style1.scss");
|
||||
|
||||
fn main() {
|
||||
style1::bar;
|
||||
style1::foo;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
use macros::import_style;
|
||||
|
||||
import_style!(style, "examples/usage/style2.scss");
|
|
@ -0,0 +1,22 @@
|
|||
.foo[value='.asdasd'] {
|
||||
background-color: red;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.bar.foo .wow {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.aaa {
|
||||
color: black;
|
||||
}
|
||||
|
||||
@media (min-width: 30em) and (orientation: landscape) {
|
||||
.aaa {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.bbb {
|
||||
color: green;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
.bar {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.facku {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.baaa {
|
||||
color: green;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
core = { workspace = true }
|
||||
proc-macro2 = "1.0.71"
|
||||
quote = "1.0.33"
|
||||
syn = { version = "2.0.43", features = ["extra-traits"] }
|
|
@ -0,0 +1,93 @@
|
|||
//#![feature(proc_macro_span)]
|
||||
|
||||
use std::{env, path::Path};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input, LitStr, Token,
|
||||
};
|
||||
|
||||
struct ImportStyleInput {
|
||||
style_ident: Ident,
|
||||
style_path: String,
|
||||
}
|
||||
|
||||
impl Parse for ImportStyleInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ident: Ident = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let string_literal: LitStr = input.parse()?;
|
||||
Ok(ImportStyleInput {
|
||||
style_ident: ident,
|
||||
style_path: string_literal.value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn import_style(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as ImportStyleInput);
|
||||
|
||||
let manifest_dir_env = env::var_os("CARGO_MANIFEST_DIR").expect("we need CARGO_MANIFEST_DIR");
|
||||
let manifest_path = Path::new(&manifest_dir_env);
|
||||
let file_path = manifest_path.join(Path::new(&input.style_path));
|
||||
|
||||
let (hash_str, classes) = core::get_classes(manifest_path, &file_path).expect("Load classes");
|
||||
|
||||
let binding = file_path.canonicalize().unwrap();
|
||||
let full_path = binding.to_string_lossy();
|
||||
|
||||
let identifiers = classes
|
||||
.iter()
|
||||
.map(|class| Ident::new(&class.original_name.replace('-', "_"), Span::call_site()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let struct_ident = Ident::new(&format!("Style{hash_str}"), Span::call_site());
|
||||
|
||||
let struct_definition = quote! {
|
||||
pub struct #struct_ident {
|
||||
#(pub #identifiers: &'static str,)*
|
||||
the_file: &'static str,
|
||||
}
|
||||
};
|
||||
|
||||
// let output_fields = classes.iter().zip(identifiers).map(|(class, class_ident)| {
|
||||
// let class_str = &class.hashed_name;
|
||||
// quote! {
|
||||
// #[deprecated]
|
||||
// #class_ident: #class_str,
|
||||
// }
|
||||
// });
|
||||
|
||||
let output_fields = classes.iter().zip(identifiers).map(|(class, class_ident)| {
|
||||
let class_str = &class.hashed_name;
|
||||
quote! {
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const #class_ident: &str = #class_str;
|
||||
}
|
||||
});
|
||||
|
||||
let style_ident = input.style_ident;
|
||||
|
||||
quote! {
|
||||
const _ : &[u8] = include_bytes!(#full_path);
|
||||
|
||||
pub mod #style_ident {
|
||||
#(#output_fields )*
|
||||
}
|
||||
}
|
||||
.into()
|
||||
// quote! {
|
||||
// #struct_definition
|
||||
|
||||
// pub const #style_ident: #struct_ident =
|
||||
// #struct_ident {
|
||||
// the_file: include_str!(#full_path),
|
||||
// #(#output_fields )*
|
||||
// }
|
||||
// ;
|
||||
// }
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub use macros::import_style;
|
Loading…
Reference in New Issue