Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
captain-yossarian committed Mar 4, 2020
2 parents 96b224e + 41601e4 commit e12364a
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 158 deletions.
162 changes: 81 additions & 81 deletions crates/macro/src/html_tree/html_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,17 +340,22 @@ impl ToTokens for HtmlComponentClose {
}
}

enum PropType {
List,
With,
}

enum Props {
List(Box<ListProps>),
With(Box<WithProps>),
None,
}

struct ListProps {
props: Vec<HtmlProp>,
node_ref: Option<Expr>,
}

struct WithProps {
props: Ident,
node_ref: Option<Expr>,
}

impl Props {
fn node_ref(&self) -> Option<&Expr> {
match self {
Expand All @@ -359,102 +364,97 @@ impl Props {
Props::None => None,
}
}
}

impl PeekValue<PropType> for Props {
fn peek(cursor: Cursor) -> Option<PropType> {
let (ident, _) = cursor.ident()?;
let prop_type = if ident == "with" {
PropType::With
} else {
PropType::List
};

Some(prop_type)
fn collision_message() -> &'static str {
"Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop"
}
}

impl Parse for Props {
fn parse(input: ParseStream) -> ParseResult<Self> {
match Props::peek(input.cursor()) {
Some(PropType::List) => input.parse().map(|l| Props::List(Box::new(l))),
Some(PropType::With) => input.parse().map(|w| Props::With(Box::new(w))),
None => Ok(Props::None),
}
}
}

struct ListProps {
props: Vec<HtmlProp>,
node_ref: Option<Expr>,
}
let mut props = Props::None;
let mut node_ref: Option<Expr> = None;

while let Some((token, _)) = input.cursor().ident() {
if token == "with" {
match props {
Props::None => Ok(()),
Props::With(_) => Err(input.error("too many `with` tokens used")),
Props::List(_) => Err(syn::Error::new_spanned(&token, Props::collision_message())),
}?;

input.parse::<Ident>()?;
props = Props::With(Box::new(WithProps {
props: input.parse::<Ident>()?,
node_ref: None,
}));

// Handle optional comma
let _ = input.parse::<Token![,]>();
continue;
}

impl Parse for ListProps {
fn parse(input: ParseStream) -> ParseResult<Self> {
let mut props: Vec<HtmlProp> = Vec::new();
while HtmlProp::peek(input.cursor()).is_some() {
props.push(input.parse::<HtmlProp>()?);
}
if (HtmlProp::peek(input.cursor())).is_none() {
break;
}

let ref_position = props.iter().position(|p| p.label.to_string() == "ref");
let node_ref = ref_position.map(|i| props.remove(i).value);
for prop in &props {
let prop = input.parse::<HtmlProp>()?;
if prop.label.to_string() == "ref" {
return Err(syn::Error::new_spanned(&prop.label, "too many refs set"));
match node_ref {
None => Ok(()),
Some(_) => Err(syn::Error::new_spanned(&prop.label, "too many refs set")),
}?;

node_ref = Some(prop.value);
continue;
}

if prop.label.to_string() == "type" {
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
}

if !prop.label.extended.is_empty() {
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
}
}

// alphabetize
props.sort_by(|a, b| {
if a.label == b.label {
Ordering::Equal
} else if a.label.to_string() == "children" {
Ordering::Greater
} else if b.label.to_string() == "children" {
Ordering::Less
} else {
a.label
.to_string()
.partial_cmp(&b.label.to_string())
.unwrap()
}
});

Ok(ListProps { props, node_ref })
}
}

struct WithProps {
props: Ident,
node_ref: Option<Expr>,
}

impl Parse for WithProps {
fn parse(input: ParseStream) -> ParseResult<Self> {
let with = input.parse::<Ident>()?;
if with != "with" {
return Err(input.error("expected to find `with` token"));
match props {
ref mut props @ Props::None => {
*props = Props::List(Box::new(ListProps {
props: vec![prop],
node_ref: None,
}));
},
Props::With(_) => return Err(syn::Error::new_spanned(&token, Props::collision_message())),
Props::List(ref mut list) => {
list.props.push(prop);
}
};
}
let props = input.parse::<Ident>()?;
let _ = input.parse::<Token![,]>();

// Check for the ref tag after `with`
let mut node_ref = None;
if let Some(ident) = input.cursor().ident() {
let prop = input.parse::<HtmlProp>()?;
if ident.0 == "ref" {
node_ref = Some(prop.value);
} else {
return Err(syn::Error::new_spanned(&prop.label, "unexpected token"));
match props {
Props::None => {},
Props::With(ref mut p) => p.node_ref = node_ref,
Props::List(ref mut p) => {
p.node_ref = node_ref;

// alphabetize
p.props.sort_by(|a, b| {
if a.label == b.label {
Ordering::Equal
} else if a.label.to_string() == "children" {
Ordering::Greater
} else if b.label.to_string() == "children" {
Ordering::Less
} else {
a.label
.to_string()
.partial_cmp(&b.label.to_string())
.unwrap()
}
});
}
}
};

Ok(WithProps { props, node_ref })
Ok(props)
}
}
9 changes: 8 additions & 1 deletion crates/macro/tests/macro/html-component-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,16 @@ fn compile_fail() {
html! { <Child props /> };
html! { <Child with props > };
html! { <Child with props ref=() ref=() /> };
html! { <Child ref=() with props /> };
html! { <Child with props ref=() ref=() value=1 /> };
html! { <Child with props ref=() value=1 ref=() /> };
html! { <Child with props value=1 ref=() ref=() /> };
html! { <Child value=1 with props ref=() ref=() /> };
html! { <Child value=1 ref=() with props ref=() /> };
html! { <Child ref=() ref=() value=1 with props /> };
html! { <Child with blah /> };
html! { <Child with props () /> };
html! { <Child value=1 with props /> };
html! { <Child with props value=1 /> };
html! { <Child type=0 /> };
html! { <Child invalid-prop-name=0 /> };
html! { <Child unknown="unknown" /> };
Expand Down
Loading

0 comments on commit e12364a

Please sign in to comment.