intercom_common/model/
comlibrary.rs

1use super::*;
2use crate::prelude::*;
3
4use crate::guid::GUID;
5use syn::{LitStr, Path};
6
7#[derive(Debug, Clone)]
8pub enum LibraryItemType
9{
10    Module(Path),
11    Class(Path),
12    Interface(Path),
13}
14
15impl syn::parse::Parse for LibraryItemType
16{
17    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self>
18    {
19        let ident: syn::Ident = input.parse()?;
20        match ident.to_string().as_str() {
21            "module" => Ok(LibraryItemType::Module(input.parse()?)),
22            "class" => Ok(LibraryItemType::Class(input.parse()?)),
23            "interface" => Ok(LibraryItemType::Interface(input.parse()?)),
24            _ => Err(input.error(&format!(
25                "Expected 'class', 'interface' or 'module', found {}",
26                ident
27            ))),
28        }
29    }
30}
31
32intercom_attribute!(
33    ComLibraryAttr< ComLibraryAttrParam, LibraryItemType > {
34        libid : LitStr,
35        on_load : Path,
36        on_register : Path,
37        on_unregister : Path,
38    }
39);
40
41/// COM library details derived from the `com_library` attribute.
42#[derive(Debug, PartialEq, Eq)]
43pub struct ComLibrary
44{
45    pub name: String,
46    pub libid: GUID,
47    pub on_load: Option<Path>,
48    pub on_register: Option<Path>,
49    pub on_unregister: Option<Path>,
50    pub coclasses: Vec<Path>,
51    pub interfaces: Vec<Path>,
52    pub submodules: Vec<Path>,
53}
54
55impl ComLibrary
56{
57    /// Parses a [com_library] attribute.
58    pub fn parse(crate_name: &str, attr_params: TokenStream) -> ParseResult<ComLibrary>
59    {
60        let attr: ComLibraryAttr = ::syn::parse2(attr_params)
61            .map_err(|_| ParseError::ComLibrary("Attribute syntax error".into()))?;
62
63        // The first parameter is the LIBID of the library.
64        let libid = match attr.libid().map_err(ParseError::ComLibrary)? {
65            Some(libid) => GUID::parse(&libid.value()).map_err(ParseError::ComLibrary)?,
66            None => crate::utils::generate_libid(crate_name),
67        };
68
69        let on_load = attr.on_load().map_err(ParseError::ComLibrary)?.cloned();
70        let on_register = attr.on_register().map_err(ParseError::ComLibrary)?.cloned();
71        let on_unregister = attr
72            .on_unregister()
73            .map_err(ParseError::ComLibrary)?
74            .cloned();
75
76        let mut coclasses = vec![];
77        let mut interfaces = vec![];
78        let mut submodules = vec![];
79        for arg in attr.args().into_iter().cloned() {
80            match arg {
81                LibraryItemType::Class(cls) => coclasses.push(cls),
82                LibraryItemType::Interface(cls) => interfaces.push(cls),
83                LibraryItemType::Module(cls) => submodules.push(cls),
84            }
85        }
86
87        Ok(ComLibrary {
88            name: crate_name.to_owned(),
89            on_load,
90            on_register,
91            on_unregister,
92            coclasses,
93            interfaces,
94            submodules,
95            libid,
96        })
97    }
98}
99
100#[cfg(test)]
101mod test
102{
103    use super::*;
104
105    #[test]
106    fn parse_com_library()
107    {
108        let lib = ComLibrary::parse(
109            "library_name".into(),
110            quote!(
111                libid = "12345678-1234-1234-1234-567890ABCDEF",
112                class Foo,
113                class Bar),
114        )
115        .expect("com_library attribute parsing failed");
116
117        assert_eq!(lib.name, "library_name");
118        assert_eq!(
119            lib.libid,
120            GUID {
121                data1: 0x12345678,
122                data2: 0x1234,
123                data3: 0x1234,
124                data4: [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF]
125            }
126        );
127        assert_eq!(lib.coclasses.len(), 2);
128        assert_eq!(lib.coclasses[0], parse_quote!(Foo));
129        assert_eq!(lib.coclasses[1], parse_quote!(Bar));
130    }
131
132    #[test]
133    fn parse_com_library_with_auto_guid()
134    {
135        // This test derives the GUID from the library name.
136        //
137        // What the final GUID is isn't important, what _is_ important however
138        // is that the final GUID will not change ever as long as the library
139        // name stays the same.
140        let lib = ComLibrary::parse("another_library".into(), quote!(class One, class Two))
141            .expect("com_library attribute parsing failed");
142
143        assert_eq!(lib.name, "another_library");
144        assert_eq!(
145            lib.libid,
146            GUID::parse("696B2FAE-AC56-3E08-7C2C-ABAA8DB8F6E3").unwrap()
147        );
148        assert_eq!(lib.coclasses.len(), 2);
149        assert_eq!(lib.coclasses[0], parse_quote!(One));
150        assert_eq!(lib.coclasses[1], parse_quote!(Two));
151    }
152
153    #[test]
154    fn parse_com_library_with_empty_parameters()
155    {
156        let lib = ComLibrary::parse("lib".into(), quote!()).unwrap();
157        assert_eq!(lib.coclasses.len(), 0);
158        assert_eq!(
159            lib.libid,
160            GUID::parse("22EC0095-CD17-3AFD-6C4F-531464178911").unwrap()
161        );
162    }
163}