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,
}