use ::prelude::*;
use super::*;
use super::macros::*;
use ::guid::GUID;
use ::ast_converters::*;
use ::methodinfo::ComMethodInfo;
use ::syn::{ Ident, Visibility, LitStr };
use ::ordermap::OrderMap;
use ::std::iter::FromIterator;
use ::tyhandlers::{ModelTypeSystem};
intercom_attribute!(
ComInterfaceAttr< ComInterfaceAttrParam, NoParams > {
com_iid : LitStr,
raw_iid : LitStr,
base : Ident,
}
);
impl ComInterfaceAttr {
pub fn iid( &self, ts : ModelTypeSystem ) -> Result< Option< &LitStr >, String > {
match ts {
ModelTypeSystem::Raw => self.raw_iid(),
ModelTypeSystem::Automation => self.com_iid(),
}
}
}
#[derive(Debug, PartialEq)]
pub struct ComInterface
{
display_name : Ident,
visibility : Visibility,
base_interface : Option<Ident>,
variants : OrderMap<ModelTypeSystem, ComInterfaceVariant>,
item_type: ::utils::InterfaceType,
is_unsafe : bool,
}
#[derive(Debug, PartialEq)]
pub struct ComInterfaceVariant
{
display_name : Ident,
unique_name : Ident,
unique_base_interface : Option<Ident>,
type_system : ModelTypeSystem,
iid : GUID,
methods : Vec<ComMethodInfo>,
}
impl ComInterface
{
pub fn parse(
crate_name : &str,
attr_params : TokenStream,
item : &str,
) -> ParseResult<ComInterface>
{
let item : ::syn::Item = ::syn::parse_str( item )
.map_err( |_| ParseError::ComInterface(
"<Unknown>".into(),
"Item syntax error".into() ) )?;
Self::from_ast( crate_name, attr_params, &item )
}
pub fn from_ast(
crate_name : &str,
attr : TokenStream,
item : &::syn::Item,
) -> ParseResult< ComInterface >
{
let attr : ComInterfaceAttr = ::syn::parse2( attr )
.map_err( |_| ParseError::ComInterface(
item.get_ident().unwrap().to_string(),
"Attribute syntax error".into() ) )?;
let ( itf_ident, fns, itf_type, unsafety ) =
::utils::get_ident_and_fns( item )
.ok_or_else( || ParseError::ComInterface(
item.get_ident().unwrap().to_string(),
"Unsupported associated item".into() ) )?;
let base = attr.base()
.map_err( |msg| ParseError::ComInterface(
item.get_ident().unwrap().to_string(), msg ) )?;
let base = match base {
Some( b ) => if b == "NO_BASE" { None } else { Some( b.to_owned() ) },
None => Some( Ident::new( "IUnknown", Span::call_site() ) ),
};
let visibility = if let ::syn::Item::Trait( ref t ) = *item {
t.vis.clone()
} else {
parse_quote!( pub )
};
let variants = OrderMap::from_iter(
[ ModelTypeSystem::Automation, ModelTypeSystem::Raw ].iter().map( |&ts| {
let itf_unique_ident = Ident::new(
&format!( "{}_{:?}", itf_ident.to_string(), ts ), Span::call_site() );
let unique_base = match base {
Some( ref iunk ) if iunk == "IUnknown" => base.clone(),
ref b => b.as_ref().map( |b| Ident::new( &format!( "{}_{:?}", b, ts ), Span::call_site() ) )
};
let iid_attr = attr.iid( ts )
.map_err( |msg| ParseError::ComInterface(
item.get_ident().unwrap().to_string(), msg ) )?;
let iid = match iid_attr {
Some( iid ) => GUID::parse( &iid.value() )
.map_err( |_| ParseError::ComInterface(
item.get_ident().unwrap().to_string(),
"Bad IID format".into() ) )?,
None => ::utils::generate_iid(
crate_name, &itf_unique_ident.to_string(), ts )
};
let methods = fns.iter()
.map( | sig |
ComMethodInfo::new( sig, ts ) )
.filter_map( Result::ok )
.collect::<Vec<_>>();
Ok( ( ts, ComInterfaceVariant {
display_name: itf_ident.clone(),
unique_name : itf_unique_ident,
unique_base_interface : unique_base,
type_system : ts,
iid,
methods,
} ) )
} ).collect::<Result<Vec<_>,_>>()? );
Ok( ComInterface {
display_name: itf_ident,
base_interface: base,
item_type: itf_type,
is_unsafe : unsafety.is_some(),
visibility,
variants,
} )
}
pub fn aut( &self ) -> &ComInterfaceVariant {
&self.variants[ &ModelTypeSystem::Automation ]
}
pub fn name( &self ) -> &Ident { &self.display_name }
pub fn visibility( &self ) -> &Visibility { &self.visibility }
pub fn base_interface( &self ) -> &Option<Ident> { &self.base_interface }
pub fn variants( &self ) -> &OrderMap<ModelTypeSystem, ComInterfaceVariant> { &self.variants }
pub fn item_type( &self ) -> ::utils::InterfaceType { self.item_type }
pub fn is_unsafe( &self ) -> bool { self.is_unsafe }
}
impl ComInterfaceVariant {
pub fn unique_name( &self ) -> &Ident { &self.unique_name }
pub fn unique_base_interface( &self ) -> &Option<Ident> { &self.unique_base_interface }
pub fn methods( &self ) -> &Vec<ComMethodInfo> { &self.methods }
pub fn iid( &self ) -> &GUID { &self.iid }
pub fn type_system( &self ) -> ModelTypeSystem { self.type_system }
}
#[cfg(test)]
mod test
{
use super::*;
use tyhandlers::ModelTypeSystem::*;
#[test]
fn parse_com_interface() {
let itf = ComInterface::parse(
"not used",
quote!(
com_iid = "12345678-1234-1234-1234-567890ABCDEF",
raw_iid = "12345678-1234-1234-1234-567890FEDCBA",
),
"trait ITrait { fn foo( &self ); fn bar( &self ); }" )
.expect( "com_interface attribute parsing failed" );
assert_eq!( itf.name(), "ITrait" );
assert_eq!( itf.visibility(), &Visibility::Inherited );
assert_eq!( itf.base_interface().as_ref().unwrap(), "IUnknown" );
let variant = &itf.variants[ &Automation ];
assert_eq!( variant.iid(),
&GUID::parse( "12345678-1234-1234-1234-567890ABCDEF" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].display_name, "foo" );
assert_eq!( variant.methods[1].display_name, "bar" );
let variant = &itf.variants[ &Raw ];
assert_eq!( variant.iid(),
&GUID::parse( "12345678-1234-1234-1234-567890FEDCBA" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].display_name, "foo" );
assert_eq!( variant.methods[1].display_name, "bar" );
}
#[test]
fn parse_com_interface_with_auto_guid() {
let itf = ComInterface::parse(
"not used",
quote!(),
"pub trait IAutoGuid { fn one( &self ); fn two( &self ); }" )
.expect( "com_interface attribute parsing failed" );
assert_eq!( itf.name(), "IAutoGuid" );
let pub_visibility : Visibility = parse_quote!( pub );
assert_eq!( itf.visibility(), &pub_visibility );
assert_eq!( itf.base_interface().as_ref().unwrap(), "IUnknown" );
let variant = &itf.variants[ &Automation ];
assert_eq!( variant.iid(),
&GUID::parse( "3DC87B73-0998-30B6-75EA-D4F564454D4B" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].display_name, "one" );
assert_eq!( variant.methods[1].display_name, "two" );
let variant = &itf.variants[ &Raw ];
assert_eq!( variant.iid(),
&GUID::parse( "D552E455-9FB2-34A2-61C0-34BDE0A9095D" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].display_name, "one" );
assert_eq!( variant.methods[1].display_name, "two" );
}
#[test]
fn parse_com_interface_with_base_interface() {
let itf = ComInterface::parse(
"not used",
quote!( base = IBase ),
"pub trait IAutoGuid { fn one( &self ); fn two( &self ); }" )
.expect( "com_interface attribute parsing failed" );
assert_eq!( itf.name(), "IAutoGuid" );
let pub_visibility : Visibility = parse_quote!( pub );
assert_eq!( itf.visibility(), &pub_visibility );
assert_eq!( itf.base_interface().as_ref().unwrap(), "IBase" );
let variant = &itf.variants[ &ModelTypeSystem::Automation];
assert_eq!( variant.iid(),
&GUID::parse( "3DC87B73-0998-30B6-75EA-D4F564454D4B" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].display_name, "one" );
assert_eq!( variant.methods[0].unique_name, "one_Automation" );
assert_eq!( variant.methods[1].display_name, "two" );
assert_eq!( variant.methods[1].unique_name, "two_Automation" );
let variant = &itf.variants[ &ModelTypeSystem::Raw];
assert_eq!( variant.iid(),
&GUID::parse( "D552E455-9FB2-34A2-61C0-34BDE0A9095D" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].display_name, "one" );
assert_eq!( variant.methods[0].unique_name, "one_Raw" );
assert_eq!( variant.methods[1].display_name, "two" );
assert_eq!( variant.methods[1].unique_name, "two_Raw" );
}
#[test]
fn parse_com_interface_with_no_base_interface() {
let itf = ComInterface::parse(
"not used",
quote!( base = NO_BASE ),
"pub trait IAutoGuid { fn one( &self ); fn two( &self ); }" )
.expect( "com_interface attribute parsing failed" );
assert_eq!( itf.name(), "IAutoGuid" );
let pub_visibility : Visibility = parse_quote!( pub );
assert_eq!( itf.visibility(), &pub_visibility );
assert_eq!( itf.base_interface(), &None );
let variant = &itf.variants[ &Automation ];
assert_eq!( variant.iid(),
&GUID::parse( "3DC87B73-0998-30B6-75EA-D4F564454D4B" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].unique_name, "one_Automation" );
assert_eq!( variant.methods[1].unique_name, "two_Automation" );
let variant = &itf.variants[ &Raw ];
assert_eq!( variant.iid(),
&GUID::parse( "D552E455-9FB2-34A2-61C0-34BDE0A9095D" ).unwrap() );
assert_eq!( variant.methods.len(), 2 );
assert_eq!( variant.methods[0].unique_name, "one_Raw" );
assert_eq!( variant.methods[1].unique_name, "two_Raw" );
}
}