Skip to content

Commit a1abc2a

Browse files
committed
refactor: Simplify derive option parsing
1 parent 98edecd commit a1abc2a

File tree

2 files changed

+47
-75
lines changed

2 files changed

+47
-75
lines changed

sailfish-compiler/src/procmacro.rs

Lines changed: 44 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use proc_macro2::{Span, TokenStream};
2-
use quote::{quote, ToTokens};
2+
use quote::quote;
33
use std::collections::hash_map::DefaultHasher;
44
use std::env;
55
use std::hash::{Hash, Hasher};
66
use std::path::{Path, PathBuf};
7-
use syn::parse::{Parse, ParseStream, Result as ParseResult};
7+
use syn::parse::{ParseStream, Parser, Result as ParseResult};
88
use syn::punctuated::Punctuated;
99
use syn::{Fields, Ident, ItemStruct, LitBool, LitChar, LitStr, Token};
1010

@@ -15,88 +15,61 @@ use crate::error::*;
1515
// options for `template` attributes
1616
#[derive(Default)]
1717
struct DeriveTemplateOptions {
18+
found_keys: Vec<Ident>,
1819
path: Option<LitStr>,
1920
delimiter: Option<LitChar>,
2021
escape: Option<LitBool>,
2122
rm_whitespace: Option<LitBool>,
2223
type_: Option<LitStr>,
2324
}
2425

25-
impl Parse for DeriveTemplateOptions {
26-
fn parse(outer: ParseStream) -> ParseResult<Self> {
27-
let s;
28-
syn::parenthesized!(s in outer);
29-
30-
let mut options = Self::default();
31-
let mut found_keys = Vec::new();
32-
33-
while !s.is_empty() {
34-
let key = s.parse::<Ident>()?;
35-
s.parse::<Token![=]>()?;
36-
37-
// check if argument is repeated
38-
if found_keys.iter().any(|e| *e == key) {
39-
return Err(syn::Error::new(
40-
key.span(),
41-
format!("Argument `{}` was repeated.", key),
42-
));
43-
}
44-
45-
if key == "path" {
46-
options.path = Some(s.parse::<LitStr>()?);
47-
} else if key == "delimiter" {
48-
options.delimiter = Some(s.parse::<LitChar>()?);
49-
} else if key == "escape" {
50-
options.escape = Some(s.parse::<LitBool>()?);
51-
} else if key == "rm_whitespace" {
52-
options.rm_whitespace = Some(s.parse::<LitBool>()?);
53-
} else if key == "type" {
54-
options.type_ = Some(s.parse::<LitStr>()?);
55-
} else {
56-
return Err(syn::Error::new(
57-
key.span(),
58-
format!("Unknown option: `{}`", key),
59-
));
60-
}
61-
62-
found_keys.push(key);
26+
impl DeriveTemplateOptions {
27+
fn parser<'s>(&'s mut self) -> impl Parser + 's {
28+
move |outer: ParseStream| -> ParseResult<()> {
29+
let s;
30+
syn::parenthesized!(s in outer);
31+
32+
while !s.is_empty() {
33+
let key = s.parse::<Ident>()?;
34+
s.parse::<Token![=]>()?;
35+
36+
// check if argument is repeated
37+
if self.found_keys.iter().any(|e| *e == key) {
38+
return Err(syn::Error::new(
39+
key.span(),
40+
format!("Argument `{}` was repeated.", key),
41+
));
42+
}
6343

64-
// consume comma token
65-
if s.is_empty() {
66-
break;
67-
} else {
68-
s.parse::<Token![,]>()?;
69-
}
70-
}
44+
if key == "path" {
45+
self.path = Some(s.parse::<LitStr>()?);
46+
} else if key == "delimiter" {
47+
self.delimiter = Some(s.parse::<LitChar>()?);
48+
} else if key == "escape" {
49+
self.escape = Some(s.parse::<LitBool>()?);
50+
} else if key == "rm_whitespace" {
51+
self.rm_whitespace = Some(s.parse::<LitBool>()?);
52+
} else if key == "type" {
53+
self.type_ = Some(s.parse::<LitStr>()?);
54+
} else {
55+
return Err(syn::Error::new(
56+
key.span(),
57+
format!("Unknown option: `{}`", key),
58+
));
59+
}
7160

72-
Ok(options)
73-
}
74-
}
61+
self.found_keys.push(key);
7562

76-
impl DeriveTemplateOptions {
77-
fn merge(&mut self, other: DeriveTemplateOptions) -> Result<(), syn::Error> {
78-
fn merge_single<T: ToTokens>(
79-
lhs: &mut Option<T>,
80-
rhs: Option<T>,
81-
) -> Result<(), syn::Error> {
82-
if lhs.is_some() {
83-
if let Some(rhs) = rhs {
84-
Err(syn::Error::new_spanned(rhs, "keyword argument repeated."))
63+
// consume comma token
64+
if s.is_empty() {
65+
break;
8566
} else {
86-
Ok(())
67+
s.parse::<Token![,]>()?;
8768
}
88-
} else {
89-
*lhs = rhs;
90-
Ok(())
9169
}
92-
}
9370

94-
merge_single(&mut self.path, other.path)?;
95-
merge_single(&mut self.delimiter, other.delimiter)?;
96-
merge_single(&mut self.escape, other.escape)?;
97-
merge_single(&mut self.rm_whitespace, other.rm_whitespace)?;
98-
merge_single(&mut self.type_, other.type_)?;
99-
Ok(())
71+
Ok(())
72+
}
10073
}
10174
}
10275

@@ -170,8 +143,7 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
170143
let mut all_options = DeriveTemplateOptions::default();
171144
for attr in strct.attrs {
172145
if attr.path.is_ident("template") {
173-
let opt = syn::parse2::<DeriveTemplateOptions>(attr.tokens)?;
174-
all_options.merge(opt)?;
146+
syn::parse::Parser::parse2(all_options.parser(), attr.tokens)?;
175147
}
176148
}
177149

sailfish-tests/integration-tests/tests/fails/repeated_arguments.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: keyword argument repeated.
2-
--> $DIR/repeated_arguments.rs:6:21
1+
error: Argument `escape` was repeated.
2+
--> $DIR/repeated_arguments.rs:6:12
33
|
44
6 | #[template(escape = false)]
5-
| ^^^^^
5+
| ^^^^^^
66

77
error[E0599]: no method named `render_once` found for struct `InvalidOptionValue` in the current scope
88
--> $DIR/repeated_arguments.rs:12:69

0 commit comments

Comments
 (0)