swc_ecma_quote_macros/
ret_type.rs1use 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
11pub 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}