telety_impl/alias/
map.rs

1use std::collections::HashMap;
2
3use quote::{TokenStreamExt as _, format_ident, quote};
4use syn::{parse_quote, spanned::Spanned as _};
5
6use crate::{Alias, Options, alias, syn_util, visitor};
7
8#[derive(Debug)]
9pub(crate) struct Root {
10    telety_path: Option<syn::Path>,
11    map_path: syn::Path,
12    generics: syn::Generics,
13    alias_traits: Option<bool>,
14}
15
16#[derive(Debug)]
17enum OwnedOrRef<'l, T> {
18    Owned(T),
19    Ref(&'l T),
20}
21
22impl<'l, T> AsRef<T> for OwnedOrRef<'l, T> {
23    fn as_ref(&self) -> &T {
24        match self {
25            OwnedOrRef::Owned(owned) => owned,
26            OwnedOrRef::Ref(rf) => rf,
27        }
28    }
29}
30
31#[derive(Debug)]
32pub struct Map<'p> {
33    root: OwnedOrRef<'p, Root>,
34    parent: Option<&'p Map<'p>>,
35
36    module: alias::Module,
37    unique_ident: syn::Ident,
38    primary: Option<(alias::Path, alias::Arguments)>,
39    // Maps exact type to index
40    lookup: HashMap<alias::Path, (usize, alias::Arguments, alias::Kind)>,
41    // // Maps index to de-Self'ed type
42    // list: Vec<alias::Path>,
43}
44
45impl<'p> Map<'p> {
46    pub(crate) fn new_root(
47        telety_path: Option<syn::Path>,
48        map_path: syn::Path,
49        module: alias::Module,
50        generics: syn::Generics,
51        unique_ident: syn::Ident,
52        options: &Options,
53    ) -> Self {
54        let root = Root {
55            telety_path,
56            map_path,
57            generics,
58            alias_traits: options.alias_traits,
59        };
60
61        Self {
62            root: OwnedOrRef::Owned(root),
63            parent: None,
64
65            module,
66            unique_ident,
67            primary: None,
68            lookup: HashMap::new(),
69            // list: vec![]
70        }
71    }
72
73    pub(crate) fn new_child(&'p self, suffix: &str) -> Self {
74        let module = self.module.new_child(suffix);
75        let parent_ident = &self.unique_ident;
76        let unique_ident = format_ident!("{parent_ident}__{suffix}");
77        Self {
78            root: OwnedOrRef::Ref(self.root()),
79            parent: Some(self),
80
81            module,
82            unique_ident,
83            primary: None,
84            lookup: HashMap::new(),
85            // list: vec![],
86        }
87    }
88
89    pub(crate) fn set_self(&mut self, self_type: &syn::Path) -> Result<(), alias::Error> {
90        let (path, args) = alias::Path::new(self_type)?;
91        // Self may have 'baked-in' generic parameters, so we can't always reuse the same alias.
92        // If the explicit type also appears, we can just add it as an ordinary secondary alias
93        self.primary = Some((path, args));
94
95        Ok(())
96    }
97
98    pub(crate) fn full_lookup<'map>(
99        &'map self,
100        ty: &syn::Path,
101    ) -> Result<Option<Alias<'map>>, alias::Error> {
102        match self.local_lookup(ty)? {
103            some @ Some(_) => Ok(some),
104            None => {
105                if let Some(parent) = self.parent {
106                    parent.full_lookup(ty)
107                } else {
108                    Ok(None)
109                }
110            }
111        }
112    }
113
114    pub(crate) fn local_lookup<'map>(
115        &'map self,
116        ty: &syn::Path,
117    ) -> Result<Option<Alias<'map>>, alias::Error> {
118        let (path, args) = alias::Path::new(ty)?;
119        if let Some((path, (index, _canon_args, kind))) = self.lookup.get_key_value(&path) {
120            Ok(Some(Alias::new(
121                self,
122                path,
123                alias::Index::Secondary(*index),
124                args,
125                *kind,
126            )))
127        } else {
128            Ok(None)
129        }
130    }
131
132    pub(crate) fn local_get_self(&self) -> Option<Alias<'_>> {
133        if let Some((path, args)) = &self.primary {
134            Some(Alias::new(
135                self,
136                path,
137                alias::Index::Primary,
138                args.clone(),
139                alias::Kind::Type,
140            ))
141        } else {
142            None
143        }
144    }
145
146    fn root(&self) -> &Root {
147        self.root.as_ref()
148    }
149
150    /// Iterate all [Alias]es at this map level ([Alias]es from parent maps are not included)
151    pub fn iter_aliases(&self) -> impl Iterator<Item = Alias<'_>> {
152        let primary_aliases = self.local_get_self().into_iter();
153
154        let secondary_aliases = self.lookup.iter().map(|(path, (index, args, alias_type))| {
155            Alias::new(
156                self,
157                path,
158                alias::Index::Secondary(*index),
159                args.clone(),
160                *alias_type,
161            )
162        });
163
164        primary_aliases.chain(secondary_aliases)
165    }
166}
167
168impl<'p> Map<'p> {
169    pub fn new_sub_map(&self, suffix: &str) -> Map<'_> {
170        Map::new_child(self, suffix)
171    }
172
173    pub fn telety_path(&self) -> Option<&syn::Path> {
174        self.root().telety_path.as_ref()
175    }
176
177    pub fn map_path(&self) -> &syn::Path {
178        &self.root().map_path
179    }
180
181    pub fn module(&self) -> &alias::Module {
182        &self.module
183    }
184
185    pub fn visibility(&self) -> &syn::Visibility {
186        self.module.visibility()
187    }
188
189    pub fn unique_ident(&self) -> &syn::Ident {
190        &self.unique_ident
191    }
192
193    pub fn generics(&self) -> &syn::Generics {
194        &self.root().generics
195    }
196
197    /// Register a [syn::TypePath] in the [Map]. If the exact (i.e. identical tokens, not equivalent Rust types) type
198    /// already exists in the map, this is a no-op. Maps constructed with the same parameters and order of
199    /// inserts will yield the same [Alias]es.
200    pub fn insert_type(&mut self, ty: &syn::TypePath) -> Result<bool, alias::Error> {
201        // Associated types are not supported (watch https://github.com/rust-lang/rust/issues/134691)
202        if ty.qself.is_some() {
203            return Err(alias::Error::new(
204                ty.span(),
205                alias::error::Kind::AssociatedType,
206            ));
207        }
208        self.insert(&ty.path, alias::Kind::Type)
209    }
210
211    /// Register a [syn::Path] to a trait in the [Map]. If the exact (i.e. identical tokens, not equivalent Rust types) type
212    /// already exists in the map, this is a no-op. Maps constructed with the same parameters and order of
213    /// inserts will yield the same [Alias]es.
214    pub fn insert_trait(&mut self, ty: &syn::Path) -> Result<bool, alias::Error> {
215        let can_insert = self
216            .root()
217            .alias_traits
218            .unwrap_or_else(|| ty.leading_colon.is_some() || ty.segments.len() > 1);
219
220        if can_insert {
221            self.insert(ty, alias::Kind::Trait)
222        } else {
223            Err(alias::Error::new(ty.span(), alias::error::Kind::Trait))
224        }
225    }
226
227    fn insert(&mut self, ty: &syn::Path, alias_type: alias::Kind) -> Result<bool, alias::Error> {
228        if ty.is_ident("Self") {
229            // TODO should this be an error instead?
230            Ok(false)
231        } else if self.full_lookup(ty)?.is_some() {
232            // Path already exists
233            Ok(false)
234        } else {
235            let (path, mut args) = alias::Path::new(ty)?;
236
237            let index = self.lookup.len();
238            args.parameterize();
239
240            self.lookup.insert(path, (index, args, alias_type));
241
242            Ok(true)
243        }
244    }
245
246    pub fn get_self(&self) -> Option<Alias<'_>> {
247        if let Some(alias) = self.local_get_self() {
248            Some(alias)
249        } else if let Some(parent) = self.parent {
250            parent.get_self()
251        } else {
252            None
253        }
254    }
255
256    pub fn get_alias<'map>(
257        &'map self,
258        ty: &syn::Path,
259    ) -> Result<Option<Alias<'map>>, alias::Error> {
260        if ty == &parse_quote!(Self) {
261            Ok(self.get_self())
262        } else {
263            self.full_lookup(ty)
264        }
265    }
266
267    pub fn visitor(&self) -> visitor::ApplyAliases<'_> {
268        visitor::ApplyAliases::new(self)
269    }
270
271    pub fn with_module(&self) -> impl quote::ToTokens + use<'_> {
272        self.module.with_contents(self)
273    }
274}
275
276impl<'p> quote::ToTokens for Map<'p> {
277    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
278        // items inside our module need to convert blank vis to `pub(super)`, etc.
279        let super_visibility = syn_util::super_visibility(self.module.visibility());
280
281        let exact_aliases = self.iter_aliases().map(alias::Alias::exact);
282
283        let public_aliases = self.iter_aliases().map(alias::Alias::public);
284
285        let map_mod = quote! {
286            mod exact {
287                #super_visibility use super::super::*;
288
289                #(#exact_aliases)*
290            }
291
292            #(#public_aliases)*
293        };
294
295        tokens.append_all(map_mod);
296    }
297}