Skip to main content

deki_proc/
lib.rs

1use std::iter::Peekable;
2
3use extension_traits::extension as ext;
4use maflow::*;
5use proc_macro2::token_stream::IntoIter;
6use syn::{token::For, *};
7
8pub use {convert_case, proc_macro2, quote, syn};
9
10// Specific Re-Exports \\
11
12    pub use convert_case::{Case,Casing};
13    pub use quote::{quote as qt, quote_spanned as qts, ToTokens};
14    pub use proc_macro2::{
15        Delimiter, Span, TokenStream, TokenTree, // no-brainers
16        Group, Ident, Punct, Literal // probably always needed
17    };
18    pub use syn::spanned::Spanned;
19
20// Quick Implement Prepare \\
21
22    fn imp_hlpr(attr:TokenStream,imp_block:TokenStream) -> TokenStream {
23        let mut iter = attr.peek_iter();
24        let create_trait = iter.devour_punct('*');
25        let maybe_path = iter.peek().is_some().then_some(TokenStream::from_iter(iter));
26        let mut input: ItemImpl = syn::parse2(imp_block).unwrap();
27        let path_tok = maybe_path.unwrap_or_else(||{
28            let self_ty = input.self_ty.to_token_stream().to_string()
29                .chars().filter(|v|v.is_alphanumeric())
30                .collect::<String>().to_case(Case::Pascal);
31            let first_fn = input.items.iter().find_map(|a|match a {
32                ImplItem::Fn(a) => Some(a.sig.ident.to_string().to_case(Case::Pascal)), _=>None
33            }).unwrap();
34            (self_ty+&first_fn+"Ext").ident().into_token_stream()
35        });
36        let path: Path = parse2(path_tok).unwrap();
37        let mut out = qt![];
38        if !create_trait {
39            input.trait_ = Some((None,path.clone(),For::default()));
40        }
41        out.extend(input.into_token_stream());
42        if create_trait {
43            out = qt![ #[ext(pub trait #path)] #out ];
44        }
45        out
46    }
47
48    /// Add a method to a type or impl block:
49    /// - `#[imp(Struct)]` on fn: for an owned type
50    /// - `#[imp(*Struct)]` on fn: auto-create a unit struct
51    /// - `#[imp(Struct|Trait)]` on fn: impl a single-method trait
52    /// - `#[imp(Struct|*)]` on fn: foreign type (generates a new trait)
53    /// - `#[imp(TraitName)] impl Type { ... }`: impl an existing trait
54    /// - `#[imp(*NewTraitName)] impl Type { ... }`: generate trait + impl
55    /// - `#[imp(*)] impl Type { ... }`: auto-generate trait name + impl
56    pub fn imp (attr:TokenStream,stream:TokenStream) -> TokenStream {
57        let is_impl = stream.clone().into_iter().find_map(|t| match t {
58            TokenTree::Ident(i) if i == "impl" => Some(true),
59            TokenTree::Ident(i) if i == "fn" => Some(false),
60            _ => None,
61        }).unwrap_or(false);
62        if is_impl {
63           imp_hlpr(attr,stream)
64        } else {
65            let mut split = attr.peek_iter().split_punct('|');
66            let mut iter = split.remove(0).peek_iter();
67            let create_unit = iter.devour_punct('*');
68            let name = iter.next().unwrap();
69            let gens: Generics = parse2(TokenStream::from_iter(iter)).unwrap();
70            let (gen_impl,gen_typ,gen_where) = gens.split_for_impl();
71            let mut imp_block = qt!( impl #gen_impl #name #gen_typ #gen_where {#stream} );
72            if let Some(tok) = split.pop() {
73                imp_block = imp_hlpr(tok,imp_block);
74            }
75            let unit = if create_unit { qt![pub struct #name;] } else { qt![] };
76            qt!{#unit #imp_block}
77
78
79//           let mut trat = qt!();
80//            let mut new = qt!();
81//            if let Some(tok) = split.pop() {
82//                let mut iter2 = tok.into_token_stream();
83//                let create_trait = iter.devour_punct('*');
84//                
85//                if tok.to_string().as_str() == "*" {
86//                    let fn_name = stream.clone().into_iter().nth(1).unwrap().to_string().to_case(Case::Pascal);
87//                    let ident = format!("{name}{fn_name}Ext").ident_span(tok.span());
88//                    new.extend(qt!(#[ext(pub trait #ident)]));
89//                } else {
90//                    trat.extend(qt!(#tok for));
91//                }
92//            }
93//
94//            qt!( #unit #new impl #gen_impl #trat #name #gen_typ #gen_where {#stream} )
95         }
96   }
97
98
99// String <> Ident \\
100
101    /// Convert a string to an `Ident`.
102    #[ext(pub trait StringProcExt)]
103    impl String {
104        fn ident(&self) -> Ident {Ident::new(self,Span::call_site())}
105        fn ident_span(&self,span:Span) -> Ident {Ident::new(self,span)}
106    }
107
108    impl StringProcExt for &str {
109        fn ident(&self) -> Ident {Ident::new(self,Span::call_site())}
110        fn ident_span(&self,span:Span) -> Ident {Ident::new(self,span)}
111    }
112
113
114// Neat Token Iterator \\
115
116    /// A token-level return type.
117    #[derive(Default)]
118    pub enum Check<T,M> {#[default] None, Some(T), Maybe(M)}
119
120
121// Token Tree Extension \\
122
123    macro_rules! is_any {
124        ($self:ident = $var:ident) => {match $self {Self::$var(_) => true, _ => false}};
125        (*$self:ident = $var:ident) => {match $self {Self::$var => true, _ => false}};
126    }
127
128    macro_rules! unwrpr {($self:ident $(.$meth:ident($($tt:tt)*))* = $var:ident) => {
129        match $self $(.$meth($($tt)*))* {TokenTree::$var(v) => v, _ => panic!{"deki-rs|proc: Risk Failed!"}}
130    }}
131
132    macro_rules! checker {($self:ident::$var:ident $block:expr) => {
133        match $self {Self::$var(v) => $block(v), _ => Default::default()}
134    }}
135
136    #[ext(pub trait DekiTokenTreeExt)]
137    impl TokenTree {
138
139        // Check Types
140        #[inline] fn is_any_punct(&self) -> bool {is_any!(self = Punct)}
141        #[inline] fn is_any_ident(&self) -> bool {is_any!(self = Ident)}
142        #[inline] fn is_any_group(&self) -> bool {is_any!(self = Group)}
143        #[inline] fn is_any_literal(&self) -> bool {is_any!(self = Literal)}
144
145        // Check Unwraps
146        #[inline] fn unwrap_punct(self) -> Punct {unwrpr!(self = Punct)}
147        #[inline] fn unwrap_ident(self) -> Ident {unwrpr!(self = Ident)}
148        #[inline] fn unwrap_group(self) -> Group {unwrpr!(self = Group)}
149        #[inline] fn unwrap_literal(self) -> Literal {unwrpr!(self = Literal)}
150
151        // Check Inner
152        #[inline]
153        #[allow(clippy::redundant_closure)]
154        fn map_punct<O:Default,F:FnOnce(&Punct)->O>(&self,check:F) -> O {checker!(self::Punct|v|check(v))}
155        #[inline]
156        #[allow(clippy::redundant_closure)]
157        fn map_ident<O:Default,F:FnOnce(&Ident)->O>(&self,check:F) -> O {checker!(self::Ident|v|check(v))}
158        #[inline]
159        #[allow(clippy::redundant_closure)]
160        fn map_group<O:Default,F:FnOnce(&Group)->O>(&self,check:F) -> O {checker!(self::Group|v|check(v))}
161        #[inline]
162        #[allow(clippy::redundant_closure)]
163        fn map_literal<O:Default,F:FnOnce(&Literal)->O>(&self,check:F) -> O {checker!(self::Literal|v|check(v))}
164
165        // Shortcuts
166        #[inline] fn is_numeric(&self) -> bool {self.map_literal(move|x|x.is_numeric())}
167        #[inline] fn is_string(&self,text:&str) -> bool {self.to_string().as_str() == text}
168        #[inline] fn is_punct(&self,punct:char) -> bool {self.map_punct(move|x|x.as_char()==punct)}
169
170        // Utils \|
171
172        /// For Groups return the inner stream; for single tokens, wrap them in a stream.
173        #[inline] fn inner_tokens(&self) -> TokenStream {match self{
174            TokenTree::Group(g) => g.stream(),
175            _ => self.to_token_stream()
176        }}
177
178    }
179
180    #[ext(pub trait DekiOptTokenTreeExt)]
181    impl <'a> Option<&'a TokenTree> {
182
183        // Check Types
184        #[inline] fn is_any_punct(&self) -> bool {exit![t=self];t.is_any_punct()}
185        #[inline] fn is_any_ident(&self) -> bool {exit![t=self];t.is_any_ident()}
186        #[inline] fn is_any_group(&self) -> bool {exit![t=self];t.is_any_group()}
187        #[inline] fn is_any_literal(&self) -> bool {exit![t=self];t.is_any_literal()}
188
189        // Check Unwraps
190        #[inline] fn unwrap_punct(self) -> &'a Punct {unwrpr!(self.unwrap() = Punct)}
191        #[inline] fn unwrap_ident(self) -> &'a Ident {unwrpr!(self.unwrap() = Ident)}
192        #[inline] fn unwrap_group(self) -> &'a Group {unwrpr!(self.unwrap() = Group)}
193        #[inline] fn unwrap_literal(self) -> &'a Literal {unwrpr!(self.unwrap() = Literal)}
194
195        // Check Inner
196        #[inline] fn map_punct<O:Default,F:FnOnce(&Punct)->O>(&self,check:F) -> O {exit![t=self];t.map_punct(check)}
197        #[inline] fn map_ident<O:Default,F:FnOnce(&Ident)->O>(&self,check:F) -> O {exit![t=self];t.map_ident(check)}
198        #[inline] fn map_group<O:Default,F:FnOnce(&Group)->O>(&self,check:F) -> O {exit![t=self];t.map_group(check)}
199        #[inline] fn map_literal<O:Default,F:FnOnce(&Literal)->O>(&self,check:F) -> O {exit![t=self];t.map_literal(check)}
200
201        // Shortcuts
202        #[inline] fn is_numeric(&self) -> bool {exit![t=self];t.is_numeric()}
203        #[inline] fn is_string(&self,text:&str) -> bool {exit![t=self];t.is_string(text)}
204        #[inline] fn is_punct(&self,punct:char) -> bool {exit![t=self];t.is_punct(punct)}
205
206    }
207
208
209// Variants \\
210
211    #[ext(pub trait IdentExt)]
212    impl Ident { 
213        fn to_case(&self,case:Case) -> Ident {
214            Ident::new(&self.to_string().to_case(case),self.span())
215        }
216    }
217
218    #[ext(pub trait LiteralExt)]
219    impl Literal {
220        fn is_numeric(&self) -> bool {
221            match self.to_string().chars().next(){
222                Some(first) => first.is_numeric(),
223                _ => false
224            }
225        }
226        #[inline] fn try_int(&self) -> Result<LitInt> {parse2(self.into_token_stream())}
227        #[inline] fn try_float(&self) -> Result<LitFloat> {parse2(self.into_token_stream())}
228    }
229
230    #[ext(pub trait PunctExt)]
231    impl Punct {
232    
233    }
234
235    #[ext(pub trait DelimiterExt)]
236    impl Delimiter {
237        #[inline] fn is_parenthesis(&self) -> bool {is_any!(*self = Parenthesis)}        
238        #[inline] fn is_brace(&self) -> bool {is_any!(*self = Brace)}        
239        #[inline] fn is_bracket(&self) -> bool {is_any!(*self = Bracket)}
240        #[inline] fn is_none(&self) -> bool {is_any!(*self = None)}
241    }
242
243
244// -- \\
245
246
247
248// Stream Handling \\
249
250    /// A stream-level return type.
251    #[derive(Default)]
252    pub enum Step<T,M> {#[default] None, Base(T), Shift(M)}
253
254    impl <T,M> Step <T,M> {
255        /// Panic-unwrap the `Shift` variant.
256        pub fn risk_shift(self) -> M {match self {
257            Self::Shift(m) => m,
258            _ => panic!{}
259        }}
260        /// Unwrap `Shift` or provide a fallback value.
261        pub fn shift_or(self,m:M) -> M {match self {
262            Self::Shift(m) => m,
263            _ => m
264        }}
265    }
266
267    #[ext(pub trait TokenStreamExt)]
268    impl TokenStream {
269        fn peek_iter(self) -> PeekIter {
270            self.into_iter().peekable()
271        }
272        fn with_span(self,span:Span) -> Self {
273            Self::from_iter(self.into_iter().map(|mut a|{a.set_span(span);a}))
274        }
275        fn as_vec(self) -> Vec<TokenTree> {
276            self.into_iter().collect()
277        }
278
279        fn replace_atoms(self,func:fn(TokenTree)->TokenTree) -> Self {
280            Self::from_iter(self.into_iter().map(|a|match &a {
281                TokenTree::Group(g) => TokenTree::Group(Group::new(g.delimiter(),g.stream().replace_atoms(func))),
282                _ => func(a)
283            }))
284        }
285
286    }
287
288    /// A peekable iterator over `TokenTree`s
289    pub type PeekIter = Peekable<IntoIter>;
290
291    #[ext(pub trait TreeIterExt)]
292    impl PeekIter {
293
294        // Devour
295        #[inline] fn devour_punct(&mut self,p:char) -> bool {self.next_if(|v|v.is_punct(p)).is_some()}
296        //#[inline] fn devour_ident(self) -> bool {..}
297        //#[inline] fn devour_group(self) -> bool {..}
298        //#[inline] fn devour_literal(self) -> bool {..}
299
300 
301        fn collect_til_punct(&mut self,punct:char) -> Vec<TokenTree> {
302            let mut out = vec![];
303            while let Some(a) = self.next_if(|a|!a.is_punct(punct)){
304                out.push(a);
305            }
306            out
307        }
308
309        /// Skip until hitting a punct in stoppers.
310        fn skip_puncts(&mut self,stoppers:&str) -> Vec<TokenTree> {
311            let mut out = vec![];
312            while let Some(TokenTree::Punct(pnc)) = self.peek() {
313                if stoppers.contains(pnc.as_char()) {return out}
314                out.push(self.next().unwrap());
315            }
316            out
317        }
318
319        /// Consume and return the next token only if it's a numeric literal.
320        fn next_if_num(&mut self) -> Option<Literal> {
321            exit![if !self.peek().is_numeric()];
322            self.next().map(|l|l.unwrap_literal())
323        }
324    
325        /// Try parsing the next numeric literal as an f32.
326        fn try_next_float(&mut self) -> Option<(f32,LitFloat)> {
327            exit![lit = self.next_if_num()];
328            let lit: LitFloat = lit.clone().into();
329            kill![num = lit.base10_parse::<f32>()];
330            Some((num,lit))
331        }
332
333        /// Peek the next punctuation character; returns `'n'` as sentinel if not a punct.
334        fn peek_punct(&mut self) -> char {
335            exit![>(peek = self.peek())'n'];
336            exit![>(*TokenTree::Punct(p) = peek)'n'];
337            p.as_char()
338        }
339
340        /// Split a token stream by a punctuation delimiter, skipping empty parts.
341        fn split_punct(self,punct:char) -> Vec<TokenStream> {
342            let mut out = vec![];
343            let mut curr = vec![];
344            for tree in self {
345                if tree.is_punct(punct){
346                    if !curr.is_empty(){
347                        out.push(TokenStream::from_iter(std::mem::take(&mut curr)));
348                    }
349                    continue
350                }
351                curr.push(tree);
352            }
353            if !curr.is_empty(){
354                out.push(TokenStream::from_iter(curr));
355            }
356            out
357        }
358    }
359
360
361// EOF \\