Skip to content

Commit 1907a7a

Browse files
committed
Address review
1 parent 711fcfd commit 1907a7a

File tree

12 files changed

+145
-95
lines changed

12 files changed

+145
-95
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ syn = { version = "2.0.15", features = ["full", "parsing", "extra-traits"] }
2121
thiserror = "1.0.37"
2222
syn_derive = "0.1.6"
2323
proc-macro2-diagnostics = { version = "0.10", default-features = false }
24+
derive-where = "1.2.5"
2425

2526
[dev-dependencies]
2627
proc-macro2 = {version = "1.0.47", features = ["span-locations"]}

benches/bench.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,29 @@ fn criterion_benchmark(c: &mut Criterion) {
2222

2323
c.bench_function("rstml::parse2(simple)", |b| {
2424
b.iter(|| {
25-
let config = rstml::ParserConfig::new().always_self_closed_elements(
26-
vec![
27-
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta",
28-
"param", "source", "track", "wbr",
29-
]
30-
.into_iter()
31-
.collect(),
32-
);
25+
let config: rstml::ParserConfig = rstml::ParserConfig::new()
26+
.always_self_closed_elements(
27+
vec![
28+
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta",
29+
"param", "source", "track", "wbr",
30+
]
31+
.into_iter()
32+
.collect(),
33+
);
3334
rstml::Parser::new(config).parse_simple(tokens.clone())
3435
})
3536
});
3637
c.bench_function("rstml::parse2(rust_site)", |b| {
3738
b.iter(|| {
38-
let config = rstml::ParserConfig::new().always_self_closed_elements(
39-
vec![
40-
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta",
41-
"param", "source", "track", "wbr",
42-
]
43-
.into_iter()
44-
.collect(),
45-
);
39+
let config: rstml::ParserConfig = rstml::ParserConfig::new()
40+
.always_self_closed_elements(
41+
vec![
42+
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta",
43+
"param", "source", "track", "wbr",
44+
]
45+
.into_iter()
46+
.collect(),
47+
);
4648
rstml::Parser::new(config).parse_simple(tokens.clone())
4749
})
4850
});

src/config.rs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
use std::{collections::HashSet, fmt::Debug, rc::Rc};
1+
use std::{collections::HashSet, convert::Infallible, fmt::Debug, marker::PhantomData, rc::Rc};
22

3+
use derive_where::derive_where;
34
use proc_macro2::TokenStream;
45
use syn::{parse::ParseStream, Result};
56

67
use crate::{
78
atoms::{CloseTag, OpenTag},
8-
node::NodeType,
9+
node::{CustomNode, NodeType},
910
};
1011

1112
pub type TransformBlockFn = dyn Fn(ParseStream) -> Result<Option<TokenStream>>;
1213
pub type ElementWildcardFn = dyn Fn(&OpenTag, &CloseTag) -> bool;
1314

1415
/// Configures the `Parser` behavior
15-
#[derive(Default, Clone)]
16-
pub struct ParserConfig {
16+
#[derive_where(Clone)]
17+
pub struct ParserConfig<C = Infallible> {
1718
pub(crate) flat_tree: bool,
1819
pub(crate) number_of_top_level_nodes: Option<usize>,
1920
pub(crate) type_of_top_level_nodes: Option<NodeType>,
@@ -22,9 +23,26 @@ pub struct ParserConfig {
2223
pub(crate) always_self_closed_elements: HashSet<&'static str>,
2324
pub(crate) raw_text_elements: HashSet<&'static str>,
2425
pub(crate) element_close_wildcard: Option<Rc<ElementWildcardFn>>,
26+
custom_node: PhantomData<C>,
2527
}
2628

27-
impl Debug for ParserConfig {
29+
impl Default for ParserConfig {
30+
fn default() -> Self {
31+
Self {
32+
flat_tree: Default::default(),
33+
number_of_top_level_nodes: Default::default(),
34+
type_of_top_level_nodes: Default::default(),
35+
transform_block: Default::default(),
36+
recover_block: Default::default(),
37+
always_self_closed_elements: Default::default(),
38+
raw_text_elements: Default::default(),
39+
element_close_wildcard: Default::default(),
40+
custom_node: Default::default(),
41+
}
42+
}
43+
}
44+
45+
impl<C> Debug for ParserConfig<C> {
2846
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2947
f.debug_struct("ParserConfig")
3048
.field("flat_tree", &self.flat_tree)
@@ -49,7 +67,9 @@ impl ParserConfig {
4967
pub fn new() -> ParserConfig {
5068
ParserConfig::default()
5169
}
70+
}
5271

72+
impl<C> ParserConfig<C> {
5373
/// Return flat tree instead of nested tree
5474
pub fn flat_tree(mut self) -> Self {
5575
self.flat_tree = true;
@@ -188,4 +208,20 @@ impl ParserConfig {
188208
close_tag.name.is_wildcard() && (!open_tag_should_be_block || open_tag.name.is_block())
189209
})
190210
}
211+
212+
/// Enables parsing for [`Node::Custom`] using a type implementing
213+
/// [`CustomNode`].
214+
pub fn custom_node<CN: CustomNode>(self) -> ParserConfig<CN> {
215+
ParserConfig {
216+
flat_tree: self.flat_tree,
217+
number_of_top_level_nodes: self.number_of_top_level_nodes,
218+
type_of_top_level_nodes: self.type_of_top_level_nodes,
219+
transform_block: self.transform_block,
220+
recover_block: self.recover_block,
221+
always_self_closed_elements: self.always_self_closed_elements,
222+
raw_text_elements: self.raw_text_elements,
223+
element_close_wildcard: self.element_close_wildcard,
224+
custom_node: Default::default(),
225+
}
226+
}
191227
}

src/lib.rs

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,9 @@
176176
//! nodes that was parsed.
177177
//!
178178
//! ```rust
179-
//! # use std::convert::Infallible;
180179
//! # use quote::quote;
181180
//! # use rstml::{Parser, ParserConfig};
182-
//! # Parser::new(ParserConfig::default()).parse_recoverable::<Infallible>(quote! {
181+
//! # Parser::new(ParserConfig::default()).parse_recoverable(quote! {
183182
//! <div hello={world.} /> <!-- dot after world is invalid syn expression -->
184183
//! <>
185184
//! <div>"1"</x> <!-- incorrect closed tag -->
@@ -246,31 +245,17 @@ pub fn parse(tokens: proc_macro::TokenStream) -> Result<Vec<Node>> {
246245
Parser::new(ParserConfig::default()).parse_simple(tokens)
247246
}
248247

249-
/// Parse the given [`proc-macro2::TokenStream`] or
250-
/// [`proc-macro::TokenStream`] into a [`Node`] tree with the [`CustomNode`]
251-
/// C.
252-
///
253-
/// [`proc-macro2::TokenStream`]: https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html
254-
/// [`proc-macro::TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
255-
/// [`CustomNode`]: trait.CustomNode.html
256-
/// [`Node`]: struct.Node.html
257-
pub fn parse_custom<C: CustomNode>(
258-
tokens: impl Into<proc_macro2::TokenStream>,
259-
) -> Result<Vec<Node<C>>> {
260-
Parser::new(ParserConfig::default()).parse_custom(tokens)
261-
}
262-
263248
/// Parse the given [`proc-macro::TokenStream`] into a [`Node`] tree with custom
264249
/// [`ParserConfig`].
265250
///
266251
/// [`proc-macro::TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
267252
/// [`Node`]: struct.Node.html
268253
/// [`ParserConfig`]: struct.ParserConfig.html
269254
#[deprecated(since = "0.10.2", note = "use rstml::Parser::parse_simple instead")]
270-
pub fn parse_with_config(
255+
pub fn parse_with_config<C: CustomNode>(
271256
tokens: proc_macro::TokenStream,
272-
config: ParserConfig,
273-
) -> Result<Vec<Node>> {
257+
config: ParserConfig<C>,
258+
) -> Result<Vec<Node<C>>> {
274259
Parser::new(config).parse_simple(tokens)
275260
}
276261
/// Parse the given [`proc-macro2::TokenStream`] into a [`Node`] tree.
@@ -288,9 +273,9 @@ pub fn parse2(tokens: proc_macro2::TokenStream) -> Result<Vec<Node>> {
288273
/// [`Node`]: struct.Node.html
289274
/// [`ParserConfig`]: struct.ParserConfig.html
290275
#[deprecated(since = "0.10.2", note = "use rstml::Parser::parse_simple instead")]
291-
pub fn parse2_with_config(
276+
pub fn parse2_with_config<C: CustomNode>(
292277
tokens: proc_macro2::TokenStream,
293-
config: ParserConfig,
294-
) -> Result<Vec<Node>> {
278+
config: ParserConfig<C>,
279+
) -> Result<Vec<Node<C>>> {
295280
Parser::new(config).parse_simple(tokens)
296281
}

src/node/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,20 @@ fn path_to_string(expr: &ExprPath) -> String {
203203
}
204204

205205
pub trait CustomNode: Sized {
206+
/// Should correspond to [`ToTokens::to_tokens`].
207+
///
208+
/// [`ToTokens::to_tokens`]: quote::ToTokens::to_tokens
206209
fn to_tokens(&self, tokens: &mut TokenStream);
210+
/// Peeks the token stream to decide whether this node should be parsed.
211+
///
212+
/// Recieves a [`ParseStream::fork`].
213+
///
214+
/// [`ParseStream::fork`]: syn::parse::ParseBuffer::fork
207215
fn peek_element(input: ParseStream) -> bool;
216+
/// Parses the custom node, only called when [`peek_element`] returns
217+
/// `true`.
218+
///
219+
/// [`peek_element`]: Self::peek_element
208220
fn parse_element(parser: &mut RecoverableContext, input: ParseStream) -> Option<Self>;
209221
}
210222

src/node/parse.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ impl ParseRecoverable for NodeDoctype {
114114
}
115115

116116
impl OpenTag {
117+
/// Parses the opening `<` of an open tag, providing handling of the
118+
/// unexpected `</` of a close tag.
119+
///
120+
/// **Note:** This is an internal function exported to make parsing of
121+
/// custom nodes easier. It is not considered stable.
117122
pub fn parse_start_tag(
118123
parser: &mut RecoverableContext,
119124
input: ParseStream,
@@ -160,6 +165,13 @@ impl ParseRecoverable for OpenTag {
160165
}
161166

162167
impl<C: CustomNode> NodeElement<C> {
168+
/// Parses the children of a node, stopping at the first matching closing
169+
/// tag, following the behavior specified in the [`ParserConfig`].
170+
///
171+
/// **Note:** This is an internal function exported to make parsing of
172+
/// custom nodes easier. It is not considered stable.
173+
///
174+
/// [`ParserConfig`]: crate::ParserConfig
163175
pub fn parse_children(
164176
parser: &mut RecoverableContext,
165177
input: ParseStream,
@@ -213,7 +225,7 @@ impl<C: CustomNode> NodeElement<C> {
213225

214226
if close_tag.name != open_tag.name {
215227
match parser.config().element_close_wildcard.as_deref() {
216-
Some(is_wildcard) if is_wildcard(&open_tag, &close_tag) => {}
228+
Some(is_wildcard) if is_wildcard(open_tag, &close_tag) => {}
217229
_ => {
218230
let diagnostic = Diagnostic::spanned(
219231
close_tag.span(),
@@ -275,7 +287,7 @@ impl<C: CustomNode> ParseRecoverable for NodeElement<C> {
275287

276288
impl<C: CustomNode> ParseRecoverable for Node<C> {
277289
fn parse_recoverable(parser: &mut RecoverableContext, input: ParseStream) -> Option<Self> {
278-
let node = if C::peek_element(input) {
290+
let node = if C::peek_element(&input.fork()) {
279291
Node::Custom(C::parse_element(parser, input)?)
280292
} else if input.peek(Token![<]) {
281293
if input.peek2(Token![!]) {

src/node/parser_ext.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ impl RecoverableContext {
77
/// Like [`parse_simple`], but splits the tokenstream at `E` first only
88
/// parsing the tokens before it as `T`.
99
///
10+
/// **Note:** This is an internal function exported to make parsing of
11+
/// custom nodes easier. It has some quirks, e.g.,
12+
/// `parse_simple_with_ending<Expr, Token![>]>`, would not support any
13+
/// [`Expr`] containing a `>`.
14+
///
15+
/// It is not considered stable.
16+
///
1017
/// [`parse_simple`]: #method.parse_simple
18+
/// [`Expr`]: https://docs.rs/syn/latest/syn/enum.Expr.html
1119
pub fn parse_simple_with_ending<T: Parse, E: Parse>(
1220
&mut self,
1321
input: ParseStream,

src/parser/mod.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! RSX Parser
22
3-
use std::{convert::Infallible, vec};
3+
use std::vec;
44

55
use proc_macro2::TokenStream;
66
use proc_macro2_diagnostics::Diagnostic;
@@ -17,13 +17,13 @@ use crate::{node::*, ParserConfig};
1717
/// Allows customization through `ParserConfig`.
1818
/// Support recovery after parsing invalid token.
1919
20-
pub struct Parser {
21-
config: ParserConfig,
20+
pub struct Parser<C> {
21+
config: ParserConfig<C>,
2222
}
2323

24-
impl Parser {
24+
impl<C: CustomNode> Parser<C> {
2525
/// Create a new parser with the given [`ParserConfig`].
26-
pub fn new(config: ParserConfig) -> Parser {
26+
pub fn new(config: ParserConfig<C>) -> Self {
2727
Parser { config }
2828
}
2929

@@ -33,38 +33,21 @@ impl Parser {
3333
/// [`proc-macro2::TokenStream`]: https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html
3434
/// [`proc-macro::TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
3535
/// [`Node`]: struct.Node.html
36-
pub fn parse_simple(&self, v: impl Into<TokenStream>) -> Result<Vec<Node<Infallible>>> {
37-
self.parse_recoverable(v).into_result()
38-
}
39-
40-
/// Parse the given [`proc-macro2::TokenStream`] or
41-
/// [`proc-macro::TokenStream`] into a [`Node`] tree with the [`CustomNode`]
42-
/// C.
43-
///
44-
/// [`proc-macro2::TokenStream`]: https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html
45-
/// [`proc-macro::TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
46-
/// [`Node`]: struct.Node.html
47-
pub fn parse_custom<C: CustomNode>(&self, v: impl Into<TokenStream>) -> Result<Vec<Node<C>>> {
36+
pub fn parse_simple(&self, v: impl Into<TokenStream>) -> Result<Vec<Node<C>>> {
4837
self.parse_recoverable(v).into_result()
4938
}
5039

5140
/// Advance version of `parse_simple` that returns array of errors in case
5241
/// of partial parsing.
53-
pub fn parse_recoverable<C: CustomNode>(
54-
&self,
55-
v: impl Into<TokenStream>,
56-
) -> ParsingResult<Vec<Node<C>>> {
42+
pub fn parse_recoverable(&self, v: impl Into<TokenStream>) -> ParsingResult<Vec<Node<C>>> {
5743
use syn::parse::Parser as _;
5844
let parser = move |input: ParseStream| Ok(self.parse_syn_stream(input));
5945
let res = parser.parse2(v.into());
6046
res.expect("No errors from parser")
6147
}
6248

6349
/// Parse a given [`ParseStream`].
64-
pub fn parse_syn_stream<C: CustomNode>(
65-
&self,
66-
input: ParseStream,
67-
) -> ParsingResult<Vec<Node<C>>> {
50+
pub fn parse_syn_stream(&self, input: ParseStream) -> ParsingResult<Vec<Node<C>>> {
6851
let mut nodes = vec![];
6952
let mut top_level_nodes = 0;
7053

src/parser/recoverable.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
//!
1111
//! Example:
1212
//! ```rust
13-
//! # use std::convert::Infallible;
1413
//! # use quote::quote;
1514
//! # use rstml::{Parser, ParserConfig};
16-
//! # Parser::new(ParserConfig::default()).parse_recoverable::<Infallible>(quote! {
15+
//! # Parser::new(ParserConfig::default()).parse_recoverable(quote! {
1716
//! <div hello={world.} /> // dot after world is invalid syn::Expr
1817
//! <>
1918
//! <div>"1"</x> // incorrect closed tag
@@ -220,8 +219,8 @@ impl<T> From<syn::Result<T>> for ParsingResult<T> {
220219
}
221220
}
222221

223-
impl From<crate::ParserConfig> for RecoveryConfig {
224-
fn from(config: ParserConfig) -> Self {
222+
impl<C> From<ParserConfig<C>> for RecoveryConfig {
223+
fn from(config: ParserConfig<C>) -> Self {
225224
RecoveryConfig {
226225
recover_block: config.recover_block,
227226
raw_text_elements: config.raw_text_elements.clone(),

0 commit comments

Comments
 (0)