Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend error message #960

Merged
merged 40 commits into from
Mar 5, 2020
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a0b2e96
Initial
captain-yossarian Feb 22, 2020
ad35203
formatting
captain-yossarian Feb 23, 2020
d41d466
wip
captain-yossarian Feb 23, 2020
5ca1685
add util fn
captain-yossarian Feb 23, 2020
0538813
message update
captain-yossarian Feb 23, 2020
166e4b4
refactor
captain-yossarian Feb 23, 2020
1516923
update error logic
captain-yossarian Feb 24, 2020
1cefeae
message refactor
captain-yossarian Feb 24, 2020
a056c89
remove debug flag
captain-yossarian Feb 24, 2020
e0b0684
update
captain-yossarian Feb 24, 2020
bc3b08c
add stderr
captain-yossarian Feb 25, 2020
69f0e0a
update condition
captain-yossarian Feb 26, 2020
8ea567c
CR fix
captain-yossarian Feb 27, 2020
81edf1b
remove empty line
captain-yossarian Feb 27, 2020
ae76a38
update
captain-yossarian Feb 27, 2020
3546d81
update error message
captain-yossarian Feb 27, 2020
b0cef02
wip
captain-yossarian Feb 27, 2020
9c69526
work
captain-yossarian Feb 27, 2020
145393a
unchange clippy
captain-yossarian Feb 27, 2020
6f307f9
update
captain-yossarian Feb 27, 2020
f7681e5
remove logs
captain-yossarian Feb 27, 2020
990437e
println
captain-yossarian Feb 27, 2020
f421174
format
captain-yossarian Feb 27, 2020
23bdf72
clippy
captain-yossarian Feb 27, 2020
62bb054
VERY WIP
captain-yossarian Feb 27, 2020
8fdbc69
error collect
captain-yossarian Feb 28, 2020
bf42a1a
refactor
captain-yossarian Feb 28, 2020
b9b2a6d
add second argument
captain-yossarian Feb 28, 2020
ee82df4
format
captain-yossarian Feb 28, 2020
6ee8610
wip
captain-yossarian Feb 29, 2020
0ff9648
wip with main parsser
captain-yossarian Feb 29, 2020
53bd8ce
wip
captain-yossarian Mar 1, 2020
a619b5b
receive unexpected error
captain-yossarian Mar 1, 2020
8a4f067
Fix with props checks
jstarry Mar 3, 2020
b55d572
merge
captain-yossarian Mar 4, 2020
748c62d
remove comments
captain-yossarian Mar 4, 2020
b8c8a38
update
captain-yossarian Mar 4, 2020
5b64ce8
format
captain-yossarian Mar 5, 2020
114cb16
Merge remote-tracking branch 'origin' into issue/664
captain-yossarian Mar 5, 2020
7fd2382
Fix test
jstarry Mar 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 => {
jstarry marked this conversation as resolved.
Show resolved Hide resolved
*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)
}
}
26 changes: 13 additions & 13 deletions src/virtual_dom/vcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,29 +323,29 @@ mod tests {

#[test]
fn set_properties_to_component() {
html! {
<Comp />
};
// html! {
// <Comp />
// };

html! {
<Comp field_1=1 />
};
// html! {
// <Comp field_1=1 />
// };

html! {
<Comp field_2=2 />
};
// html! {
// <Comp field_2=2 />
// };

html! {
<Comp field_1=1 field_2=2 />
};
// html! {
// <Comp field_1=1 field_2=2 />
// };

let props = Props {
field_1: 1,
field_2: 1,
};

html! {
<Comp with props />
<Comp ref=::yew::html::NodeRef::default() with props />
};
}
}
9 changes: 8 additions & 1 deletion tests/macro/html-component-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,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