rune_macros/
hash.rs

1use core::mem::take;
2
3use rune_core::hash::Hash;
4use rune_core::item::{ComponentRef, ItemBuf};
5use syn::parse::{Parse, ParseStream};
6
7use crate::context::Context;
8
9pub(super) struct Arguments {
10    path: syn::Path,
11    associated: Option<(syn::Token![.], syn::Ident)>,
12}
13
14impl Arguments {
15    pub(super) fn new(path: syn::Path) -> Self {
16        Self {
17            path,
18            associated: None,
19        }
20    }
21
22    /// Build a type item based on the current path.
23    pub(crate) fn build_type_item(&self, cx: &Context) -> Result<syn::ExprArray, ()> {
24        match crate::item::build_item(&self.path) {
25            Ok(type_item) => Ok(type_item),
26            Err(error) => {
27                cx.error(error);
28                Err(())
29            }
30        }
31    }
32
33    /// Construct a type hash from a path.
34    pub(crate) fn build_type_hash(&self, cx: &Context) -> Result<Hash, ()> {
35        self.build_type_hash_with_inner(cx, None)
36    }
37
38    /// Construct a type hash from a path with an extra string component at the end.
39    pub(crate) fn build_type_hash_with(&self, cx: &Context, extra: &str) -> Result<Hash, ()> {
40        self.build_type_hash_with_inner(cx, Some(extra))
41    }
42
43    fn build_type_hash_with_inner(&self, cx: &Context, extra: Option<&str>) -> Result<Hash, ()> {
44        // Construct type hash.
45        let mut buf = ItemBuf::new();
46        let mut first = self.path.leading_colon.is_some();
47
48        for s in &self.path.segments {
49            let ident = s.ident.to_string();
50
51            let c = if take(&mut first) {
52                ComponentRef::Crate(&ident)
53            } else {
54                ComponentRef::Str(&ident)
55            };
56
57            if let Err(error) = buf.push(c) {
58                cx.error(syn::Error::new_spanned(s, error));
59                return Err(());
60            }
61
62            match &s.arguments {
63                syn::PathArguments::None => {}
64                syn::PathArguments::AngleBracketed(generics) => {
65                    cx.error(syn::Error::new_spanned(
66                        generics,
67                        "Generic arguments are not supported",
68                    ));
69                }
70                syn::PathArguments::Parenthesized(generics) => {
71                    cx.error(syn::Error::new_spanned(
72                        generics,
73                        "Generic arguments are not supported",
74                    ));
75                }
76            }
77        }
78
79        if let Some(extra) = extra {
80            if let Err(error) = buf.push(ComponentRef::Str(extra)) {
81                cx.error(syn::Error::new_spanned(&self.path, error));
82                return Err(());
83            }
84        }
85
86        let base = Hash::type_hash(&buf);
87
88        let hash = if let Some((_, associated)) = &self.associated {
89            let name = associated.to_string();
90            Hash::associated_function(base, name.as_str())
91        } else {
92            base
93        };
94
95        Ok(hash)
96    }
97}
98
99impl Parse for Arguments {
100    fn parse(input: ParseStream) -> syn::Result<Self> {
101        let path = input.parse()?;
102
103        let ident = if let Some(colon) = input.parse::<Option<syn::Token![.]>>()? {
104            Some((colon, input.parse()?))
105        } else {
106            None
107        };
108
109        Ok(Self {
110            path,
111            associated: ident,
112        })
113    }
114}