struct_baker/interpolation/
mod.rs

1use std::{
2    error::Error,
3    fmt,
4    fmt::{Display, Formatter},
5    ops::Deref,
6};
7
8use proc_macro2::{TokenStream, TokenTree};
9use quote::{quote, ToTokens};
10use syn::{parse2, parse_quote};
11
12use crate::{functions::BakeableFnOnce, Bake, Bakeable};
13
14pub mod helper;
15pub mod ops;
16mod flatten;
17
18pub use flatten::*;
19
20#[derive(Debug, Clone)]
21pub enum Interpolatable<T> {
22    Inter(TokenTree),
23    Actual(T),
24}
25
26impl<T> Interpolatable<T> {
27    /// Adds conversion via `into` to the stream and wraps it into a tree
28    pub fn new_inter(stream: TokenStream) -> Self {
29        Self::Inter(parse_quote!({{#stream}.into()}))
30    }
31
32    /// Uses the TokenTree as-is for interpolation
33    ///
34    /// You have to take care of type conversion manually
35    pub fn new_inter_raw(tree: TokenTree) -> Self {
36        Self::Inter(parse_quote!(#tree.into()))
37    }
38}
39
40// create `new` method that wraps the stream in a tree and adds .into()
41// create secondary new without into for from iterator
42
43impl<T: Bake + PartialEq> PartialEq for Interpolatable<T> {
44    fn eq(&self, other: &Self) -> bool {
45        match (self, other) {
46            (Self::Actual(a), Self::Actual(b)) => a.eq(b),
47            _ => false,
48        }
49    }
50}
51
52#[derive(Debug)]
53pub struct RuntimeInterpolationError(TokenTree);
54
55impl Display for RuntimeInterpolationError {
56    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57        write!(f, "Runtime Interpolation. {}", self.0)
58    }
59}
60
61impl Error for RuntimeInterpolationError {}
62
63trait UnwrapInterpolation<T> {
64    fn unwrap(self) -> T;
65}
66
67impl<T: Bake> Bakeable for Interpolatable<T> {
68    fn bake(&self) -> TokenStream {
69        match self {
70            Interpolatable::Inter(tree) => tree.to_token_stream(),
71            Interpolatable::Actual(t) => t.to_stream(),
72        }
73    }
74}
75
76pub trait Interpolate<T> {
77    fn fit(self) -> Result<T, RuntimeInterpolationError>;
78
79    ///Panics: If T can not be transformed
80    fn force_fit(self) -> T
81    where
82        Self: Sized,
83    {
84        self.fit().expect("Interpolated during runtime")
85    }
86
87    #[cfg(feature = "nom")]
88    /// Performs [fit] but will map [RuntimeInterpolationError] to nom::Err::Failure
89    fn nom<I>(self, input: I) -> Result<T, nom::Err<nom::error::Error<I>>>
90    where
91        Self: Sized,
92    {
93        self.fit().map_err(|_| {
94            nom::Err::Failure(nom::error::Error::new(input, nom::error::ErrorKind::Fail))
95        })
96    }
97}
98
99impl<T: Bake> Interpolate<T> for Interpolatable<T> {
100    fn fit(self) -> Result<T, RuntimeInterpolationError> {
101        match self {
102            Interpolatable::Actual(t) => Ok(t),
103            Interpolatable::Inter(tree) => Err(RuntimeInterpolationError(tree)),
104        }
105    }
106}
107
108impl<'a, T: Bake> Interpolate<&'a T> for &'a Interpolatable<T> {
109    fn fit(self) -> Result<&'a T, RuntimeInterpolationError> {
110        match self {
111            Interpolatable::Actual(t) => Ok(t),
112            Interpolatable::Inter(tree) => Err(RuntimeInterpolationError(tree.clone())),
113        }
114    }
115}
116
117impl<T: Bake> Interpolate<Interpolatable<T>> for Interpolatable<T> {
118    fn fit(self) -> Result<Interpolatable<T>, RuntimeInterpolationError> {
119        Ok(self)
120    }
121}
122
123impl<T: Bake> Interpolate<Interpolatable<T>> for T {
124    fn fit(self) -> Result<Interpolatable<T>, RuntimeInterpolationError> {
125        Ok(Interpolatable::Actual(self))
126    }
127}
128
129impl<T: Bake> Interpolate<T> for T {
130    fn fit(self) -> Result<T, RuntimeInterpolationError> {
131        Ok(self)
132    }
133}
134
135impl<T: Bake> From<T> for Interpolatable<T> {
136    fn from(value: T) -> Self {
137        Self::Actual(value)
138    }
139}
140
141impl<T> From<TokenTree> for Interpolatable<T> {
142    fn from(value: TokenTree) -> Self {
143        Self::Inter(value)
144    }
145}
146
147impl From<RuntimeInterpolationError> for syn::Error {
148    fn from(value: RuntimeInterpolationError) -> Self {
149        Self::new_spanned(value.0, "Runtime interpolation is not possible")
150    }
151}
152
153pub trait IntoInterpolation
154where
155    Self: Sized + Bake,
156{
157    fn interpolate(self) -> Interpolatable<Self>;
158}
159
160impl<T: Bake> IntoInterpolation for T {
161    fn interpolate(self) -> Interpolatable<Self> {
162        Interpolatable::Actual(self)
163    }
164}
165
166//TODO: This may be really dumb
167impl<T: Bake> Deref for Interpolatable<T> {
168    type Target = T;
169    fn deref(&self) -> &Self::Target {
170        self.fit().expect("Can not deref an interpolation")
171    }
172}
173
174impl<B: Bake> From<Vec<Interpolatable<B>>> for Interpolatable<Vec<B>> {
175    fn from(value: Vec<Interpolatable<B>>) -> Self {
176        let mut visited: Vec<B> = Vec::with_capacity(value.len());
177        let mut result: Option<Vec<TokenTree>> = None;
178
179        use Interpolatable::*;
180
181        for element in value {
182            match element {
183                Inter(tree) => {
184                    if result.is_none() {
185                        let mut v = Vec::with_capacity(visited.len());
186                        v.extend(visited.iter().map(|b| b.to_token_tree()));
187                        result = Some(v);
188                    }
189                    result.as_mut().unwrap().push(tree);
190                }
191                Actual(item) => match result.as_mut() {
192                    Some(vec) => vec.push(item.to_token_tree()),
193                    None => visited.push(item),
194                },
195            }
196        }
197
198        match result {
199            Some(list) => Inter(parse_quote!({ vec![#(#list.into(),)*] })),
200            None => Actual(visited),
201        }
202    }
203}
204
205impl<B: Bake, Collection: FromIterator<B>> FromIterator<Interpolatable<B>>
206    for Interpolatable<Collection>
207{
208    fn from_iter<T: IntoIterator<Item = Interpolatable<B>>>(iter: T) -> Self {
209        let iter = iter.into_iter();
210        let mut visited: Vec<B> = Vec::with_capacity(iter.size_hint().1.unwrap_or_default());
211        let mut result: Option<Vec<TokenTree>> = None;
212
213        use Interpolatable::*;
214
215        for element in iter {
216            match element {
217                Inter(tree) => {
218                    if result.is_none() {
219                        let mut v = Vec::with_capacity(visited.len());
220                        v.extend(visited.iter().map(|b| b.to_token_tree()));
221                        result = Some(v);
222                    }
223                    result.as_mut().unwrap().push(parse_quote!({#tree.into()}));
224                }
225                Actual(item) => match result.as_mut() {
226                    Some(vec) => vec.push(item.to_token_tree()),
227                    None => visited.push(item),
228                },
229            }
230        }
231
232        match result {
233            Some(list) => Inter(parse_quote!({FromIterator::from_iter([#(#list,)*])})),
234            None => Actual(FromIterator::from_iter(visited)),
235        }
236    }
237}
238
239impl<T: Bake> Interpolatable<T> {
240    /// Maps an `Interpolatable<T>` to `Interpolatable<U>` by applying a function to its contents
241    ///
242    /// - `Actual(T)` gets mapped to `Actual(U)`
243    /// - `Interpolation` gets mapped to an `Interpolation` that applies f to the content at runtime
244    pub fn map<F, U: Bake>(self, f: BakeableFnOnce<F, T, U>) -> Interpolatable<U>
245    where
246        F: FnOnce(T) -> U,
247    {
248        use Interpolatable::*;
249        match self {
250            Actual(inner) => Actual(f.call(inner)),
251            Inter(tree) => {
252                let function_path = f.bake();
253                Inter(parse2(quote!(#function_path(#tree.into()))).unwrap())
254            }
255        }
256    }
257
258    pub fn actual(self) -> Option<T> {
259        match self {
260            Interpolatable::Inter(_) => None,
261            Interpolatable::Actual(t) => Some(t),
262        }
263    }
264
265    pub fn tree(self) -> Option<TokenTree> {
266        match self {
267            Interpolatable::Inter(tree) => Some(tree),
268            Interpolatable::Actual(_) => None,
269        }
270    }
271}