use std::io::Write;
use std::path::Path;
use super::GeneratorError;
use utils;
use model;
use model::{ComCrate, ComInterfaceVariant};
use ast_converters::GetIdent;
use tyhandlers::{Direction, ModelTypeSystem, ModelTypeSystemConfig};
use foreign_ty::*;
use type_parser::*;
use handlebars::Handlebars;
#[derive(PartialEq, Serialize, Debug)]
pub struct IdlModel {
pub lib_id : String,
pub lib_name : String,
pub interfaces : Vec<IdlInterface>,
pub coclasses : Vec<IdlCoClass>,
}
#[derive(PartialEq, Serialize, Debug)]
pub struct IdlInterface {
pub name : String,
pub base : Option<String>,
pub iid : String,
pub methods : Vec<IdlMethod>,
}
#[derive(PartialEq, Serialize, Debug)]
pub struct IdlMethod {
pub name : String,
pub idx : usize,
pub ret_type : String,
pub args : Vec<IdlArg>,
}
#[derive(PartialEq, Serialize, Debug)]
pub struct IdlArg {
pub name : String,
pub arg_type : String,
pub attributes : String,
}
#[derive(PartialEq, Serialize, Debug)]
pub struct IdlCoClass {
pub name : String,
pub clsid : String,
pub interfaces : Vec<String>,
}
trait IdlTypeInfo<'s> {
fn to_idl(
&self,
krate : &ComCrate,
ts_config : &ModelTypeSystemConfig,
) -> String;
fn get_idl_type_name(
&self,
krate: &model::ComCrate,
ts_config : &ModelTypeSystemConfig,
) -> String;
fn is_pointer(
&self
) -> bool;
}
impl IdlModel {
pub fn from_path(
path : &Path,
all_type_systems : bool,
) -> Result<IdlModel, GeneratorError>
{
let krate = model::ComCrate::parse_package( path )
.map_err( GeneratorError::CrateParseError )?;
IdlModel::from_crate( &krate, all_type_systems )
}
pub fn from_crate(
c : &model::ComCrate,
all_type_systems : bool,
) -> Result<IdlModel, GeneratorError> {
let itf_variant_filter : Box<dyn Fn( &( &ModelTypeSystem, &ComInterfaceVariant ) ) -> bool> =
match all_type_systems {
true => Box::new( | _ | true ),
false => Box::new( | ( ts, _ ) | match ts {
ModelTypeSystem::Automation => true,
_ => false
} ),
};
let foreign = CTypeHandler;
let lib = c.lib().as_ref().ok_or( GeneratorError::MissingLibrary )?;
let itfs = c.interfaces().iter()
.flat_map(|(_, itf)| itf.variants().iter()
.filter( itf_variant_filter.as_ref() )
.map(|(&ts, itf_variant)| {
let methods = itf_variant.methods().iter().enumerate().map(|(i,m)| {
let ts_config = ModelTypeSystemConfig {
effective_system: ts,
is_default: ! all_type_systems,
};
let args = m.raw_com_args().iter().map(|a| {
let ( attrs, out_ptr ) = match a.dir {
Direction::In => ( "in", "" ),
Direction::Out => ( "out", "*" ),
Direction::Retval => ( "out, retval", "*" ),
};
let com_ty = a.handler.com_ty( a.dir );
let idl_type = foreign
.get_ty( &com_ty )
.ok_or_else( || GeneratorError::UnsupportedType(
utils::ty_to_string( &a.ty ) ) )?;
Ok( IdlArg {
name : a.name.to_string(),
arg_type : format!( "{}{}", idl_type.to_idl( c, &ts_config ), out_ptr ),
attributes : attrs.to_owned(),
} )
} ).collect::<Result<Vec<_>, GeneratorError>>()?;
let ret_ty = m.returnhandler.com_ty();
let ret_ty = foreign
.get_ty( &ret_ty )
.ok_or_else( || GeneratorError::UnsupportedType(
utils::ty_to_string( &ret_ty ) ) )?;
Ok( IdlMethod {
name: utils::pascal_case( m.display_name.to_string() ),
idx: i,
ret_type: ret_ty.to_idl( c, &ts_config ),
args
} )
} ).collect::<Result<Vec<_>, GeneratorError>>()?;
let ( itf_name, base_name ) = match all_type_systems {
false => (
itf.name(),
itf.base_interface() ),
true => (
itf_variant.unique_name(),
itf_variant.unique_base_interface() ),
};
Ok( IdlInterface {
name: foreign.get_name( c, itf_name ),
base: base_name.as_ref().map( |i| foreign.get_name( c, i ) ),
iid: format!( "{:-X}", itf_variant.iid() ),
methods,
} )
} )
.collect::<Vec<_>>() )
.collect::<Result<Vec<_>, GeneratorError>>()?;
let classes = lib.coclasses().iter().map(|class_path| {
let class_name = class_path
.get_ident()
.expect( "coclass had no name" )
.to_string();
let coclass = &c.structs().get( &class_name )
.ok_or_else( || GeneratorError::TypeNotFound( class_name ) )?;
let interfaces = coclass.interfaces().iter()
.flat_map(|itf_name| {
let result = c.interfaces().get( &itf_name.to_string() )
.ok_or_else( || GeneratorError::TypeNotFound( itf_name.to_string() ) );
match result {
Ok( itf ) => itf.variants().iter()
.filter( itf_variant_filter.as_ref() )
.map( |(_, itf_variant)| {
Ok( foreign.get_name( c, match all_type_systems {
false => itf.name(),
true => itf_variant.unique_name(),
} ) )
} ).collect::<Vec<_>>(),
Err( e ) => vec![ Err( e ) ],
}
} )
.collect::<Result<Vec<_>, GeneratorError>>()?;
let clsid = coclass.clsid().as_ref()
.ok_or_else( || GeneratorError::MissingClsid(
coclass.name().to_string() ) )?;
Ok( IdlCoClass {
name : coclass.name().to_string(),
clsid: format!( "{:-X}", clsid ),
interfaces
} )
} ).collect::<Result<_,GeneratorError>>()?;
Ok( IdlModel {
lib_id : format!( "{:-X}", lib.libid() ),
lib_name : utils::pascal_case( lib.name() ),
interfaces : itfs,
coclasses : classes,
} )
}
pub fn write(
&self,
out : &mut dyn Write
) -> Result<(), GeneratorError> {
let mut reg = Handlebars::new();
reg.register_template_string( "idl", include_str!( "idl.hbs" ) )
.expect( "Error in the built-in IDL template." );
let rendered = reg
.render( "idl", self )
.expect( "Rendering a valid ComCrate to IDL failed" );
write!( out, "{}", rendered )?;
Ok(())
}
}
impl<'s> dyn IdlTypeInfo<'s> {
fn get_idl_name_for_custom_type(
krate : &ComCrate,
ty_name : &str,
ts_config : &ModelTypeSystemConfig,
) -> String {
let itf = if let Some( itf ) = krate.interfaces().get( ty_name ) {
itf
} else {
return ty_name.to_owned()
};
let base_name = if itf.item_type() == ::utils::InterfaceType::Struct {
format!( "I{}", itf.name() )
} else {
ty_name.to_string()
};
ts_config.get_unique_name( &base_name )
}
}
impl<'s> IdlTypeInfo<'s> for TypeInfo<'s> {
fn to_idl(
&self,
krate : &ComCrate,
ts_config : &ModelTypeSystemConfig,
) -> String {
let const_specifier = if self.is_mutable || self.pass_by != PassBy::Reference { "" } else { "const " };
let type_name = self.get_leaf().get_idl_type_name( krate, ts_config );
let ptr = if self.is_pointer() { "*" } else { "" };
format!("{}{}{}", const_specifier, type_name, ptr )
}
fn get_idl_type_name(
&self,
krate : &ComCrate,
ts_config : &ModelTypeSystemConfig,
) -> String {
let type_name = self.get_name();
match type_name.as_str() {
"RawComPtr" => "void*".to_owned(),
"InBSTR" | "OutBSTR" => "BSTR".to_owned(),
"InCStr" | "OutCStr" => "char*".to_owned(),
"usize" => "size_t".to_owned(),
"u64" => "uint64".to_owned(),
"i64" => "int64".to_owned(),
"u32" => "uint32".to_owned(),
"i32" => "int32".to_owned(),
"u16" => "uint16".to_owned(),
"i16" => "int16".to_owned(),
"u8" => "uint8".to_owned(),
"i8" => "int8".to_owned(),
"f64" => "double".to_owned(),
"f32" => "float".to_owned(),
"VariantBool" => "VARIANT_BOOL".to_owned(),
"Variant" => "VARIANT".to_owned(),
"c_void" => "void".to_owned(),
"c_char" => "char".to_owned(),
t => IdlTypeInfo::get_idl_name_for_custom_type( krate, t, ts_config ),
}
}
fn is_pointer(
&self
) -> bool
{
if let RustType::Wrapper( _, _ ) = self.rust_type {
return true;
}
match self.pass_by {
PassBy::Value => false,
PassBy::Reference | PassBy::Ptr => true,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn crate_to_idl() {
let krate = model::ComCrate::parse( "com_library", &[ r#"
com_library!( libid = "11112222-3333-4444-5555-666677778888", CoClass );
#[com_interface( com_iid = "22223333-4444-5555-6666-777788889999", base = NO_BASE )]
trait IInterface {
fn method( &self, a : u32 ) -> ComResult<bool>;
}
#[com_class( clsid = "33334444-5555-6666-7777-888899990000", CoClass, IInterface )]
struct CoClass;
#[com_interface( com_iid = "44445555-6666-7777-8888-99990000AAAA" )]
#[com_impl]
impl CoClass {
pub fn new() -> CoClass { CoClass }
pub fn com_method( &self, b : u32 ) {}
}
#[com_impl]
impl IInterface for CoClass {
fn method( &self, a : u32 ) -> ComResult<bool> { unreachable!() }
}
"# ] ).expect( "Could not parse test crate" );
let expected_idl = IdlModel {
lib_id : "11112222-3333-4444-5555-666677778888".to_owned(),
lib_name : "ComLibrary".to_owned(),
interfaces : vec![
IdlInterface {
name : "IInterface".to_owned(),
base : None,
iid : "22223333-4444-5555-6666-777788889999".to_owned(),
methods : vec![
IdlMethod {
name : "Method".to_owned(),
idx : 0,
ret_type : "HRESULT".to_owned(),
args : vec![
IdlArg {
name : "a".to_owned(),
arg_type : "uint32".to_owned(),
attributes : "in".to_owned(),
},
IdlArg {
name : "__out".to_owned(),
arg_type : "VARIANT_BOOL*".to_owned(),
attributes : "out, retval".to_owned(),
},
]
}
]
},
IdlInterface {
name : "ICoClass".to_owned(),
base : Some( "IUnknown".to_owned() ),
iid : "44445555-6666-7777-8888-99990000AAAA".to_owned(),
methods : vec![
IdlMethod {
name : "ComMethod".to_owned(),
idx : 0,
ret_type : "void".to_owned(),
args : vec![
IdlArg {
name : "b".to_owned(),
arg_type : "uint32".to_owned(),
attributes : "in".to_owned(),
},
]
}
]
},
IdlInterface {
name : "IAllocator".to_owned(),
base : Some( "IUnknown".to_owned() ),
iid : "18EE22B3-B0C6-44A5-A94A-7A417676FB66".to_owned(),
methods : vec![
IdlMethod {
name : "AllocBstr".to_owned(),
idx : 0,
ret_type : "BSTR".to_owned(),
args : vec![
IdlArg {
name : "text".to_owned(),
arg_type : "uint16*".to_owned(),
attributes : "in".to_owned(),
},
IdlArg {
name : "len".to_owned(),
arg_type : "uint32".to_owned(),
attributes : "in".to_owned(),
},
]
},
IdlMethod {
name : "FreeBstr".to_owned(),
idx : 1,
ret_type : "void".to_owned(),
args : vec![
IdlArg {
name : "bstr".to_owned(),
arg_type : "BSTR".to_owned(),
attributes : "in".to_owned(),
},
]
},
IdlMethod {
name : "Alloc".to_owned(),
idx : 2,
ret_type : "void*".to_owned(),
args : vec![
IdlArg {
name : "len".to_owned(),
arg_type : "size_t".to_owned(),
attributes : "in".to_owned(),
},
]
},
IdlMethod {
name : "Free".to_owned(),
idx : 3,
ret_type : "void".to_owned(),
args : vec![
IdlArg {
name : "ptr".to_owned(),
arg_type : "void*".to_owned(),
attributes : "in".to_owned(),
},
]
},
]
},
IdlInterface {
name : "IErrorStore".to_owned(),
base : Some( "IUnknown".to_owned() ),
iid : "D7F996C5-0B51-4053-82F8-19A7261793A9".to_owned(),
methods : vec![
IdlMethod {
name : "GetErrorInfo".to_owned(),
idx : 0,
ret_type : "HRESULT".to_owned(),
args : vec![
IdlArg {
name : "__out".to_owned(),
arg_type : "IErrorInfo**".to_owned(),
attributes : "out, retval".to_owned(),
},
]
},
IdlMethod {
name : "SetErrorInfo".to_owned(),
idx : 1,
ret_type : "HRESULT".to_owned(),
args : vec![
IdlArg {
name : "info".to_owned(),
arg_type : "IErrorInfo*".to_owned(),
attributes : "in".to_owned(),
},
]
},
IdlMethod {
name : "SetErrorMessage".to_owned(),
idx : 2,
ret_type : "HRESULT".to_owned(),
args : vec![
IdlArg {
name : "msg".to_owned(),
arg_type : "BSTR".to_owned(),
attributes : "in".to_owned(),
},
]
},
]
},
],
coclasses : vec![
IdlCoClass {
name : "CoClass".to_owned(),
clsid : "33334444-5555-6666-7777-888899990000".to_owned(),
interfaces : vec![
"ICoClass".to_owned(),
"IInterface".to_owned(),
],
},
IdlCoClass {
name : "Allocator".to_owned(),
clsid : "EC444090-9CDC-31A4-4023-D0458C5CD45C".to_owned(),
interfaces : vec![
"IAllocator".to_owned(),
]
},
IdlCoClass {
name : "ErrorStore".to_owned(),
clsid : "1467B819-62DF-3720-4EE6-6E76FD4E1120".to_owned(),
interfaces : vec![
"IErrorStore".to_owned(),
]
},
],
};
let actual_idl = IdlModel::from_crate( &krate, false ).unwrap();
assert_eq!( expected_idl, actual_idl );
}
}