Skip to content

Commit 41601e4

Browse files
Merge pull request #1 from jstarry/pr-960
Fix with props checks
2 parents a619b5b + 8a4f067 commit 41601e4

File tree

4 files changed

+146
-230
lines changed

4 files changed

+146
-230
lines changed

crates/macro/src/html_tree/html_component.rs

+83-163
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use boolinator::Boolinator;
66
use proc_macro2::Span;
77
use quote::{quote, quote_spanned, ToTokens};
88
use std::cmp::Ordering;
9-
use std::collections::HashMap;
109
use syn::buffer::Cursor;
1110
use syn::parse;
1211
use syn::parse::{Parse, ParseStream, Result as ParseResult};
@@ -341,17 +340,22 @@ impl ToTokens for HtmlComponentClose {
341340
}
342341
}
343342

344-
enum PropType {
345-
List,
346-
With,
347-
}
348-
349343
enum Props {
350344
List(Box<ListProps>),
351345
With(Box<WithProps>),
352346
None,
353347
}
354348

349+
struct ListProps {
350+
props: Vec<HtmlProp>,
351+
node_ref: Option<Expr>,
352+
}
353+
354+
struct WithProps {
355+
props: Ident,
356+
node_ref: Option<Expr>,
357+
}
358+
355359
impl Props {
356360
fn node_ref(&self) -> Option<&Expr> {
357361
match self {
@@ -360,181 +364,97 @@ impl Props {
360364
Props::None => None,
361365
}
362366
}
367+
363368
fn collision_message() -> &'static str {
364369
"Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop"
365370
}
371+
}
366372

367-
fn collect_props(input: ParseStream) -> ParseResult<Vec<HtmlProp>> {
368-
let mut props: Vec<HtmlProp> = Vec::new();
369-
while HtmlProp::peek(input.cursor()).is_some() {
370-
props.push(input.parse::<HtmlProp>()?);
371-
}
372-
Ok(props)
373-
}
374-
375-
fn remove_refs(mut props: Vec<HtmlProp>) -> ListProps {
376-
let ref_position = props.iter().position(|p| p.label.to_string() == "ref");
377-
let node_ref = ref_position.map(|i| props.remove(i).value);
378-
ListProps { props, node_ref }
379-
}
380-
381-
fn return_type(input: ParseStream) -> ParseResult<PropType> {
382-
let stream = input.fork();
383-
let mut is_with = false;
384-
let mut ref_counter = 0;
385-
let mut props_counter = 0;
373+
impl Parse for Props {
374+
fn parse(input: ParseStream) -> ParseResult<Self> {
375+
let mut props = Props::None;
376+
let mut node_ref: Option<Expr> = None;
386377

387-
while let Some((token, _)) = stream.cursor().ident() {
388-
let prop_value = stream.parse::<HtmlProp>();
378+
while let Some((token, _)) = input.cursor().ident() {
389379
if token == "with" {
390-
is_with = true;
391-
}
392-
if is_with && (props_counter - ref_counter) > 0 {
393-
return Err(syn::Error::new_spanned(&token, Props::collision_message()));
380+
match props {
381+
Props::None => Ok(()),
382+
Props::With(_) => Err(input.error("too many `with` tokens used")),
383+
Props::List(_) => Err(syn::Error::new_spanned(&token, Props::collision_message())),
384+
}?;
385+
386+
input.parse::<Ident>()?;
387+
props = Props::With(Box::new(WithProps {
388+
props: input.parse::<Ident>()?,
389+
node_ref: None,
390+
}));
391+
392+
// Handle optional comma
393+
let _ = input.parse::<Token![,]>();
394+
continue;
394395
}
395-
if let Ok(prop) = prop_value {
396-
if prop.label.to_string() == "ref" {
397-
ref_counter = ref_counter + 1;
398-
if ref_counter > 1 {
399-
return Err(syn::Error::new_spanned(&prop.label, "too many refs set"));
400-
}
401-
} else {
402-
if is_with {
403-
return Err(syn::Error::new_spanned(&token, Props::collision_message()));
404-
}
405-
}
406-
if prop.label.to_string() == "type" {
407-
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
408-
}
409-
if !prop.label.extended.is_empty() {
410-
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
411-
}
412-
props_counter = props_counter + 1;
413-
}
414-
}
415-
if is_with {
416-
Ok(PropType::With)
417-
} else {
418-
Ok(PropType::List)
419-
}
420-
}
421-
422-
fn get_type(input: ParseStream) -> ParseResult<Option<PropType>> {
423-
let props_type = match Props::return_type(input) {
424-
Ok(props) => Some(props),
425-
Err(_) => None,
426-
};
427-
428-
match props_type {
429-
Some(PropType::With) => println!("props_type::with"),
430-
Some(PropType::List) => println!("props_type::list"),
431-
None => println!("none"),
432-
}
433-
let _ = Props::collect_props(input)?;
434-
435-
println!("RETURN TYPE");
436-
Ok(props_type)
437-
}
438-
}
439-
440-
impl PeekValue<PropType> for Props {
441-
fn peek(cursor: Cursor) -> Option<PropType> {
442-
let (ident, _) = cursor.ident()?;
443396

444-
let prop_type = if ident == "with" {
445-
PropType::With
446-
} else {
447-
PropType::List
448-
};
449-
450-
Some(prop_type)
451-
}
452-
}
453-
454-
impl Parse for Props {
455-
fn parse(input: ParseStream) -> ParseResult<Self> {
456-
match Props::get_type(input)? {
457-
Some(PropType::List) => input.parse().map(|l| Props::List(Box::new(l))),
458-
Some(PropType::With) => {
459-
let result = input.parse().map(|w| Props::With(Box::new(w)));
460-
match result {
461-
Ok(v) => Ok(v),
462-
Err(e) => {
463-
println!("MY ERROR");
464-
return Err(input.error("MY"));
465-
}
466-
}
397+
if (HtmlProp::peek(input.cursor())).is_none() {
398+
break;
467399
}
468-
None => Ok(Props::None),
469-
}
470-
}
471-
}
472400

473-
struct ListProps {
474-
props: Vec<HtmlProp>,
475-
node_ref: Option<Expr>,
476-
}
401+
let prop = input.parse::<HtmlProp>()?;
402+
if prop.label.to_string() == "ref" {
403+
match node_ref {
404+
None => Ok(()),
405+
Some(_) => Err(syn::Error::new_spanned(&prop.label, "too many refs set")),
406+
}?;
477407

478-
impl Parse for ListProps {
479-
fn parse(input: ParseStream) -> ParseResult<Self> {
480-
let ListProps {
481-
mut props,
482-
node_ref,
483-
} = Props::remove_refs(Props::collect_props(input)?);
484-
485-
// alphabetize
486-
props.sort_by(|a, b| {
487-
if a.label == b.label {
488-
Ordering::Equal
489-
} else if a.label.to_string() == "children" {
490-
Ordering::Greater
491-
} else if b.label.to_string() == "children" {
492-
Ordering::Less
493-
} else {
494-
a.label
495-
.to_string()
496-
.partial_cmp(&b.label.to_string())
497-
.unwrap()
408+
node_ref = Some(prop.value);
409+
continue;
498410
}
499-
});
500411

501-
Ok(ListProps { props, node_ref })
502-
}
503-
}
412+
if prop.label.to_string() == "type" {
413+
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
414+
}
504415

505-
struct WithProps {
506-
props: Ident,
507-
node_ref: Option<Expr>,
508-
}
416+
if !prop.label.extended.is_empty() {
417+
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
418+
}
509419

510-
impl Parse for WithProps {
511-
fn parse(input: ParseStream) -> ParseResult<Self> {
512-
let with = input.parse::<Ident>()?;
513-
if with != "with" {
514-
return Err(input.error("expected to find `with` token"));
420+
match props {
421+
ref mut props @ Props::None => {
422+
*props = Props::List(Box::new(ListProps {
423+
props: vec![prop],
424+
node_ref: None,
425+
}));
426+
},
427+
Props::With(_) => return Err(syn::Error::new_spanned(&token, Props::collision_message())),
428+
Props::List(ref mut list) => {
429+
list.props.push(prop);
430+
}
431+
};
515432
}
516-
let props = input.parse::<Ident>()?;
517-
518-
let _ = input.parse::<Token![,]>();
519-
520-
// Check for the ref tag after `with`
521-
let mut node_ref = None;
522433

523-
if input.cursor().ident().is_some() {
524-
let ListProps {
525-
node_ref: reference,
526-
..
527-
} = Props::remove_refs(Props::collect_props(input)?);
528-
node_ref = reference;
529-
530-
if let Some(ident) = input.cursor().ident() {
531-
let prop = input.parse::<HtmlProp>()?;
532-
if ident.0 == "ref" {
533-
node_ref = Some(prop.value);
534-
}
434+
match props {
435+
Props::None => {},
436+
Props::With(ref mut p) => p.node_ref = node_ref,
437+
Props::List(ref mut p) => {
438+
p.node_ref = node_ref;
439+
440+
// alphabetize
441+
p.props.sort_by(|a, b| {
442+
if a.label == b.label {
443+
Ordering::Equal
444+
} else if a.label.to_string() == "children" {
445+
Ordering::Greater
446+
} else if b.label.to_string() == "children" {
447+
Ordering::Less
448+
} else {
449+
a.label
450+
.to_string()
451+
.partial_cmp(&b.label.to_string())
452+
.unwrap()
453+
}
454+
});
535455
}
536-
}
456+
};
537457

538-
Ok(WithProps { props, node_ref })
458+
Ok(props)
539459
}
540460
}

tests/macro/html-component-fail.rs

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ fn compile_fail() {
6262
html! { <Child value=1 with props ref=() ref=() /> };
6363
html! { <Child value=1 ref=() with props ref=() /> };
6464
html! { <Child ref=() ref=() value=1 with props /> };
65-
html! { <Child ref=() with props /> };
6665
html! { <Child with blah /> };
6766
html! { <Child with props () /> };
6867
html! { <Child value=1 with props /> };

0 commit comments

Comments
 (0)