portrait_framework/
item_map.rs

1use std::collections::HashMap;
2
3use syn::{Error, Result};
4
5/// Shorthand for `TraitItemMap::new().minus(ImplItemMap::new())`.
6pub fn subtract_items<'t>(
7    trait_items: &'t [syn::TraitItem],
8    impl_block: &'t syn::ItemImpl,
9) -> syn::Result<TraitItemMap<'t>> {
10    let mut items = TraitItemMap::new(trait_items);
11    items.minus(&ImplItemMap::new(impl_block))?;
12    Ok(items)
13}
14
15/// Indexes items in a trait by namespaced identifier.
16#[derive(Default)]
17pub struct TraitItemMap<'t> {
18    /// Associated constants in the trait.
19    pub consts: HashMap<syn::Ident, &'t syn::TraitItemConst>,
20    /// Associated functions in the trait.
21    pub fns:    HashMap<syn::Ident, &'t syn::TraitItemFn>,
22    /// Associated types in the trait.
23    pub types:  HashMap<syn::Ident, &'t syn::TraitItemType>,
24}
25
26impl<'t> TraitItemMap<'t> {
27    /// Constructs the trait item index from a slice of trait items.
28    pub fn new(trait_items: &'t [syn::TraitItem]) -> Self {
29        let mut map = Self::default();
30        for item in trait_items {
31            match item {
32                syn::TraitItem::Const(item) => {
33                    map.consts.insert(item.ident.clone(), item);
34                }
35                syn::TraitItem::Fn(item) => {
36                    map.fns.insert(item.sig.ident.clone(), item);
37                }
38                syn::TraitItem::Type(item) => {
39                    map.types.insert(item.ident.clone(), item);
40                }
41                _ => {}
42            }
43        }
44        map
45    }
46
47    /// Removes the items found in the impl, leaving only unimplemented items.
48    pub fn minus(&mut self, impl_items: &ImplItemMap) -> Result<()> {
49        for (ident, impl_item) in &impl_items.consts {
50            if self.consts.remove(ident).is_none() {
51                return Err(Error::new_spanned(
52                    impl_item,
53                    "no associated constant called {ident} in trait",
54                ));
55            }
56        }
57
58        for (ident, impl_item) in &impl_items.fns {
59            if self.fns.remove(ident).is_none() {
60                return Err(Error::new_spanned(
61                    impl_item,
62                    "no associated function called {ident} in trait",
63                ));
64            }
65        }
66
67        for (ident, impl_item) in &impl_items.types {
68            if self.types.remove(ident).is_none() {
69                return Err(Error::new_spanned(
70                    impl_item,
71                    "no associated type called {ident} in trait",
72                ));
73            }
74        }
75
76        Ok(())
77    }
78}
79
80/// Indexes items in an impl block by namespaced identifier.
81#[derive(Default)]
82pub struct ImplItemMap<'t> {
83    /// Associated constants in the implementation.
84    pub consts: HashMap<syn::Ident, &'t syn::ImplItemConst>,
85    /// Associated functions in the implementation.
86    pub fns:    HashMap<syn::Ident, &'t syn::ImplItemFn>,
87    /// Associated types in the implementation.
88    pub types:  HashMap<syn::Ident, &'t syn::ImplItemType>,
89}
90
91impl<'t> ImplItemMap<'t> {
92    /// Constructs the impl item index from an impl block.
93    pub fn new(impl_block: &'t syn::ItemImpl) -> Self {
94        let mut map = Self::default();
95        for item in &impl_block.items {
96            match item {
97                syn::ImplItem::Const(item) => {
98                    map.consts.insert(item.ident.clone(), item);
99                }
100                syn::ImplItem::Fn(item) => {
101                    map.fns.insert(item.sig.ident.clone(), item);
102                }
103                syn::ImplItem::Type(item) => {
104                    map.types.insert(item.ident.clone(), item);
105                }
106                _ => {}
107            }
108        }
109        map
110    }
111}