1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use crate::prelude::{
    Delimiter, Group, Ident, LexError, Literal, Punct, Result, Spacing, Span, TokenStream,
    TokenTree,
};
use std::str::FromStr;

/// A helper struct build around a [TokenStream] to make it easier to build code.
#[must_use]
#[derive(Default)]
pub struct StreamBuilder {
    pub(crate) stream: TokenStream,
}

impl StreamBuilder {
    /// Generate a new StreamBuilder
    pub fn new() -> Self {
        Self {
            stream: TokenStream::new(),
        }
    }

    /// Add multiple `TokenTree` items to the stream.
    pub fn extend(&mut self, item: impl IntoIterator<Item = TokenTree>) -> &mut Self {
        self.stream.extend(item);
        self
    }

    /// Append another StreamBuilder to the current StreamBuilder.
    pub fn append(&mut self, builder: StreamBuilder) -> &mut Self {
        self.stream.extend(builder.stream);
        self
    }

    /// Push a single token to the stream.
    pub fn push(&mut self, item: impl Into<TokenTree>) -> &mut Self {
        self.stream.extend([item.into()]);
        self
    }

    /// Attempt to parse the given string as valid Rust code, and append the parsed result to the internal stream.
    ///
    /// Currently panics if the string could not be parsed as valid Rust code.
    pub fn push_parsed(&mut self, item: impl AsRef<str>) -> Result<&mut Self> {
        let tokens = TokenStream::from_str(item.as_ref()).map_err(|e| PushParseError {
            error: e,
            code: item.as_ref().to_string(),
        })?;
        self.stream.extend(tokens);
        Ok(self)
    }

    /// Push a single ident to the stream. An ident is any word that a code file may contain, e.g. `fn`, `struct`, `where`, names of functions and structs, etc.
    pub fn ident(&mut self, ident: Ident) -> &mut Self {
        self.stream.extend([TokenTree::Ident(ident)]);
        self
    }

    /// Push a single ident to the stream. An ident is any word that a code file may contain, e.g. `fn`, `struct`, `where`, names of functions and structs, etc.
    pub fn ident_str(&mut self, ident: impl AsRef<str>) -> &mut Self {
        self.stream.extend([TokenTree::Ident(Ident::new(
            ident.as_ref(),
            Span::call_site(),
        ))]);
        self
    }

    /// Add a group. A group is any block surrounded by `{ .. }`, `[ .. ]` or `( .. )`.
    ///
    /// `delim` indicates which group it is. The `inner` callback is used to fill the contents of the group.
    pub fn group<FN>(&mut self, delim: Delimiter, inner: FN) -> crate::Result<&mut Self>
    where
        FN: FnOnce(&mut StreamBuilder) -> crate::Result<()>,
    {
        let mut stream = StreamBuilder::new();
        inner(&mut stream)?;
        self.stream
            .extend([TokenTree::Group(Group::new(delim, stream.stream))]);
        Ok(self)
    }

    /// Add a single punctuation to the stream. Puncts are single-character tokens like `.`, `<`, `#`, etc
    ///
    /// Note that this should not be used for multi-punct constructions like `::` or `->`. For that use [`puncts`] instead.
    ///
    /// [`puncts`]: #method.puncts
    pub fn punct(&mut self, p: char) -> &mut Self {
        self.stream
            .extend([TokenTree::Punct(Punct::new(p, Spacing::Alone))]);
        self
    }

    /// Add multiple punctuations to the stream. Multi punct tokens are e.g. `::`, `->` and `=>`.
    ///
    /// Note that this is the only way to add multi punct tokens.
    /// If you were to use [`Punct`] to insert `->` it would be inserted as `-` and then `>`, and not form a single token. Rust would interpret this as a "minus sign and then a greater than sign", not as a single arrow.
    pub fn puncts(&mut self, puncts: &str) -> &mut Self {
        self.stream.extend(
            puncts
                .chars()
                .map(|char| TokenTree::Punct(Punct::new(char, Spacing::Joint))),
        );
        self
    }

    /// Add a lifetime to the stream.
    ///
    /// Note that this is the only way to add lifetimes, if you were to do:
    /// ```ignore
    /// builder.punct('\'');
    /// builder.ident_str("static");
    /// ```
    /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work.
    pub fn lifetime(&mut self, lt: Ident) -> &mut Self {
        self.stream.extend([
            TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
            TokenTree::Ident(lt),
        ]);
        self
    }

    /// Add a lifetime to the stream.
    ///
    /// Note that this is the only way to add lifetimes, if you were to do:
    /// ```ignore
    /// builder.punct('\'');
    /// builder.ident_str("static");
    /// ```
    /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work.
    pub fn lifetime_str(&mut self, lt: &str) -> &mut Self {
        self.stream.extend([
            TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
            TokenTree::Ident(Ident::new(lt, Span::call_site())),
        ]);
        self
    }

    /// Add a literal string (`&'static str`) to the stream.
    pub fn lit_str(&mut self, str: impl AsRef<str>) -> &mut Self {
        self.stream
            .extend([TokenTree::Literal(Literal::string(str.as_ref()))]);
        self
    }

    /// Add an `usize` value to the stream.
    pub fn lit_usize(&mut self, val: usize) -> &mut Self {
        self.stream
            .extend([TokenTree::Literal(Literal::usize_unsuffixed(val))]);
        self
    }

    /// Set the given span on all tokens in the stream. This span is used by rust for e.g. compiler errors, to indicate the position of the error.
    pub fn set_span_on_all_tokens(&mut self, span: Span) {
        self.stream = std::mem::take(&mut self.stream)
            .into_iter()
            .map(|mut token| {
                token.set_span(span);
                token
            })
            .collect();
    }
}

/// Failed to parse the code passed to [`StreamBuilder::push_parsed`]
///
/// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed
#[derive(Debug)]
pub struct PushParseError {
    /// The parsing error
    pub error: LexError,
    /// The code that was being parsed
    pub code: String,
}