rstml/node/node_value.rs
1//!
2//! Node value type
3use std::convert::TryFrom;
4
5use proc_macro2::TokenStream;
6use syn::{token::Brace, Block};
7
8#[derive(Clone, Debug, syn_derive::ToTokens, syn_derive::Parse)]
9pub struct InvalidBlock {
10 #[syn(braced)]
11 pub brace: Brace,
12 #[syn(in = brace)]
13 pub body: TokenStream,
14}
15
16/// Block node.
17///
18/// Arbitrary rust code in braced `{}` blocks.
19#[derive(Clone, Debug, syn_derive::ToTokens)]
20pub enum NodeBlock {
21 /// The block value..
22 ValidBlock(Block),
23
24 Invalid(InvalidBlock),
25}
26
27impl NodeBlock {
28 ///
29 /// Returns syntactically valid `syn::Block` of Rust code.
30 ///
31 /// Usually to make macro expansion IDE friendly, its better to use
32 /// `ToTokens` instead. Because it also emit blocks that is invalid for
33 /// syn, but valid for rust and rust analyzer. But if you need early
34 /// checks that this block is valid - use this method.
35 ///
36 /// Example of blocks that will or will not parse:
37 /// ```no_compile
38 /// {x.} // Rust will parse this syntax, but for syn this is invalid Block, because after dot ident is expected.
39 /// // Emiting code like this for rust analyzer allows it to find completion.
40 /// // This block is parsed as NodeBlock::Invalid
41 /// {]} // this is invalid syntax for rust compiler and rust analyzer so it will not be parsed at all.
42 /// {x + y} // Valid syn Block, parsed as NodeBlock::Valid
43 /// ```
44 pub fn try_block(&self) -> Option<&Block> {
45 match self {
46 Self::ValidBlock(b) => Some(b),
47 Self::Invalid(_) => None,
48 }
49 }
50}
51
52impl TryFrom<NodeBlock> for Block {
53 type Error = syn::Error;
54 fn try_from(v: NodeBlock) -> Result<Block, Self::Error> {
55 match v {
56 NodeBlock::ValidBlock(v) => Ok(v),
57 NodeBlock::Invalid(_) => Err(syn::Error::new_spanned(
58 v,
59 "Cant parse expression as block.",
60 )),
61 }
62 }
63}