use ::prelude::*;
use std::rc::Rc;
use syn::*;
use ast_converters::*;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }
pub struct TypeConversion {
pub temporary: Option<TokenStream>,
pub value : TokenStream,
}
#[derive(PartialEq, Eq, Debug, Hash)]
pub struct ModelTypeSystemConfig {
pub effective_system : ModelTypeSystem,
pub is_default : bool,
}
impl ModelTypeSystemConfig {
pub fn get_unique_name( &self, base : &str ) -> String {
match self.is_default {
true => base.to_string(),
false => format!( "{}_{:?}", base, self.effective_system ),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub enum ModelTypeSystem {
Automation,
Raw,
}
impl ModelTypeSystem {
pub fn as_typesystem_tokens( self ) -> TokenStream {
match self {
ModelTypeSystem::Automation =>
quote!( ::intercom::TypeSystem::Automation ),
ModelTypeSystem::Raw =>
quote!( ::intercom::TypeSystem::Raw ),
}
}
}
pub struct TypeContext {
type_system: ModelTypeSystem,
}
impl TypeContext {
pub fn new( type_system : ModelTypeSystem ) -> TypeContext {
TypeContext { type_system }
}
}
pub trait TypeHandler {
fn rust_ty( &self ) -> Type;
fn com_ty( &self, _dir: Direction ) -> Type
{
self.rust_ty()
}
fn com_to_rust(
&self,
ident : &Ident,
_dir: Direction,
) -> TypeConversion
{
TypeConversion {
temporary: None,
value: quote!( #ident.into() ),
}
}
fn rust_to_com(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
TypeConversion {
temporary: None,
value: quote!( #ident.into() )
}
}
fn default_value( &self ) -> TokenStream
{
match self.rust_ty() {
Type::Path( ref p ) => {
let ident = p.path.get_ident().unwrap();
let name = ident.to_string();
match name.as_ref() {
"RawComPtr" => quote!( ::std::ptr::null_mut() ),
_ => quote!( Default::default() )
}
},
Type::Ptr( .. ) => quote!( ::std::ptr::null_mut() ),
_ => quote!( Default::default() )
}
}
}
struct IdentityParam( Type );
impl TypeHandler for IdentityParam {
fn rust_ty( &self ) -> Type { self.0.clone() }
}
struct ComItfParam { ty: Type, context: TypeContext }
impl TypeHandler for ComItfParam {
fn rust_ty( &self ) -> Type { self.ty.clone() }
fn com_ty( &self, _dir: Direction ) -> Type
{
let rust_ty = self.rust_ty();
let itf_ty = match rust_ty {
syn::Type::Path( path ) =>
match path.path.segments.last().unwrap().value().arguments {
syn::PathArguments::AngleBracketed( ref ab ) =>
match ab.args.last().unwrap().value() {
syn::GenericArgument::Type( ref t ) => t.clone(),
_ => panic!( "ComItf generic argument must be type" ),
},
_ => panic!( "ComItf type parameter must be angle bracketed" ),
},
_ => panic!( "ComItf type parameter must be a type path" ),
};
parse_quote!( ::intercom::raw::InterfacePtr< #itf_ty > )
}
fn default_value( &self ) -> TokenStream
{
quote!( ::intercom::raw::InterfacePtr::new( ::std::ptr::null_mut() ) )
}
fn com_to_rust(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
let ts = self.context.type_system.as_typesystem_tokens();
TypeConversion {
temporary: None,
value: quote!( ::intercom::ComItf::wrap( #ident, #ts ) ),
}
}
fn rust_to_com(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
let ts = self.context.type_system.as_typesystem_tokens();
TypeConversion {
temporary: None,
value: quote!( ::intercom::ComItf::ptr( &#ident.into(), #ts ) )
}
}
}
struct BoolParam { context: TypeContext }
impl TypeHandler for BoolParam {
fn rust_ty( &self ) -> Type { parse_quote!( bool ) }
fn com_ty( &self, _dir: Direction ) -> Type
{
match self.context.type_system {
ModelTypeSystem::Automation => parse_quote!( ::intercom::raw::VariantBool ),
ModelTypeSystem::Raw => parse_quote!( bool ),
}
}
fn default_value( &self ) -> TokenStream
{
match self.context.type_system {
ModelTypeSystem::Automation => quote!( false.into() ),
ModelTypeSystem::Raw => quote!( false ),
}
}
fn com_to_rust(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
TypeConversion {
temporary: None,
value: quote!( #ident.into() )
}
}
fn rust_to_com(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
TypeConversion {
temporary: None,
value: quote!( #ident.into() )
}
}
}
struct VariantParam { ty: Type }
impl TypeHandler for VariantParam {
fn rust_ty( &self ) -> Type { self.ty.clone() }
fn com_ty( &self, _dir: Direction ) -> Type
{
parse_quote!( ::intercom::raw::Variant )
}
fn com_to_rust(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
TypeConversion {
temporary: None,
value: quote!( #ident.into() )
}
}
fn rust_to_com(
&self, ident : &Ident, _dir: Direction
) -> TypeConversion
{
TypeConversion {
temporary: None,
value: quote!( #ident.com_into()? )
}
}
}
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
fn rust_ty( &self ) -> Type { self.ty.clone() }
fn com_ty( &self, dir: Direction ) -> Type
{
match self.context.type_system {
ModelTypeSystem::Automation => match dir {
Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
},
ModelTypeSystem::Raw => match dir {
Direction::In => parse_quote!( ::intercom::raw::InCStr ),
Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutCStr ),
},
}
}
fn com_to_rust( &self, ident : &Ident, dir: Direction ) -> TypeConversion
{
match dir {
Direction::In => {
let str_type = match self.context.type_system {
ModelTypeSystem::Automation => quote!( BStr ),
ModelTypeSystem::Raw => quote!( CStr ),
};
let target_ty = self.rust_ty();
let intermediate_ty = quote!( &::intercom::#str_type );
let to_intermediate = quote!( ::intercom::#str_type::from_ptr( #ident ) );
let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );
let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
TypeConversion {
temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
}
},
Direction::Out | Direction::Retval => {
let str_type = match self.context.type_system {
ModelTypeSystem::Automation => quote!( BString::from_ptr ),
ModelTypeSystem::Raw => quote!( CString::from_raw ),
};
let ts_string = quote!( ::intercom::#str_type( #ident ) );
TypeConversion {
temporary: None,
value: quote!( #ts_string.com_into()? ),
}
},
}
}
fn rust_to_com( &self, ident : &Ident, dir: Direction ) -> TypeConversion
{
match dir {
Direction::In => {
let str_type = match self.context.type_system {
ModelTypeSystem::Automation => quote!( BStr ),
ModelTypeSystem::Raw => quote!( CStr ),
};
let target_ty = self.rust_ty();
let intermediate_ty = quote!( &::intercom::#str_type );
let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );
let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
TypeConversion {
temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
}
},
Direction::Out | Direction::Retval => {
let str_type = match self.context.type_system {
ModelTypeSystem::Automation => quote!( BString ),
ModelTypeSystem::Raw => quote!( CString ),
};
let ts_string = quote!( ::intercom::ComInto::< ::intercom::#str_type >::com_into( #ident )? );
TypeConversion {
temporary: None,
value: match self.context.type_system {
ModelTypeSystem::Automation =>
quote!( #ts_string.into_ptr() ),
ModelTypeSystem::Raw =>
quote!( #ts_string.into_raw() ),
}
}
},
}
}
fn default_value( &self ) -> TokenStream
{
quote!( ::std::ptr::null_mut() )
}
}
pub fn get_ty_handler(
arg_ty : &Type,
context : TypeContext,
) -> Rc<dyn TypeHandler>
{
let type_info = ::type_parser::parse( arg_ty )
.unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );
map_by_name(
type_info.get_name().as_ref(), type_info.original.clone(),
context )
}
fn map_by_name(
name: &str,
original_type: Type,
context: TypeContext,
) -> Rc<dyn TypeHandler> {
match name {
"ComItf" => Rc::new( ComItfParam { ty: original_type, context } ),
"CString" | "CStr" | "BString" | "BStr" | "String" | "str" =>
Rc::new( StringParam { ty: original_type, context } ),
"bool" =>
Rc::new( BoolParam { context } ),
"Variant" =>
Rc::new( VariantParam { ty: original_type } ),
_ => Rc::new( IdentityParam( original_type ) )
}
}