intercom_common/model/
comclass.rs1use super::macros::*;
2use super::*;
3use crate::prelude::*;
4
5use crate::guid::GUID;
6use syn::{Generics, Path, Visibility};
7
8intercom_attribute!(
9 ComClassAttr<ComClassAttrParam, Path> {
10 clsid : StrOption,
11 }
12);
13
14#[derive(Debug, PartialEq, Eq)]
16pub struct ComClass
17{
18 pub name: Ident,
19 pub clsid: Option<GUID>,
20 pub visibility: Visibility,
21 pub interfaces: Vec<Path>,
22 pub generics: Generics,
23}
24
25impl ComClass
26{
27 pub fn parse(
29 crate_name: &str,
30 attr_params: TokenStream,
31 item: TokenStream,
32 ) -> ParseResult<ComClass>
33 {
34 let item: ::syn::ItemStruct = ::syn::parse2(item)
36 .map_err(|_| ParseError::ComClass("<Unknown>".into(), "Item syntax error".into()))?;
37
38 let attr: ComClassAttr = ::syn::parse2(attr_params).map_err(|e| {
39 ParseError::ComClass(
40 item.ident.to_string(),
41 format!("Attribute syntax error: {}", e),
42 )
43 })?;
44
45 let clsid_attr = attr
47 .clsid()
48 .map_err(|msg| ParseError::ComClass(item.ident.to_string(), msg))?;
49 let clsid = match clsid_attr {
50 None => Some(crate::utils::generate_clsid(
51 crate_name,
52 &item.ident.to_string(),
53 )),
54 Some(StrOption::Str(clsid)) => Some(GUID::parse(&clsid.value()).map_err(|_| {
55 ParseError::ComClass(item.ident.to_string(), "Bad CLSID format".into())
56 })?),
57 Some(StrOption::None) => None,
58 };
59
60 let name = item.ident.clone();
62 let interfaces = attr
63 .args()
64 .into_iter()
65 .map(|itf| match itf.get_ident() {
66 Some(ident) if ident == "Self" => parse_quote!(#name),
67 _ => itf.clone(),
68 })
69 .collect();
70
71 Ok(ComClass {
72 visibility: item.vis.clone(),
73 generics: item.generics,
74 name,
75 clsid,
76 interfaces,
77 })
78 }
79
80 pub fn is_self_path(&self, path: &Path) -> bool
82 {
83 if let Some(ident) = path.get_ident() {
87 if ident == &self.name || ident == "Self" {
90 return true;
91 }
92 }
93
94 false
95 }
96}
97
98#[cfg(test)]
99mod test
100{
101 use super::*;
102
103 #[test]
104 fn parse_com_class()
105 {
106 let cls = ComClass::parse(
107 "not used",
108 quote!(clsid = "12345678-1234-1234-1234-567890ABCDEF", Foo, Bar),
109 quote!(
110 struct S;
111 ),
112 )
113 .expect("com_class attribute parsing failed");
114
115 assert_eq!(cls.name, "S");
116 assert_eq!(
117 cls.clsid,
118 Some(GUID::parse("12345678-1234-1234-1234-567890ABCDEF").unwrap())
119 );
120 assert_eq!(cls.interfaces.len(), 2);
121 assert_eq!(cls.interfaces[0], parse_quote!(Foo));
122 assert_eq!(cls.interfaces[1], parse_quote!(Bar));
123 }
124
125 #[test]
126 fn parse_com_class_with_auto_guid()
127 {
128 let cls = ComClass::parse(
134 "not used",
135 quote!(MyStruct, IThings, IStuff),
136 quote!(
137 struct MyStruct
138 {
139 a: u32,
140 }
141 ),
142 )
143 .expect("com_class attribute parsing failed");
144
145 assert_eq!(cls.name, "MyStruct");
146 assert_eq!(
147 cls.clsid,
148 Some(GUID::parse("28F57CBA-6AF4-3D3F-7C55-1CF1394D5C7A").unwrap())
149 );
150 assert_eq!(cls.interfaces.len(), 3);
151 assert_eq!(cls.interfaces[0], parse_quote!(MyStruct));
152 assert_eq!(cls.interfaces[1], parse_quote!(IThings));
153 assert_eq!(cls.interfaces[2], parse_quote!(IStuff));
154 }
155
156 #[test]
157 fn parse_com_class_with_no_data()
158 {
159 let cls = ComClass::parse(
160 "not used",
161 quote!(clsid = None),
162 quote!(
163 struct EmptyType;
164 ),
165 )
166 .expect("com_class attribute parsing failed");
167
168 assert_eq!(cls.name, "EmptyType");
169 assert_eq!(cls.clsid, None);
170 assert_eq!(cls.interfaces.len(), 0);
171 }
172
173 #[test]
174 fn parse_com_class_with_no_guid_with_interface()
175 {
176 let cls = ComClass::parse(
177 "not used",
178 quote!(clsid = None, ITestInterface),
179 quote!(
180 struct EmptyType;
181 ),
182 )
183 .expect("com_class attribute parsing failed");
184
185 assert_eq!(cls.name, "EmptyType");
186 assert_eq!(cls.clsid, None);
187 assert_eq!(cls.interfaces.len(), 1);
188 }
189}