1use super::{
2 Definition, InterfaceDcl, ModuleDcl, ParserProperties, Specification, TypeDcl,
3 expand_annotations, interface_codegen, parse_xidlc_pragma,
4};
5use crate::jsonrpc_hir;
6use crate::rest_hir::{self, HirProjectionKind, ProjectedHir};
7use crate::semantic;
8use serde_json::Value;
9use std::path::Path;
10
11impl From<crate::typed_ast::Specification> for Specification {
12 fn from(value: crate::typed_ast::Specification) -> Self {
13 spec_from_typed_ast(value, true)
14 }
15}
16
17impl Specification {
18 pub fn from_typed_ast_with_properties(
19 value: crate::typed_ast::Specification,
20 properties: ParserProperties,
21 ) -> Self {
22 spec_from_typed_ast(value, expand_interface(&properties))
23 }
24
25 pub fn from_typed_ast_with_properties_and_path(
26 value: crate::typed_ast::Specification,
27 properties: ParserProperties,
28 path: impl AsRef<Path>,
29 ) -> crate::error::ParserResult<Self> {
30 spec_from_typed_ast_with_path(value, expand_interface(&properties), path.as_ref())
31 }
32
33 pub fn from_typed_ast_with_path(
34 value: crate::typed_ast::Specification,
35 path: impl AsRef<Path>,
36 ) -> crate::error::ParserResult<Self> {
37 spec_from_typed_ast_with_path(value, true, path.as_ref())
38 }
39
40 pub fn project_typed_ast_with_properties_and_path(
41 value: crate::typed_ast::Specification,
42 properties: ParserProperties,
43 path: impl AsRef<Path>,
44 ) -> crate::error::ParserResult<ProjectedHir> {
45 let spec =
46 spec_from_typed_ast_with_path(value, expand_interface(&properties), path.as_ref())?;
47 match hir_projection_kind(&properties) {
48 HirProjectionKind::Rpc => Ok(ProjectedHir::Rpc(spec)),
49 HirProjectionKind::Http => rest_hir::project(&spec).map(ProjectedHir::Http),
50 HirProjectionKind::JsonRpc => jsonrpc_hir::project(&spec).map(ProjectedHir::JsonRpc),
51 }
52 }
53}
54
55pub(crate) fn spec_from_typed_ast(
56 value: crate::typed_ast::Specification,
57 expand_interfaces: bool,
58) -> Specification {
59 let mut definitions = Vec::new();
60 collect_defs_with_context(
61 value.0,
62 &mut Vec::new(),
63 expand_interfaces,
64 &mut definitions,
65 )
66 .expect("HIR conversion should not fail");
67 let mut spec = Specification(definitions);
68 semantic::analyze(&mut spec);
69 spec
70}
71
72fn spec_from_typed_ast_with_path(
73 value: crate::typed_ast::Specification,
74 expand_interfaces: bool,
75 _path: &Path,
76) -> crate::error::ParserResult<Specification> {
77 let mut definitions = Vec::new();
78 collect_defs_with_context(
79 value.0,
80 &mut Vec::new(),
81 expand_interfaces,
82 &mut definitions,
83 )?;
84 let mut spec = Specification(definitions);
85 semantic::analyze(&mut spec);
86 Ok(spec)
87}
88
89fn collect_defs_with_context(
90 defs: Vec<crate::typed_ast::Definition>,
91 modules: &mut Vec<String>,
92 expand_interfaces: bool,
93 out: &mut Vec<Definition>,
94) -> crate::error::ParserResult<()> {
95 for def in defs {
96 match def {
97 crate::typed_ast::Definition::ModuleDcl(module) => {
98 let ident = module.ident.0;
99 let annotations = expand_annotations(module.annotations);
100 modules.push(ident.clone());
101 let mut inner = Vec::new();
102 collect_defs_with_context(
103 module.definition,
104 modules,
105 expand_interfaces,
106 &mut inner,
107 )?;
108 modules.pop();
109 out.push(Definition::ModuleDcl(ModuleDcl {
110 annotations,
111 ident,
112 definition: inner,
113 }));
114 }
115 crate::typed_ast::Definition::PreprocCall(call) => {
116 if let Some(pragma) = parse_xidlc_pragma(&call) {
117 out.push(Definition::Pragma(pragma));
118 }
119 }
120 crate::typed_ast::Definition::TypeDcl(value) => {
121 out.push(Definition::TypeDcl(TypeDcl::from(value)))
122 }
123 crate::typed_ast::Definition::ConstDcl(value) => {
124 out.push(Definition::ConstDcl(value.into()))
125 }
126 crate::typed_ast::Definition::ExceptDcl(value) => {
127 out.push(Definition::ExceptDcl(value.into()))
128 }
129 crate::typed_ast::Definition::InterfaceDcl(value) => {
130 let interface = InterfaceDcl::from(value);
131 if expand_interfaces {
132 let generated = interface_codegen::expand_interface(&interface, modules)
133 .unwrap_or_else(|err| panic!("interface expansion failed: {err}"));
134 out.extend(generated);
135 }
136 out.push(Definition::InterfaceDcl(interface));
137 }
138 crate::typed_ast::Definition::PreprocInclude(_) => {
139 }
142 crate::typed_ast::Definition::TemplateModuleDcl(_)
143 | crate::typed_ast::Definition::TemplateModuleInst(_)
144 | crate::typed_ast::Definition::PreprocDefine(_) => {}
145 }
146 }
147
148 Ok(())
149}
150
151fn expand_interface(properties: &ParserProperties) -> bool {
152 if let Some(expand) = properties.get("expand_interface").and_then(Value::as_bool) {
153 return expand;
154 }
155
156 matches!(hir_projection_kind(properties), HirProjectionKind::Rpc)
157}
158
159fn hir_projection_kind(properties: &ParserProperties) -> HirProjectionKind {
160 match properties.get("hir_kind").and_then(Value::as_str) {
161 Some(value) if value.eq_ignore_ascii_case("http") => HirProjectionKind::Http,
162 Some(value)
163 if value.eq_ignore_ascii_case("jsonrpc") || value.eq_ignore_ascii_case("json-rpc") =>
164 {
165 HirProjectionKind::JsonRpc
166 }
167 _ => HirProjectionKind::Rpc,
168 }
169}