1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

use ::prelude::*;
use super::*;

use ::guid::GUID;
use ::syn::{ LitStr, Path };

intercom_attribute!(
    ComLibraryAttr< ComLibraryAttrParam, Path > {
        libid : LitStr,
    }
);

/// COM library details derived from the `com_library` attribute.
#[derive(Debug, PartialEq)]
pub struct ComLibrary {
    name : String,
    libid : GUID,
    coclasses : Vec<Path>,
}

impl ComLibrary
{
    /// Parses a [com_library] attribute.
    pub fn parse(
        crate_name : &str,
        attr_params : TokenStream,
    ) -> ParseResult<ComLibrary>
    {
        let attr : ComLibraryAttr = ::syn::parse2( attr_params )
            .map_err( |_| ParseError::ComLibrary(
                    "Attribute syntax error".into() ) )?;

        // The first parameter is the LIBID of the library.
        let libid = match attr.libid().map_err( ParseError::ComLibrary )? {
            Some( libid ) => GUID::parse( &libid.value() )
                    .map_err( ParseError::ComLibrary )?,
            None => ::utils::generate_libid( crate_name )
        } ;

        Ok( ComLibrary {
            name: crate_name.to_owned(),
            coclasses: attr.args().into_iter().cloned().collect(),
            libid,
        } )
    }

    /// Library name.
    pub fn name( &self ) -> &str { &self.name }

    /// Library LIBID.
    pub fn libid( &self ) -> &GUID { &self.libid }

    /// CoClasses exposed by the library.
    pub fn coclasses( &self ) -> &[Path] { &self.coclasses }

    /// Adds a coclass.
    pub fn add_coclass( &mut self, clsid : Path ) { self.coclasses.push( clsid ) }
}

#[cfg(test)]
mod test
{
    use super::*;

    #[test]
    fn parse_com_library() {

        let lib = ComLibrary::parse(
            "library_name".into(),
            quote!( libid = "12345678-1234-1234-1234-567890ABCDEF",
                    Foo, Bar ) )
                .expect( "com_library attribute parsing failed" );

        assert_eq!( lib.name(), "library_name" );
        assert_eq!( lib.libid(), &GUID {
            data1: 0x12345678,
            data2: 0x1234,
            data3: 0x1234,
            data4: [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF ]
        } );
        assert_eq!( lib.coclasses().len(), 2 );
        assert_eq!( lib.coclasses()[0], parse_quote!( Foo ) );
        assert_eq!( lib.coclasses()[1], parse_quote!( Bar ) );
    }

    #[test]
    fn parse_com_library_with_auto_guid() {

        // This test derives the GUID from the library name.
        //
        // What the final GUID is isn't important, what _is_ important however
        // is that the final GUID will not change ever as long as the library
        // name stays the same.
        let lib = ComLibrary::parse(
            "another_library".into(),
            quote!( One, Two ) )
                .expect( "com_library attribute parsing failed" );

        assert_eq!( lib.name(), "another_library" );
        assert_eq!(
                lib.libid(),
                &GUID::parse( "696B2FAE-AC56-3E08-7C2C-ABAA8DB8F6E3" ).unwrap() );
        assert_eq!( lib.coclasses().len(), 2 );
        assert_eq!( lib.coclasses()[0], parse_quote!( One ) );
        assert_eq!( lib.coclasses()[1], parse_quote!( Two ) );
    }

    #[test]
    fn parse_com_library_with_empty_parameters() {

        let lib = ComLibrary::parse( "lib".into(), quote!() ).unwrap();
        assert_eq!( lib.coclasses().len(), 0 );
        assert_eq!(
                lib.libid(),
                &GUID::parse( "22EC0095-CD17-3AFD-6C4F-531464178911" ).unwrap() );
    }
}