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 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 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
207pub fn pascal_case<T: AsRef<str>>(input: T) -> String
209{
210 let input = input.as_ref();
211
212 let mut output = String::new();
215 output.reserve(input.len());
216
217 let mut capitalize = true;
219 for c in input.chars() {
220 if c == '_' {
222 capitalize = true;
224 } else if capitalize {
225 for c_up in c.to_uppercase() {
227 output.push(c_up)
228 }
229
230 capitalize = false;
232 } else {
233 output.push(c);
235 }
236 }
237 output
238}
239
240#[cfg(test)]
241mod test
242{
243 use super::*;
244
245 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}