swc_ecma_quote_macros/
ret_type.rs

1use std::any::type_name;
2
3use anyhow::{anyhow, bail, Context, Error};
4use swc_common::{sync::Lrc, FileName, SourceMap};
5use swc_ecma_ast::{AssignTarget, EsVersion};
6use swc_ecma_parser::{lexer::Lexer, PResult, Parser, StringInput};
7use syn::{GenericArgument, PathArguments, Type};
8
9use crate::{ast::ToCode, ctxt::Ctx};
10
11/// Storage for `dyn ToCode`.The first `Box`, which is required to store `dyn
12/// ToCode`, is ignored.
13pub struct BoxWrapper(Box<dyn ToCode>);
14
15impl ToCode for BoxWrapper {
16    fn to_code(&self, cx: &Ctx) -> syn::Expr {
17        (*self.0).to_code(cx)
18    }
19}
20
21pub(crate) fn parse_input_type(input_str: &str, ty: &Type) -> Result<BoxWrapper, Error> {
22    if let Some(ty) = extract_generic("Box", ty) {
23        let node = parse_input_type(input_str, ty).context("failed to parse `T` in Box<T>")?;
24        return Ok(BoxWrapper(Box::new(Box::new(node))));
25    }
26
27    if let Some(ty) = extract_generic("Option", ty) {
28        if input_str.is_empty() {
29            return Ok(BoxWrapper(Box::new(None::<swc_ecma_ast::Expr>)));
30        }
31
32        let node = parse_input_type(input_str, ty).context("failed to parse `T` in Option<T>")?;
33        return Ok(BoxWrapper(Box::new(Some(node))));
34    }
35
36    if let Type::Path(p) = ty {
37        if let Some(ident) = p.path.get_ident() {
38            match &*ident.to_string() {
39                "Expr" => return parse(input_str, &mut |p| p.parse_expr().map(|v| *v)),
40                "Pat" => return parse(input_str, &mut |p| p.parse_pat()),
41                "Stmt" => return parse(input_str, &mut |p| p.parse_stmt_list_item()),
42                "AssignTarget" => {
43                    return parse(input_str, &mut |p| {
44                        Ok(AssignTarget::try_from(p.parse_pat()?)
45                            .expect("failed to parse AssignTarget"))
46                    })
47                }
48                "ModuleItem" => return parse(input_str, &mut |p| p.parse_module_item()),
49                _ => {}
50            }
51        }
52    }
53
54    bail!("Unknown quote type: {:?}", ty);
55}
56
57fn parse<T>(
58    input_str: &str,
59    op: &mut dyn FnMut(&mut Parser<Lexer>) -> PResult<T>,
60) -> Result<BoxWrapper, Error>
61where
62    T: ToCode,
63{
64    let cm = Lrc::new(SourceMap::default());
65    let fm = cm.new_source_file(FileName::Anon.into(), input_str.to_string());
66
67    let lexer = Lexer::new(
68        Default::default(),
69        EsVersion::Es2020,
70        StringInput::from(&*fm),
71        None,
72    );
73    let mut parser = Parser::new_from(lexer);
74    op(&mut parser)
75        .map_err(|err| anyhow!("{:?}", err))
76        .with_context(|| format!("failed to parse input as `{}`", type_name::<T>()))
77        .map(|val| BoxWrapper(Box::new(val)))
78}
79
80fn extract_generic<'a>(name: &str, ty: &'a Type) -> Option<&'a Type> {
81    if let Type::Path(p) = ty {
82        let last = p.path.segments.last().unwrap();
83
84        if !last.arguments.is_empty() && last.ident == name {
85            match &last.arguments {
86                PathArguments::AngleBracketed(tps) => {
87                    let arg = tps.args.first().unwrap();
88
89                    match arg {
90                        GenericArgument::Type(arg) => return Some(arg),
91                        _ => unimplemented!("generic parameter other than type"),
92                    }
93                }
94                _ => unimplemented!("Box() -> T or Box without a type parameter"),
95            }
96        }
97    }
98
99    None
100}