intercom_common/
returnhandlers.rs

1use crate::methodinfo::ComArg;
2use crate::prelude::*;
3use crate::tyhandlers::{self, Direction, ModelTypeSystem, TypeContext};
4use crate::utils;
5use proc_macro2::Span;
6use syn::Type;
7
8/// Defines return handler for handling various different return type schemes.
9pub trait ReturnHandler: ::std::fmt::Debug
10{
11    /// Returns the current type system. Used internally by the trait.
12    fn type_system(&self) -> ModelTypeSystem;
13
14    /// The return type of the original Rust method.
15    fn rust_ty(&self) -> Type;
16
17    /// The return type span.
18    fn return_type_span(&self) -> Span;
19
20    /// Infallible status.
21    fn is_infallible(&self) -> bool;
22
23    /// The return type for COM implementation.
24    fn com_ty(&self) -> Type
25    {
26        tyhandlers::get_ty_handler(&self.rust_ty(), TypeContext::new(self.type_system()))
27            .com_ty(self.return_type_span())
28    }
29
30    /// Gets the return statement for converting the COM result into Rust
31    /// return.
32    fn com_to_rust_return(&self, _result: &Ident) -> TokenStream
33    {
34        quote!()
35    }
36
37    /// Gets the return statement for converting the Rust result into COM
38    /// outputs.
39    fn rust_to_com_return(&self, _result: &Ident) -> TokenStream
40    {
41        quote!()
42    }
43
44    /// Gets the COM out arguments that result from the Rust return type.
45    fn com_out_args(&self) -> Vec<ComArg>
46    {
47        vec![]
48    }
49}
50
51/// Void return type.
52#[derive(Debug)]
53struct VoidHandler(Span);
54impl ReturnHandler for VoidHandler
55{
56    fn rust_ty(&self) -> Type
57    {
58        utils::unit_ty(self.0)
59    }
60
61    fn com_ty(&self) -> Type
62    {
63        syn::parse2(quote_spanned!(self.0 => ())).unwrap()
64    }
65
66    // Void types do not depend on the type system.
67    fn type_system(&self) -> ModelTypeSystem
68    {
69        ModelTypeSystem::Automation
70    }
71
72    /// The return type span.
73    fn return_type_span(&self) -> Span
74    {
75        self.0
76    }
77
78    fn is_infallible(&self) -> bool
79    {
80        true
81    }
82}
83
84/// Simple return type with the return value as the immediate value.
85#[derive(Debug)]
86struct ReturnOnlyHandler(Type, ModelTypeSystem, Span);
87impl ReturnHandler for ReturnOnlyHandler
88{
89    fn type_system(&self) -> ModelTypeSystem
90    {
91        self.1
92    }
93
94    fn rust_ty(&self) -> Type
95    {
96        self.0.clone()
97    }
98
99    fn return_type_span(&self) -> Span
100    {
101        self.2
102    }
103
104    fn com_to_rust_return(&self, result: &Ident) -> TokenStream
105    {
106        tyhandlers::get_ty_handler(&self.rust_ty(), TypeContext::new(self.1)).com_to_rust(
107            result,
108            self.2,
109            Direction::Retval,
110            true,
111        )
112    }
113
114    fn rust_to_com_return(&self, result: &Ident) -> TokenStream
115    {
116        tyhandlers::get_ty_handler(&self.rust_ty(), TypeContext::new(self.1)).rust_to_com(
117            result,
118            self.2,
119            Direction::Retval,
120            true,
121        )
122    }
123
124    fn com_out_args(&self) -> Vec<ComArg>
125    {
126        vec![]
127    }
128
129    fn is_infallible(&self) -> bool
130    {
131        true
132    }
133}
134
135/// Result type that supports error info for the `Err` value. Converted to
136/// `[retval]` on success or `HRESULT` + `IErrorInfo` on error.
137#[derive(Debug)]
138struct ErrorResultHandler
139{
140    retval_ty: Type,
141    return_ty: Type,
142    span: Span,
143    type_system: ModelTypeSystem,
144}
145
146impl ReturnHandler for ErrorResultHandler
147{
148    fn type_system(&self) -> ModelTypeSystem
149    {
150        self.type_system
151    }
152    fn rust_ty(&self) -> Type
153    {
154        self.return_ty.clone()
155    }
156    fn return_type_span(&self) -> Span
157    {
158        self.span
159    }
160    fn com_ty(&self) -> Type
161    {
162        let ts = self.type_system.as_typesystem_type(self.span);
163        syn::parse2(quote_spanned!(self.span=>
164            < intercom::raw::HRESULT as
165                intercom::type_system::ExternType< #ts >>
166                    ::ForeignType ))
167        .unwrap()
168    }
169
170    fn com_to_rust_return(&self, result: &Ident) -> TokenStream
171    {
172        // Format the final Ok value.
173        // If there is only one, it should be a raw value;
174        // If there are multiple value turn them into a tuple.
175        let (temp_values, ok_values) =
176            get_rust_ok_values(self.com_out_args(), self.is_infallible());
177        let ok_values = if ok_values.len() != 1 {
178            quote!( ( #( #ok_values ),* ) )
179        } else {
180            quote!( #( #ok_values )* )
181        };
182
183        // Return statement checks for S_OK (should be is_success) HRESULT and
184        // yields either Ok or Err Result based on that.
185        quote!(
186            // TODO: HRESULT::succeeded
187            if #result == intercom::raw::S_OK || #result == intercom::raw::S_FALSE {
188                #( #temp_values; )*
189                Ok( #ok_values )
190            } else {
191                return Err( intercom::load_error(
192                        self,
193                        &__intercom_iid,
194                        #result ) );
195            }
196        )
197    }
198
199    fn rust_to_com_return(&self, result: &Ident) -> TokenStream
200    {
201        // Get the OK idents. We'll use v0, v1, v2, ... depending on the amount
202        // of patterns we need for possible tuples.
203        let ok_idents = self
204            .com_out_args()
205            .iter()
206            .enumerate()
207            .map(|(idx, _)| Ident::new(&format!("v{}", idx + 1), Span::call_site()))
208            .collect::<Vec<_>>();
209
210        // Generate the pattern for the Ok(..).
211        // Tuples get (v0, v1, v2, ..) pattern while everything else is
212        // represented with just Ok( v0 ) as there's just one parameter.
213        let ok_pattern = {
214            // quote! takes ownership of tokens if we allow so let's give them
215            // by reference here.
216            let rok_idents = &ok_idents;
217            match self.retval_ty {
218                Type::Tuple(_) => quote!( ( #( #rok_idents ),* ) ),
219
220                // Non-tuples should have only one ident. Concatenate the vector.
221                _ => quote!( #( #rok_idents )* ),
222            }
223        };
224
225        let (temp_writes, ok_writes, err_writes) = write_out_values(
226            &ok_idents,
227            self.com_out_args(),
228            self.is_infallible(),
229            self.span,
230            self.type_system,
231        );
232        quote!(
233            match #result.and_then(|#ok_pattern| {
234                // These may fail, resulting in early exit from the lambda.
235                #( #temp_writes; )*
236
237                // Once we get here, everything should succeed.
238                #( #ok_writes; )*
239                Ok( intercom::raw::S_OK )
240            }) {
241                Ok( s ) => s,
242                Err( e ) => {
243                    #( #err_writes );*;
244                    intercom::store_error( e ).hresult
245                },
246            }
247        )
248    }
249
250    fn com_out_args(&self) -> Vec<ComArg>
251    {
252        get_out_args_for_result(&self.retval_ty, self.span, self.type_system)
253    }
254
255    fn is_infallible(&self) -> bool
256    {
257        false
258    }
259}
260
261fn get_out_args_for_result(
262    retval_ty: &Type,
263    span: Span,
264    type_system: ModelTypeSystem,
265) -> Vec<ComArg>
266{
267    match *retval_ty {
268        // Tuples map to multiple out args, no [retval].
269        Type::Tuple(ref t) => t
270            .elems
271            .iter()
272            .enumerate()
273            .map(|(idx, ty)| {
274                ComArg::new(
275                    Ident::new(&format!("__out{}", idx + 1), span),
276                    ty.clone(),
277                    span,
278                    Direction::Out,
279                    type_system,
280                )
281            })
282            .collect::<Vec<_>>(),
283        _ => vec![ComArg::new(
284            Ident::new("__out", span),
285            retval_ty.clone(),
286            span,
287            Direction::Retval,
288            type_system,
289        )],
290    }
291}
292
293fn write_out_values(
294    idents: &[Ident],
295    out_args: Vec<ComArg>,
296    infallible: bool,
297    span: Span,
298    ts: ModelTypeSystem,
299) -> (Vec<TokenStream>, Vec<TokenStream>, Vec<TokenStream>)
300{
301    let ts = ts.as_typesystem_type(span);
302    let mut temp_tokens = vec![];
303    let mut ok_tokens = vec![];
304    let mut err_tokens = vec![];
305    for (ident, out_arg) in idents.iter().zip(out_args) {
306        let arg_name = out_arg.name;
307        let temp_name = Ident::new(&format!("__{}_guard", arg_name), span);
308        let ty = out_arg.ty;
309        let ok_value = out_arg
310            .handler
311            .rust_to_com(ident, span, Direction::Out, infallible);
312        let err_value = out_arg.handler.default_value();
313
314        temp_tokens.push(quote!( let #temp_name = intercom::type_system::OutputGuard::<#ts, #ty>::wrap( #ok_value ) ));
315        ok_tokens.push(quote!( *#arg_name = #temp_name.consume() ));
316        err_tokens.push(quote!( *#arg_name = #err_value ));
317    }
318
319    (temp_tokens, ok_tokens, err_tokens)
320}
321
322/// Gets the result as Rust types for a success return value.
323fn get_rust_ok_values(
324    out_args: Vec<ComArg>,
325    infallible: bool,
326) -> (Vec<TokenStream>, Vec<TokenStream>)
327{
328    let mut temp_tokens = vec![];
329    let mut ok_tokens = vec![];
330    for out_arg in out_args {
331        let value =
332            out_arg
333                .handler
334                .com_to_rust(&out_arg.name, out_arg.span, Direction::Retval, infallible);
335        let temp_name = Ident::new(&format!("__{}_guard", out_arg.name), out_arg.span);
336        let unwrap = match infallible {
337            true => quote!(),
338            false => quote!(?),
339        };
340
341        temp_tokens.push(quote!(let #temp_name = #value));
342        ok_tokens.push(quote!(#temp_name#unwrap));
343    }
344    (temp_tokens, ok_tokens)
345}
346
347/// Resolves the correct return handler to use.
348pub fn get_return_handler(
349    retval_ty: &Option<Type>,
350    return_ty: &Option<Type>,
351    span: Span,
352    type_system: ModelTypeSystem,
353) -> Result<Box<dyn ReturnHandler>, &'static str>
354{
355    Ok(match (retval_ty, return_ty) {
356        (&None, &None) => Box::new(VoidHandler(span)),
357        (&None, &Some(ref ty)) => Box::new(ReturnOnlyHandler(ty.clone(), type_system, span)),
358        (&Some(ref rv), &Some(ref rt)) => Box::new(ErrorResultHandler {
359            retval_ty: rv.clone(),
360            return_ty: rt.clone(),
361            span,
362            type_system,
363        }),
364
365        // Unsupported return scheme. Note we are using Result::Err instead of
366        // Option::None here because having no return handler is unsupported
367        // error case.
368        _ => return Err("Unsupported return type configuration"),
369    })
370}