1use std::borrow::Cow;
2use std::iter;
3
4use proc_macro2::{Group, TokenStream, TokenTree};
5use syn::Ident;
6
7use crate::session::Session;
8
9use crate::syntax::{Block, Element, Pipe, Pipeline, TokelGroup, TokelStream, TokelTree};
10
11pub trait Expand {
17 type Context;
22
23 fn expand_with(
33 self,
34 session: &mut Session,
35 context: Self::Context,
36 ) -> Result<TokenStream, syn::Error>;
37
38 fn expand(self, session: &mut Session) -> Result<TokenStream, syn::Error>
48 where
49 Self::Context: Default,
50 Self: Sized,
51 {
52 Self::expand_with(self, session, Self::Context::default())
53 }
54}
55
56impl Expand for TokelStream {
57 type Context = ();
58
59 fn expand_with(
60 self,
61 session: &mut Session,
62 (): Self::Context,
63 ) -> Result<TokenStream, syn::Error> {
64 let Self(input_vector) = self;
65
66 let mut output_stream = TokenStream::new();
67
68 for input_element in input_vector {
69 output_stream.extend(input_element.expand(session)?);
70 }
71
72 Ok(output_stream)
73 }
74}
75
76impl Expand for Element {
77 type Context = ();
78
79 fn expand_with(
80 self,
81 session: &mut Session,
82 (): Self::Context,
83 ) -> Result<TokenStream, syn::Error> {
84 match self {
85 Self::Block {
86 block: Block { stream, .. },
87 pipeline: Some(pipeline),
88 } => pipeline.expand_with(session, stream),
89 Self::Block {
90 pipeline: None,
91 block: Block { stream, .. },
92 } => stream.expand(session),
93 Self::Tree(tokel_tree) => tokel_tree.expand(session),
94 }
95 }
96}
97
98impl Expand for TokelTree {
99 type Context = ();
100
101 fn expand_with(
102 self,
103 session: &mut Session,
104 (): Self::Context,
105 ) -> Result<TokenStream, syn::Error> {
106 match self {
107 Self::Group(tokel_group) => tokel_group.expand(session),
108 tree => {
109 let mut stream = TokenStream::new();
110
111 stream.extend(iter::once(match tree {
112 Self::Ident(ident) => TokenTree::Ident(ident),
113 Self::Literal(literal) => TokenTree::Literal(literal),
114 Self::Punct(punct) => TokenTree::Punct(punct),
115 Self::Group(..) => unreachable!(),
116 }));
117
118 Ok(stream)
119 }
120 }
121 }
122}
123
124impl Expand for TokelGroup {
125 type Context = ();
126
127 fn expand_with(
128 self,
129 session: &mut Session,
130 (): Self::Context,
131 ) -> Result<TokenStream, syn::Error> {
132 let Self {
133 delimiter,
134 span,
135 stream,
136 } = self;
137
138 let mut output_stream = TokenStream::new();
139
140 let mut group = Group::new(delimiter, stream.expand(session)?);
141
142 group.set_span(span);
143
144 output_stream.extend(iter::once(TokenTree::Group(group)));
145
146 Ok(output_stream)
147 }
148}
149
150impl Expand for Pipeline {
151 type Context = TokelStream;
152
153 fn expand_with(
154 self,
155 session: &mut Session,
156 body: Self::Context,
157 ) -> Result<TokenStream, syn::Error> {
158 let mut input = body.expand(session)?;
159
160 let Self(pipe_list) = self;
161
162 for Pipe {
163 ref name, argument, ..
164 } in pipe_list
165 {
166 let target_name = &Cow::Owned(Ident::to_string(name));
167
168 let argument = if let Some((.., argument)) = argument {
169 argument.expand(session)
170 } else {
171 Ok(TokenStream::new())
172 };
173
174 if let Some(transformer) = session.registry_mut().get_mut(target_name) {
175 input = transformer.transform(input, argument?)?;
176 } else {
177 return Err(syn::Error::new(
178 name.span(),
179 format!("no such transformer `{target_name}`"),
180 ));
181 }
182 }
183
184 Ok(input)
185 }
186}