Skip to content

Commit 8f79f6f

Browse files
Add direct-er literals
1 parent 2e85731 commit 8f79f6f

4 files changed

Lines changed: 72 additions & 10 deletions

File tree

packages/yew-macro/src/props/prop.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use quote::{quote, quote_spanned, ToTokens};
66
use syn::parse::{Parse, ParseBuffer, ParseStream};
77
use syn::spanned::Spanned;
88
use syn::token::Brace;
9-
use syn::{braced, Block, Expr, ExprBlock, ExprMacro, ExprPath, ExprRange, Stmt, Token};
9+
use syn::{braced, Block, Expr, ExprBlock, ExprMacro, ExprPath, ExprRange, Stmt, Token, LitStr, parse_quote};
1010

1111
use crate::html_tree::HtmlDashedName;
1212
use crate::stringify::Stringify;
@@ -27,6 +27,12 @@ impl From<HtmlDashedName> for PropLabel {
2727
}
2828
}
2929

30+
impl From<LitStr> for PropLabel {
31+
fn from(value: LitStr) -> Self {
32+
Self::Dynamic(parse_quote! { #value })
33+
}
34+
}
35+
3036
impl TryFrom<PropLabel> for HtmlDashedName {
3137
type Error = ();
3238

@@ -100,7 +106,9 @@ impl Parse for Prop {
100106
.map(PropDirective::ApplyAsProperty)
101107
.ok();
102108
if input.peek(Brace) {
103-
Self::parse_shorthand_or_dynamic_prop_assignment(input, directive)
109+
Self::parse_shorthand_or_expr_dynamic_prop_assignment(input, directive)
110+
} else if input.peek(LitStr) {
111+
Self::parse_literal_dynamic_prop_assignment(input, directive)
104112
} else {
105113
Self::parse_prop_assignment(input, directive)
106114
}
@@ -114,7 +122,7 @@ impl Prop {
114122
///
115123
/// Shorthand syntax only allows for labels with no hyphens,
116124
/// as it would otherwise create an ambiguity in the syntax.
117-
fn parse_shorthand_or_dynamic_prop_assignment(
125+
fn parse_shorthand_or_expr_dynamic_prop_assignment(
118126
input: ParseStream,
119127
directive: Option<PropDirective>,
120128
) -> syn::Result<Self> {
@@ -163,6 +171,37 @@ impl Prop {
163171
})
164172
}
165173

174+
/// Parse a prop of the form `"label"={value}`
175+
fn parse_literal_dynamic_prop_assignment(
176+
input: ParseStream,
177+
directive: Option<PropDirective>,
178+
) -> syn::Result<Self> {
179+
let label = input.parse::<LitStr>()?;
180+
let equals = input.parse::<Token![=]>().map_err(|_| {
181+
let display = label.stringify();
182+
syn::Error::new_spanned(
183+
&label,
184+
format!(
185+
"`{display}` doesn't have a value. (hint: set the value to `true` or `false` \
186+
for boolean attributes)"
187+
),
188+
)
189+
})?;
190+
if input.is_empty() {
191+
return Err(syn::Error::new_spanned(
192+
equals,
193+
"expected an expression following this equals sign",
194+
));
195+
}
196+
197+
let value = parse_prop_value(input)?;
198+
Ok(Self {
199+
label: label.into(),
200+
value,
201+
directive,
202+
})
203+
}
204+
166205
/// Parse a prop of the form `label={value}`
167206
fn parse_prop_assignment(
168207
input: ParseStream,

packages/yew-macro/tests/html_macro/dyn-prop-fail.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,13 @@ impl ::yew::Component for Simple {
5858
pub struct Fail;
5959

6060
fn main() {
61-
_ = ::yew::html! { <span { Fail }={ "" } /> };
62-
6361
let dyn_prop = || Fail;
62+
63+
_ = ::yew::html! { <span { Fail }={ "" } /> };
6464
_ = ::yew::html! { <span { dyn_prop() }={ "" } /> };
6565

66-
_ = ::yew::html! { <Simple { "test" }={ "" } /> }
66+
_ = ::yew::html! { <Simple "test"="" /> };
67+
_ = ::yew::html! { <Simple "test"={ "" } /> };
68+
_ = ::yew::html! { <Simple { "test" }={ "" } /> };
69+
_ = ::yew::html! { <Simple { dyn_prop() }={ "" } /> };
6770
}

packages/yew-macro/tests/html_macro/dyn-prop-fail.stderr

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
error: expected a valid Rust identifier
2-
--> tests/html_macro/dyn-prop-fail.rs:66:34
2+
--> tests/html_macro/dyn-prop-fail.rs:66:32
33
|
4-
66 | _ = ::yew::html! { <Simple { "test" }={ "" } /> }
4+
66 | _ = ::yew::html! { <Simple "test"="" /> };
5+
| ^^^^^^
6+
7+
error: expected a valid Rust identifier
8+
--> tests/html_macro/dyn-prop-fail.rs:67:32
9+
|
10+
67 | _ = ::yew::html! { <Simple "test"={ "" } /> };
11+
| ^^^^^^
12+
13+
error: expected a valid Rust identifier
14+
--> tests/html_macro/dyn-prop-fail.rs:68:34
15+
|
16+
68 | _ = ::yew::html! { <Simple { "test" }={ "" } /> };
517
| ^^^^^^
618

19+
error: expected a valid Rust identifier
20+
--> tests/html_macro/dyn-prop-fail.rs:69:34
21+
|
22+
69 | _ = ::yew::html! { <Simple { dyn_prop() }={ "" } /> };
23+
| ^^^^^^^^^^
24+
725
error[E0277]: the trait bound `implicit_clone::unsync::IString: From<Fail>` is not satisfied
8-
--> tests/html_macro/dyn-prop-fail.rs:61:9
26+
--> tests/html_macro/dyn-prop-fail.rs:63:9
927
|
10-
61 | _ = ::yew::html! { <span { Fail }={ "" } /> };
28+
63 | _ = ::yew::html! { <span { Fail }={ "" } /> };
1129
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<Fail>` is not implemented for `implicit_clone::unsync::IString`
1230
|
1331
= help: the following other types implement trait `From<T>`:

packages/yew-macro/tests/html_macro/dyn-prop-pass.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ fn main() {
4343
// but valid html! (any checks can happen only during runtime)
4444

4545
// literal
46+
_ = ::yew::html! { <span "hx-on:click"="alert('Clicked!')" /> };
47+
_ = ::yew::html! { <span "hx-on:click"="alert('Clicked!')" "hx-on:click"="alert('Clicked!')" /> };
4648
_ = ::yew::html! { <span { "hx-on:click" }={ "alert('Clicked!')" } /> };
4749
_ = ::yew::html! { <span { "hx-on:click" }={ "alert('Clicked!')" } { "hx-on:click" }={ "alert('Clicked!')" } /> };
4850

0 commit comments

Comments
 (0)