dynatos_html::{html, html_file} now allow expressions inside of text using %{expr}%.

This commit is contained in:
Filipe Rodrigues 2024-12-06 09:24:01 +00:00
parent f3d792f544
commit 9939564b37
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
5 changed files with 66 additions and 6 deletions

1
Cargo.lock generated
View File

@ -95,6 +95,7 @@ name = "dynatos-html-macros"
version = "0.1.0"
dependencies = [
"dynatos-html-parser",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.50",

View File

@ -10,6 +10,7 @@ proc-macro = true
dynatos-html-parser = { workspace = true }
itertools = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
syn = { workspace = true }

View File

@ -3,6 +3,7 @@
// Imports
use {
dynatos_html_parser::{XHtml, XHtmlNode},
itertools::Itertools,
proc_macro::TokenStream,
std::{
fs,
@ -197,11 +198,37 @@ impl Node {
return None;
}
let args = self::split_text_args(text);
// If we have just a single constant argument, return a simple version
if let [TextArg::Cons(text)] = &*args {
return Some(Self {
ty: NodeTy::Text,
expr: syn::parse_quote!(
dynatos_html::text(#text)
),
});
};
let (cons, args) = args
.into_iter()
.partition_map::<Vec<_>, Vec<_>, _, _, _>(|arg| match arg {
TextArg::Cons(text) => itertools::Either::Left(text),
TextArg::Argument(arg) => itertools::Either::Right(arg),
});
let fmt = cons.into_iter().join("{}");
let args = args
.into_iter()
.map(|arg| syn::parse_str::<syn::Expr>(arg).expect("Unable to parse argument expression"))
.collect::<Vec<_>>();
Self {
ty: NodeTy::Text,
expr: syn::parse_quote!(
dynatos_html::text(#text)
),
expr: syn::parse_quote!(dynatos::NodeWithDynText::with_dyn_text(
dynatos_html::text(""),
move || format!(#fmt, #(#args),*)
)),
}
},
XHtmlNode::Comment(comment) => Self {
@ -221,3 +248,35 @@ impl quote::ToTokens for Node {
self.expr.to_tokens(tokens);
}
}
enum TextArg<'a> {
/// Constant
Cons(&'a str),
/// Argument
Argument(&'a str),
}
/// Splits a string into constants and arguments
fn split_text_args(mut text: &str) -> Vec<TextArg> {
let mut args = vec![];
while !text.is_empty() {
// Find the first escape
#[expect(clippy::mixed_read_write_in_expression, reason = "False positive")]
let Some(start) = text.find("%{") else {
args.push(TextArg::Cons(text));
text = &text[text.len()..];
continue;
};
let Some(end) = text[start..].find("}%") else {
panic!("Expected `}}%`, found {:?}", &text[start..]);
};
args.push(TextArg::Cons(&text[..start]));
args.push(TextArg::Argument(&text[start..][2..end]));
text = &text[start..][end + 2..];
}
args
}

1
examples/Cargo.lock generated
View File

@ -98,6 +98,7 @@ name = "dynatos-html-macros"
version = "0.1.0"
dependencies = [
"dynatos-html-parser",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.53",

View File

@ -5,7 +5,6 @@
// Imports
use {
dynatos::NodeWithDynText,
dynatos_html::{html, NodeWithChildren, NodeWithText},
dynatos_reactive::{Signal, SignalBorrowMut, SignalGet, SignalSet},
dynatos_util::{cloned, ev, EventTargetWithListener, JsResultContext},
@ -57,7 +56,6 @@ fn counter() -> Element {
html::button()
.with_text("-")
.with_event_listener::<ev::Click>(move |_ev| *value.borrow_mut() -= 1),
#[cloned(value)]
html::span().with_dyn_text(move || format!("Value: {}.", value.get())),
html!("<span>Value: %{value.get()}%.</span>"),
])
}