1use super::macros::*;
2use super::*;
3use crate::prelude::*;
4
5use crate::ast_converters::*;
6use crate::guid::GUID;
7use crate::idents::{self, SomeIdent};
8use crate::methodinfo::ComMethodInfo;
9use crate::quote::ToTokens;
10use crate::tyhandlers::ModelTypeSystem;
11use indexmap::IndexMap;
12use proc_macro2::Span;
13use std::iter::FromIterator;
14use syn::{Ident, LitStr, Path, TypePath, Visibility};
15
16intercom_attribute!(
17 ComInterfaceAttr< ComInterfaceAttrParam, NoParams > {
18 com_iid : LitStr,
19 raw_iid : LitStr,
20 base : Path,
21 vtable_of: Path,
22 implemented_by: Path,
23 }
24);
25
26impl ComInterfaceAttr
27{
28 pub fn iid(&self, ts: ModelTypeSystem) -> Result<Option<&LitStr>, String>
29 {
30 match ts {
31 ModelTypeSystem::Raw => self.raw_iid(),
32 ModelTypeSystem::Automation => self.com_iid(),
33 }
34 }
35}
36
37#[derive(Debug)]
38pub struct ComInterface
39{
40 pub path: Path,
41 pub ident: Ident,
42 pub visibility: Visibility,
43 pub base_interface: Option<Path>,
44 pub variants: IndexMap<ModelTypeSystem, ComInterfaceVariant>,
45 pub item_type: crate::utils::InterfaceType,
46 pub span: Span,
47 pub is_unsafe: bool,
48 pub itf_ref: TokenStream,
49 pub vtable_of: Option<Path>,
50 pub implemented_by: Option<Path>,
51}
52
53#[derive(Debug, PartialEq)]
54pub struct ComInterfaceVariant
55{
56 pub type_system: ModelTypeSystem,
57 pub iid: GUID,
58 pub methods: Vec<ComMethodInfo>,
59}
60
61impl ComInterface
62{
63 pub fn from_ast(
65 crate_name: &str,
66 attr: TokenStream,
67 item: TokenStream,
68 ) -> ParseResult<ComInterface>
69 {
70 let item: syn::Item = ::syn::parse2(item).map_err(|_| {
71 ParseError::ComInterface("<Unknown>".into(), "Item syntax error".into())
72 })?;
73
74 let attr: ComInterfaceAttr = ::syn::parse2(attr).map_err(|_| {
75 ParseError::ComInterface(
76 item.get_ident().unwrap().to_string(),
77 "Attribute syntax error".into(),
78 )
79 })?;
80
81 let (path, fns, itf_type, unsafety) =
84 crate::utils::get_ident_and_fns(&item).ok_or_else(|| {
85 ParseError::ComInterface(
86 item.get_ident().unwrap().to_string(),
87 "Unsupported associated item".into(),
88 )
89 })?;
90
91 let ident = path.get_some_ident().ok_or_else(|| {
92 ParseError::ComInterface(
93 path.to_token_stream().to_string(),
94 "Could not resolve ident for".to_string(),
95 )
96 })?;
97
98 let base = attr
104 .base()
105 .map_err(|msg| ParseError::ComInterface(item.get_ident().unwrap().to_string(), msg))?;
106 let base = match base {
107 Some(b) => {
108 if b.get_ident().map(|i| i == "NO_BASE") == Some(true) {
109 None
110 } else {
111 Some(b.to_owned())
112 }
113 }
114 None => Some(syn::parse2(quote!(intercom::IUnknown)).unwrap()),
115 };
116
117 let visibility = if let ::syn::Item::Trait(ref t) = item {
126 t.vis.clone()
127 } else {
128 parse_quote!(pub)
129 };
130
131 let variants = IndexMap::from_iter(
132 [ModelTypeSystem::Automation, ModelTypeSystem::Raw]
133 .iter()
134 .map(|&ts| {
135 let iid_attr = attr.iid(ts).map_err(|msg| {
136 ParseError::ComInterface(item.get_ident().unwrap().to_string(), msg)
137 })?;
138 let iid = match iid_attr {
139 Some(iid) => GUID::parse(&iid.value()).map_err(|_| {
140 ParseError::ComInterface(
141 item.get_ident().unwrap().to_string(),
142 "Bad IID format".into(),
143 )
144 })?,
145 None => crate::utils::generate_iid(crate_name, &ident.to_string(), ts),
146 };
147
148 let methods = fns
153 .iter()
154 .map(|sig| ComMethodInfo::new(sig, ts))
155 .filter_map(Result::ok)
156 .collect::<Vec<_>>();
157
158 Ok((
159 ts,
160 ComInterfaceVariant {
161 type_system: ts,
162 iid,
163 methods,
164 },
165 ))
166 })
167 .collect::<Result<Vec<_>, _>>()?,
168 );
169
170 let itf_ref = match itf_type {
171 crate::utils::InterfaceType::Trait => quote_spanned!(ident.span() => dyn #path),
172 crate::utils::InterfaceType::Struct => quote_spanned!(ident.span() => #path),
173 };
174
175 Ok(ComInterface {
176 base_interface: base,
177 item_type: itf_type,
178 is_unsafe: unsafety.is_some(),
179 span: ident.span(),
180 vtable_of: attr
181 .vtable_of()
182 .map_err(|e| ParseError::ComInterface(ident.to_string(), e))?
183 .cloned(),
184 implemented_by: attr
185 .implemented_by()
186 .map_err(|e| ParseError::ComInterface(ident.to_string(), e))?
187 .cloned(),
188 path,
189 ident,
190 visibility,
191 variants,
192 itf_ref,
193 })
194 }
195
196 pub fn vtable(&self, ts: ModelTypeSystem) -> TypePath
197 {
198 let ts_type_tokens = ts.as_typesystem_type(self.ident.span());
199 let tt = match &self.vtable_of {
200 Some(path) => {
201 quote_spanned!(self.ident.span() => <dyn #path as intercom::attributes::ComInterfaceVariant<#ts_type_tokens>>::VTable)
202 }
203 None => {
204 let ident = idents::vtable(&self.ident, ts);
205 quote!(#ident)
206 }
207 };
208 syn::parse2(tt).unwrap()
209 }
210}
211
212#[cfg(test)]
213mod test
214{
215 use super::*;
216 use crate::tyhandlers::ModelTypeSystem::*;
217
218 #[test]
219 fn parse_com_interface()
220 {
221 let itf = ComInterface::from_ast(
222 "not used",
223 quote!(
224 com_iid = "12345678-1234-1234-1234-567890ABCDEF",
225 raw_iid = "12345678-1234-1234-1234-567890FEDCBA",
226 ),
227 quote!(
228 trait ITrait
229 {
230 fn foo(&self);
231 fn bar(&self);
232 }
233 ),
234 )
235 .expect("com_interface attribute parsing failed");
236
237 assert_eq!(itf.path, parse_quote!(ITrait));
238 assert_eq!(itf.visibility, Visibility::Inherited);
239 assert_eq!(
240 itf.base_interface.as_ref().unwrap(),
241 &parse_quote!(intercom::IUnknown)
242 );
243
244 let variant = &itf.variants[&Automation];
245 assert_eq!(
246 variant.iid,
247 GUID::parse("12345678-1234-1234-1234-567890ABCDEF").unwrap()
248 );
249 assert_eq!(variant.methods.len(), 2);
250 assert_eq!(variant.methods[0].name, "foo");
251 assert_eq!(variant.methods[1].name, "bar");
252
253 let variant = &itf.variants[&Raw];
254 assert_eq!(
255 variant.iid,
256 GUID::parse("12345678-1234-1234-1234-567890FEDCBA").unwrap()
257 );
258 assert_eq!(variant.methods.len(), 2);
259 assert_eq!(variant.methods[0].name, "foo");
260 assert_eq!(variant.methods[1].name, "bar");
261 }
262
263 #[test]
264 fn parse_com_interface_with_auto_guid()
265 {
266 let itf = ComInterface::from_ast(
267 "not used",
268 quote!(),
269 quote!(
270 pub trait IAutoGuid
271 {
272 fn one(&self);
273 fn two(&self);
274 }
275 ),
276 )
277 .expect("com_interface attribute parsing failed");
278
279 assert_eq!(itf.path, parse_quote!(IAutoGuid));
280
281 let pub_visibility: Visibility = parse_quote!(pub);
282 assert_eq!(itf.visibility, pub_visibility);
283 assert_eq!(
284 itf.base_interface.as_ref().unwrap(),
285 &parse_quote!(intercom::IUnknown)
286 );
287
288 let variant = &itf.variants[&Automation];
289 assert_eq!(
290 variant.iid,
291 GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
292 );
293 assert_eq!(variant.methods.len(), 2);
294 assert_eq!(variant.methods[0].name, "one");
295 assert_eq!(variant.methods[1].name, "two");
296
297 let variant = &itf.variants[&Raw];
298 assert_eq!(
299 variant.iid,
300 GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
301 );
302 assert_eq!(variant.methods.len(), 2);
303 assert_eq!(variant.methods[0].name, "one");
304 assert_eq!(variant.methods[1].name, "two");
305 }
306
307 #[test]
308 fn parse_com_interface_with_base_interface()
309 {
310 let itf = ComInterface::from_ast(
311 "not used",
312 quote!(base = IBase),
313 quote!(
314 pub trait IAutoGuid
315 {
316 fn one(&self);
317 fn two(&self);
318 }
319 ),
320 )
321 .expect("com_interface attribute parsing failed");
322
323 assert_eq!(itf.path, parse_quote!(IAutoGuid));
324
325 let pub_visibility: Visibility = parse_quote!(pub);
326 assert_eq!(itf.visibility, pub_visibility);
327 assert_eq!(itf.base_interface.as_ref().unwrap(), &parse_quote!(IBase));
328
329 let variant = &itf.variants[&ModelTypeSystem::Automation];
330 assert_eq!(
331 variant.iid,
332 GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
333 );
334 assert_eq!(variant.methods.len(), 2);
335 assert_eq!(variant.methods[0].name, "one");
336 assert_eq!(variant.methods[1].name, "two");
337
338 let variant = &itf.variants[&ModelTypeSystem::Raw];
339 assert_eq!(
340 variant.iid,
341 GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
342 );
343 assert_eq!(variant.methods.len(), 2);
344 assert_eq!(variant.methods[0].name, "one");
345 assert_eq!(variant.methods[1].name, "two");
346 }
347
348 #[test]
349 fn parse_com_interface_with_no_base_interface()
350 {
351 let itf = ComInterface::from_ast(
352 "not used",
353 quote!(base = NO_BASE),
354 quote!(
355 pub trait IAutoGuid
356 {
357 fn one(&self);
358 fn two(&self);
359 }
360 ),
361 )
362 .expect("com_interface attribute parsing failed");
363
364 assert_eq!(itf.path, parse_quote!(IAutoGuid));
365
366 let pub_visibility: Visibility = parse_quote!(pub);
367 assert_eq!(itf.visibility, pub_visibility);
368 assert_eq!(itf.base_interface, None);
369
370 let variant = &itf.variants[&Automation];
371 assert_eq!(
372 variant.iid,
373 GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
374 );
375 assert_eq!(variant.methods.len(), 2);
376 assert_eq!(variant.methods[0].name, "one");
377 assert_eq!(variant.methods[1].name, "two");
378
379 let variant = &itf.variants[&Raw];
380 assert_eq!(
381 variant.iid,
382 GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
383 );
384 assert_eq!(variant.methods.len(), 2);
385 assert_eq!(variant.methods[0].name, "one");
386 assert_eq!(variant.methods[1].name, "two");
387 }
388}