intercom_common/
utils.rs

1use crate::prelude::*;
2use crate::tyhandlers::ModelTypeSystem;
3use syn::*;
4
5use super::*;
6use proc_macro2::Span;
7
8#[derive(PartialEq, Eq, Debug, Clone, Copy)]
9pub enum InterfaceType
10{
11    Trait,
12    Struct,
13}
14
15pub type InterfaceData<'a> = (
16    Path,
17    Vec<&'a Signature>,
18    InterfaceType,
19    Option<Token!(unsafe)>,
20);
21
22pub fn get_ident_and_fns(item: &Item) -> Option<InterfaceData>
23{
24    match *item {
25        Item::Impl(ItemImpl {
26            ref unsafety,
27            ref trait_,
28            ref self_ty,
29            ref items,
30            ..
31        }) => {
32            let (_, struct_ident, items) = get_impl_data_raw(trait_, self_ty, items);
33            Some((struct_ident, items, InterfaceType::Struct, *unsafety))
34        }
35        Item::Trait(ItemTrait {
36            ref ident,
37            unsafety,
38            ref items,
39            ..
40        }) => {
41            let methods: Option<Vec<&Signature>> = items.iter().map(get_trait_method).collect();
42            let path = syn::Path::from(ident.clone());
43
44            methods.map(|m| (path, m, InterfaceType::Trait, unsafety))
45        }
46        _ => None,
47    }
48}
49
50pub type ImplData<'a> = (Option<Path>, Path, Vec<&'a Signature>);
51
52fn get_impl_data_raw<'a>(
53    trait_ref: &'a Option<(Option<Token!(!)>, Path, Token!(for))>,
54    struct_ty: &'a Type,
55    items: &'a [ImplItem],
56) -> ImplData<'a>
57{
58    let struct_path = match struct_ty {
59        syn::Type::Path(typepath) => {
60            if typepath.qself.is_some() {
61                panic!("#[com_interface] cannot use associated types");
62            }
63            typepath.path.clone()
64        }
65        _ => panic!("#[com_interface] must be defined for Path"),
66    };
67
68    let trait_path = trait_ref.as_ref().map(|(_, path, _)| path.clone());
69
70    let methods_opt: Option<Vec<&Signature>> = items.iter().map(get_impl_method).collect();
71    let methods = methods_opt.unwrap_or_default();
72
73    (trait_path, struct_path, methods)
74}
75
76pub fn get_impl_method(i: &ImplItem) -> Option<&Signature>
77{
78    match *i {
79        ImplItem::Method(ref itm) => Some(&itm.sig),
80        _ => None,
81    }
82}
83
84pub fn get_trait_method(i: &TraitItem) -> Option<&Signature>
85{
86    match *i {
87        TraitItem::Method(ref tim) => Some(&tim.sig),
88        _ => None,
89    }
90}
91
92const AUTO_GUID_BASE: guid::GUID = guid::GUID {
93    data1: 0x4449_494C,
94    data2: 0xDE1F,
95    data3: 0x4525,
96    data4: [0xB9, 0x57, 0x89, 0xD6, 0x0C, 0xE9, 0x34, 0x77],
97};
98
99pub fn generate_iid(crate_name: &str, item_name: &str, type_system: ModelTypeSystem) -> guid::GUID
100{
101    generate_guid(
102        &[
103            "IID",
104            crate_name,
105            item_name,
106            match type_system {
107                ModelTypeSystem::Automation => "automation",
108                ModelTypeSystem::Raw => "raw",
109            },
110        ]
111        .join(":"),
112    )
113}
114
115pub fn generate_libid(crate_name: &str) -> guid::GUID
116{
117    generate_guid(&["LIBID", crate_name].join(":"))
118}
119
120pub fn generate_clsid(crate_name: &str, item_name: &str) -> guid::GUID
121{
122    generate_guid(&["CLSID", crate_name, item_name].join(":"))
123}
124
125pub fn generate_guid(key: &str) -> guid::GUID
126{
127    // Hash the name. The name will be hashed in a form similar to:
128    // AUTO_GUID_BASE + "CLSID:random_rust_crate:FooBar"
129    let mut hash = sha1::Sha1::new();
130    hash.update(AUTO_GUID_BASE.as_bytes());
131    hash.update(key.as_bytes());
132
133    let digest = hash.digest();
134    let bytes = digest.bytes();
135
136    // Set the GUID bytes according to RFC-4122, section 4.3.
137    let time_low: u32 = (u32::from(bytes[0]) << 24)
138        + (u32::from(bytes[1]) << 16)
139        + (u32::from(bytes[2]) << 8)
140        + u32::from(bytes[3]);
141    let time_mid: u16 = (u16::from(bytes[4]) << 8) + (u16::from(bytes[5]));
142    let time_hi_and_version: u16 =
143        (((u16::from(bytes[6]) << 8) + u16::from(bytes[7])) & 0x0fff) | 0x3000;
144    let clk_seq_hi_res: u8 = (bytes[8] & 0b0011_1111) | 0b0100_0000;
145    let clk_seq_low: u8 = bytes[9];
146
147    guid::GUID {
148        data1: time_low,
149        data2: time_mid,
150        data3: time_hi_and_version,
151        data4: [
152            clk_seq_hi_res,
153            clk_seq_low,
154            bytes[10],
155            bytes[11],
156            bytes[12],
157            bytes[13],
158            bytes[14],
159            bytes[15],
160        ],
161    }
162}
163
164pub fn ty_to_string(ty: &syn::Type) -> String
165{
166    quote!( #ty )
167        .to_string()
168        .replace(' ', "")
169        .replace(',', ", ")
170}
171
172pub fn is_unit(tk: &Type) -> bool
173{
174    if let Type::Tuple(ref t) = *tk {
175        t.elems.is_empty()
176    } else {
177        false
178    }
179}
180
181pub fn unit_ty(span: Span) -> Type
182{
183    syn::parse2(quote_spanned!(span => ())).unwrap()
184}
185
186pub fn get_guid_tokens(g: &guid::GUID, span: Span) -> TokenStream
187{
188    let d1 = g.data1;
189    let d2 = g.data2;
190    let d3 = g.data3;
191    let d4_0 = g.data4[0];
192    let d4_1 = g.data4[1];
193    let d4_2 = g.data4[2];
194    let d4_3 = g.data4[3];
195    let d4_4 = g.data4[4];
196    let d4_5 = g.data4[5];
197    let d4_6 = g.data4[6];
198    let d4_7 = g.data4[7];
199    quote_spanned!(span =>
200        intercom::GUID {
201            data1: #d1, data2: #d2, data3: #d3,
202            data4: [ #d4_0, #d4_1, #d4_2, #d4_3, #d4_4, #d4_5, #d4_6, #d4_7 ]
203        }
204    )
205}
206
207/// Convert the Rust identifier from `snake_case` to `PascalCase`
208pub fn pascal_case<T: AsRef<str>>(input: T) -> String
209{
210    let input = input.as_ref();
211
212    // Allocate the output string. We'll never increase the amount of
213    // characters so we can reserve string buffer using the input string length.
214    let mut output = String::new();
215    output.reserve(input.len());
216
217    // Process each character from the input.
218    let mut capitalize = true;
219    for c in input.chars() {
220        // Check the capitalization requirement.
221        if c == '_' {
222            // Skip '_' but capitalize the following character.
223            capitalize = true;
224        } else if capitalize {
225            // Capitalize. Add the uppercase characters.
226            for c_up in c.to_uppercase() {
227                output.push(c_up)
228            }
229
230            // No need to capitalize any more.
231            capitalize = false;
232        } else {
233            // No need to capitalize. Just add the character as is.
234            output.push(c);
235        }
236    }
237    output
238}
239
240#[cfg(test)]
241mod test
242{
243    use super::*;
244
245    /// Tests the `ty_to_string` by converting parameter to Type and back to
246    /// String to ensure they equal.
247    fn test_ty(ty_str: &str)
248    {
249        let ty = parse_str(ty_str).unwrap();
250        let as_string = ty_to_string(&ty);
251        assert_eq!(ty_str, as_string);
252    }
253
254    #[test]
255    fn path_to_test()
256    {
257        test_ty("::path::Foo")
258    }
259    #[test]
260    fn generics_to_test()
261    {
262        test_ty("Result<Foo, Bar>")
263    }
264    #[test]
265    fn unit_to_test()
266    {
267        test_ty("()")
268    }
269}