Skip to content

Commit f2be1ef

Browse files
authored
Merge 1907a7a into aa9d210
2 parents aa9d210 + 1907a7a commit f2be1ef

File tree

13 files changed

+359
-97
lines changed

13 files changed

+359
-97
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
});

examples/html-to-string-macro/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ fn walk_nodes<'a>(empty_elements: &HashSet<&str>, nodes: &'a Vec<Node>) -> WalkN
111111
out.static_format.push_str("{}");
112112
out.values.push(block.to_token_stream());
113113
}
114+
Node::Custom(custom) => match *custom {},
114115
}
115116
}
116117

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: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,17 @@
203203
//! configuration.
204204
//!
205205
//! One highlight with regards to customization is the [`transform_block`]
206-
//! configuration, which takes a closure that receives raw block content as
207-
//! `ParseStream` and lets you optionally convert it to a `TokenStream`. That
208-
//! makes it possible to have custom syntax in blocks. More details in [#9]
206+
//! configuration, which takes a closure that receives raw block content as
207+
//! `ParseStream` and lets you optionally convert it to a `TokenStream`. That
208+
//! makes it possible to have custom syntax in blocks. More details in [#9].
209209
//!
210+
//! Additionally, [`CustomNode`] can be used to implement fully custom
211+
//! parsing.
210212
//!
211213
//! [`syn`]: /syn
212214
//! [`TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
213215
//! [`Node`]: enum.Node.html
216+
//! [`CustomNode`]: trait.CustomNode.html
214217
//! [`Span::join`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.join
215218
//! [`Span::source_text`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.source_text
216219
//! [`ParserConfig`]: struct.ParserConfig.html
@@ -231,7 +234,7 @@ mod parser;
231234
pub use config::ParserConfig;
232235
pub use error::Error;
233236
pub use node::atoms;
234-
use node::Node;
237+
use node::{CustomNode, Node};
235238
pub use parser::{recoverable, recoverable::ParsingResult, Parser};
236239

237240
/// Parse the given [`proc-macro::TokenStream`] into a [`Node`] tree.
@@ -249,10 +252,10 @@ pub fn parse(tokens: proc_macro::TokenStream) -> Result<Vec<Node>> {
249252
/// [`Node`]: struct.Node.html
250253
/// [`ParserConfig`]: struct.ParserConfig.html
251254
#[deprecated(since = "0.10.2", note = "use rstml::Parser::parse_simple instead")]
252-
pub fn parse_with_config(
255+
pub fn parse_with_config<C: CustomNode>(
253256
tokens: proc_macro::TokenStream,
254-
config: ParserConfig,
255-
) -> Result<Vec<Node>> {
257+
config: ParserConfig<C>,
258+
) -> Result<Vec<Node<C>>> {
256259
Parser::new(config).parse_simple(tokens)
257260
}
258261
/// Parse the given [`proc-macro2::TokenStream`] into a [`Node`] tree.
@@ -270,9 +273,9 @@ pub fn parse2(tokens: proc_macro2::TokenStream) -> Result<Vec<Node>> {
270273
/// [`Node`]: struct.Node.html
271274
/// [`ParserConfig`]: struct.ParserConfig.html
272275
#[deprecated(since = "0.10.2", note = "use rstml::Parser::parse_simple instead")]
273-
pub fn parse2_with_config(
276+
pub fn parse2_with_config<C: CustomNode>(
274277
tokens: proc_macro2::TokenStream,
275-
config: ParserConfig,
276-
) -> Result<Vec<Node>> {
278+
config: ParserConfig<C>,
279+
) -> Result<Vec<Node<C>>> {
277280
Parser::new(config).parse_simple(tokens)
278281
}

src/node/mod.rs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
//! Tree of nodes.
22
3-
use std::fmt;
3+
use std::{convert::Infallible, fmt};
44

55
use atoms::{tokens, FragmentClose, FragmentOpen};
6-
use proc_macro2::Ident;
7-
use syn::{ExprPath, LitStr, Token};
6+
use proc_macro2::{Ident, TokenStream};
7+
use syn::{parse::ParseStream, ExprPath, LitStr, Token};
88

99
pub mod atoms;
1010
mod attribute;
1111
mod node_name;
1212
mod node_value;
1313
pub mod parse;
14+
mod parser_ext;
1415
mod raw_text;
1516

1617
pub use attribute::{
@@ -20,6 +21,7 @@ pub use node_name::{NodeName, NodeNameFragment};
2021
pub use node_value::NodeBlock;
2122

2223
pub use self::raw_text::RawText;
24+
use crate::recoverable::RecoverableContext;
2325

2426
/// Node types.
2527
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -31,6 +33,7 @@ pub enum NodeType {
3133
Block,
3234
Fragment,
3335
RawText,
36+
Custom,
3437
}
3538

3639
impl fmt::Display for NodeType {
@@ -46,24 +49,26 @@ impl fmt::Display for NodeType {
4649
Self::Doctype => "NodeType::Doctype",
4750
Self::Block => "NodeType::Block",
4851
Self::Fragment => "NodeType::Fragment",
52+
Self::Custom => "NodeType::Custom",
4953
}
5054
)
5155
}
5256
}
5357

5458
/// Node in the tree.
5559
#[derive(Clone, Debug, syn_derive::ToTokens)]
56-
pub enum Node {
60+
pub enum Node<C: CustomNode = Infallible> {
5761
Comment(NodeComment),
5862
Doctype(NodeDoctype),
59-
Fragment(NodeFragment),
60-
Element(NodeElement),
63+
Fragment(NodeFragment<C>),
64+
Element(NodeElement<C>),
6165
Block(NodeBlock),
6266
Text(NodeText),
6367
RawText(RawText),
68+
Custom(C),
6469
}
6570

66-
impl Node {
71+
impl<C: CustomNode> Node<C> {
6772
pub fn flatten(mut self) -> Vec<Self> {
6873
let children = self
6974
.children_mut()
@@ -86,11 +91,12 @@ impl Node {
8691
Self::Block(_) => NodeType::Block,
8792
Self::Fragment(_) => NodeType::Fragment,
8893
Self::RawText(_) => NodeType::RawText,
94+
Self::Custom(_) => NodeType::Custom,
8995
}
9096
}
9197

9298
/// Get node children.
93-
pub fn children(&self) -> Option<&Vec<Node>> {
99+
pub fn children(&self) -> Option<&Vec<Self>> {
94100
match self {
95101
Self::Fragment(NodeFragment { children, .. })
96102
| Self::Element(NodeElement { children, .. }) => Some(children),
@@ -99,7 +105,7 @@ impl Node {
99105
}
100106

101107
/// Get mutable node children.
102-
pub fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
108+
pub fn children_mut(&mut self) -> Option<&mut Vec<Self>> {
103109
match self {
104110
Self::Fragment(NodeFragment { children, .. })
105111
| Self::Element(NodeElement { children, .. }) => Some(children),
@@ -113,14 +119,14 @@ impl Node {
113119
/// A HTMLElement tag, with optional children and attributes.
114120
/// Potentially selfclosing. Any tag name is valid.
115121
#[derive(Clone, Debug, syn_derive::ToTokens)]
116-
pub struct NodeElement {
122+
pub struct NodeElement<C: CustomNode = Infallible> {
117123
pub open_tag: atoms::OpenTag,
118124
#[to_tokens(parse::to_tokens_array)]
119-
pub children: Vec<Node>,
125+
pub children: Vec<Node<C>>,
120126
pub close_tag: Option<atoms::CloseTag>,
121127
}
122128

123-
impl NodeElement {
129+
impl<C: CustomNode> NodeElement<C> {
124130
pub fn name(&self) -> &NodeName {
125131
&self.open_tag.name
126132
}
@@ -177,12 +183,12 @@ pub struct NodeDoctype {
177183
///
178184
/// Fragment: `<></>`
179185
#[derive(Clone, Debug, syn_derive::ToTokens)]
180-
pub struct NodeFragment {
186+
pub struct NodeFragment<C: CustomNode = Infallible> {
181187
/// Open fragment token
182188
pub tag_open: FragmentOpen,
183189
/// Children of the fragment node.
184190
#[to_tokens(parse::to_tokens_array)]
185-
pub children: Vec<Node>,
191+
pub children: Vec<Node<C>>,
186192
/// Close fragment token
187193
pub tag_close: Option<FragmentClose>,
188194
}
@@ -195,3 +201,35 @@ fn path_to_string(expr: &ExprPath) -> String {
195201
.collect::<Vec<String>>()
196202
.join("::")
197203
}
204+
205+
pub trait CustomNode: Sized {
206+
/// Should correspond to [`ToTokens::to_tokens`].
207+
///
208+
/// [`ToTokens::to_tokens`]: quote::ToTokens::to_tokens
209+
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
215+
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
220+
fn parse_element(parser: &mut RecoverableContext, input: ParseStream) -> Option<Self>;
221+
}
222+
223+
impl CustomNode for Infallible {
224+
fn to_tokens(&self, _tokens: &mut TokenStream) {
225+
match *self {}
226+
}
227+
228+
fn peek_element(_input: ParseStream) -> bool {
229+
false
230+
}
231+
232+
fn parse_element(_parser: &mut RecoverableContext, _input: ParseStream) -> Option<Self> {
233+
unreachable!("Infallible::peek_element returns false")
234+
}
235+
}

0 commit comments

Comments
 (0)