use crate::methodinfo::ComArg;
use crate::prelude::*;
use crate::tyhandlers::{self, Direction, ModelTypeSystem, TypeContext};
use crate::utils;
use proc_macro2::Span;
use syn::Type;
pub trait ReturnHandler: ::std::fmt::Debug
{
fn type_system(&self) -> ModelTypeSystem;
fn rust_ty(&self) -> Type;
fn return_type_span(&self) -> Span;
fn is_infallible(&self) -> bool;
fn com_ty(&self) -> Type
{
tyhandlers::get_ty_handler(&self.rust_ty(), TypeContext::new(self.type_system()))
.com_ty(self.return_type_span())
}
fn com_to_rust_return(&self, _result: &Ident) -> TokenStream
{
quote!()
}
fn rust_to_com_return(&self, _result: &Ident) -> TokenStream
{
quote!()
}
fn com_out_args(&self) -> Vec<ComArg>
{
vec![]
}
}
#[derive(Debug)]
struct VoidHandler(Span);
impl ReturnHandler for VoidHandler
{
fn rust_ty(&self) -> Type
{
utils::unit_ty(self.0)
}
fn com_ty(&self) -> Type
{
syn::parse2(quote_spanned!(self.0 => ())).unwrap()
}
fn type_system(&self) -> ModelTypeSystem
{
ModelTypeSystem::Automation
}
fn return_type_span(&self) -> Span
{
self.0
}
fn is_infallible(&self) -> bool
{
true
}
}
#[derive(Debug)]
struct ReturnOnlyHandler(Type, ModelTypeSystem, Span);
impl ReturnHandler for ReturnOnlyHandler
{
fn type_system(&self) -> ModelTypeSystem
{
self.1
}
fn rust_ty(&self) -> Type
{
self.0.clone()
}
fn return_type_span(&self) -> Span
{
self.2
}
fn com_to_rust_return(&self, result: &Ident) -> TokenStream
{
tyhandlers::get_ty_handler(&self.rust_ty(), TypeContext::new(self.1)).com_to_rust(
result,
self.2,
Direction::Retval,
true,
)
}
fn rust_to_com_return(&self, result: &Ident) -> TokenStream
{
tyhandlers::get_ty_handler(&self.rust_ty(), TypeContext::new(self.1)).rust_to_com(
result,
self.2,
Direction::Retval,
true,
)
}
fn com_out_args(&self) -> Vec<ComArg>
{
vec![]
}
fn is_infallible(&self) -> bool
{
true
}
}
#[derive(Debug)]
struct ErrorResultHandler
{
retval_ty: Type,
return_ty: Type,
span: Span,
type_system: ModelTypeSystem,
}
impl ReturnHandler for ErrorResultHandler
{
fn type_system(&self) -> ModelTypeSystem
{
self.type_system
}
fn rust_ty(&self) -> Type
{
self.return_ty.clone()
}
fn return_type_span(&self) -> Span
{
self.span
}
fn com_ty(&self) -> Type
{
let ts = self.type_system.as_typesystem_type(self.span);
syn::parse2(quote_spanned!(self.span=>
< intercom::raw::HRESULT as
intercom::type_system::ExternType< #ts >>
::ForeignType ))
.unwrap()
}
fn com_to_rust_return(&self, result: &Ident) -> TokenStream
{
let (temp_values, ok_values) =
get_rust_ok_values(self.com_out_args(), self.is_infallible());
let ok_values = if ok_values.len() != 1 {
quote!( ( #( #ok_values ),* ) )
} else {
quote!( #( #ok_values )* )
};
quote!(
if #result == intercom::raw::S_OK || #result == intercom::raw::S_FALSE {
#( #temp_values; )*
Ok( #ok_values )
} else {
return Err( intercom::load_error(
self,
&__intercom_iid,
#result ) );
}
)
}
fn rust_to_com_return(&self, result: &Ident) -> TokenStream
{
let ok_idents = self
.com_out_args()
.iter()
.enumerate()
.map(|(idx, _)| Ident::new(&format!("v{}", idx + 1), Span::call_site()))
.collect::<Vec<_>>();
let ok_pattern = {
let rok_idents = &ok_idents;
match self.retval_ty {
Type::Tuple(_) => quote!( ( #( #rok_idents ),* ) ),
_ => quote!( #( #rok_idents )* ),
}
};
let (temp_writes, ok_writes, err_writes) = write_out_values(
&ok_idents,
self.com_out_args(),
self.is_infallible(),
self.span,
self.type_system,
);
quote!(
match #result.and_then(|#ok_pattern| {
#( #temp_writes; )*
#( #ok_writes; )*
Ok( intercom::raw::S_OK )
}) {
Ok( s ) => s,
Err( e ) => {
#( #err_writes );*;
intercom::store_error( e ).hresult
},
}
)
}
fn com_out_args(&self) -> Vec<ComArg>
{
get_out_args_for_result(&self.retval_ty, self.span, self.type_system)
}
fn is_infallible(&self) -> bool
{
false
}
}
fn get_out_args_for_result(
retval_ty: &Type,
span: Span,
type_system: ModelTypeSystem,
) -> Vec<ComArg>
{
match *retval_ty {
Type::Tuple(ref t) => t
.elems
.iter()
.enumerate()
.map(|(idx, ty)| {
ComArg::new(
Ident::new(&format!("__out{}", idx + 1), span),
ty.clone(),
span,
Direction::Out,
type_system,
)
})
.collect::<Vec<_>>(),
_ => vec![ComArg::new(
Ident::new("__out", span),
retval_ty.clone(),
span,
Direction::Retval,
type_system,
)],
}
}
fn write_out_values(
idents: &[Ident],
out_args: Vec<ComArg>,
infallible: bool,
span: Span,
ts: ModelTypeSystem,
) -> (Vec<TokenStream>, Vec<TokenStream>, Vec<TokenStream>)
{
let ts = ts.as_typesystem_type(span);
let mut temp_tokens = vec![];
let mut ok_tokens = vec![];
let mut err_tokens = vec![];
for (ident, out_arg) in idents.iter().zip(out_args) {
let arg_name = out_arg.name;
let temp_name = Ident::new(&format!("__{}_guard", arg_name), span);
let ty = out_arg.ty;
let ok_value = out_arg
.handler
.rust_to_com(ident, span, Direction::Out, infallible);
let err_value = out_arg.handler.default_value();
temp_tokens.push(quote!( let #temp_name = intercom::type_system::OutputGuard::<#ts, #ty>::wrap( #ok_value ) ));
ok_tokens.push(quote!( *#arg_name = #temp_name.consume() ));
err_tokens.push(quote!( *#arg_name = #err_value ));
}
(temp_tokens, ok_tokens, err_tokens)
}
fn get_rust_ok_values(
out_args: Vec<ComArg>,
infallible: bool,
) -> (Vec<TokenStream>, Vec<TokenStream>)
{
let mut temp_tokens = vec![];
let mut ok_tokens = vec![];
for out_arg in out_args {
let value =
out_arg
.handler
.com_to_rust(&out_arg.name, out_arg.span, Direction::Retval, infallible);
let temp_name = Ident::new(&format!("__{}_guard", out_arg.name), out_arg.span);
let unwrap = match infallible {
true => quote!(),
false => quote!(?),
};
temp_tokens.push(quote!(let #temp_name = #value));
ok_tokens.push(quote!(#temp_name#unwrap));
}
(temp_tokens, ok_tokens)
}
pub fn get_return_handler(
retval_ty: &Option<Type>,
return_ty: &Option<Type>,
span: Span,
type_system: ModelTypeSystem,
) -> Result<Box<dyn ReturnHandler>, &'static str>
{
Ok(match (retval_ty, return_ty) {
(&None, &None) => Box::new(VoidHandler(span)),
(&None, &Some(ref ty)) => Box::new(ReturnOnlyHandler(ty.clone(), type_system, span)),
(&Some(ref rv), &Some(ref rt)) => Box::new(ErrorResultHandler {
retval_ty: rv.clone(),
return_ty: rt.clone(),
span,
type_system,
}),
_ => return Err("Unsupported return type configuration"),
})
}