1use std::collections::hash_map::Entry;
2
3use cpp::CppExportedFnDefinition;
4use cpp::CppExportedImplDefinition;
5use cpp::CppFile;
6use cpp::CppFnDefinition;
7use cpp::CppFnSig;
8use cpp::CppMethod;
9use cpp::CppPath;
10use cpp::CppTraitDefinition;
11use cpp::CppType;
12use cpp::CppTypeDefinition;
13use cpp::cpp_handle_keyword;
14use itertools::Itertools;
15use rust::IntoCpp;
16
17pub mod cpp;
18mod rust;
19
20pub use rust::RustFile;
21pub use zngur_parser::ParsedZngFile;
22
23pub use zngur_def::*;
24
25pub struct ZngurGenerator(ZngurSpec);
26
27impl ZngurGenerator {
28 pub fn build_from_zng(zng: ZngurSpec) -> Self {
29 ZngurGenerator(zng)
30 }
31
32 pub fn render(self) -> (String, String, Option<String>) {
33 let mut zng = self.0;
34
35 zng.types.push(ZngurType {
37 ty: RustType::UNIT,
38 layout: LayoutPolicy::ZERO_SIZED_TYPE,
39 wellknown_traits: vec![ZngurWellknownTrait::Copy],
40 methods: vec![],
41 constructors: vec![],
42 fields: vec![],
43 cpp_value: None,
44 cpp_ref: None,
45 });
46 let mut cpp_file = CppFile::default();
47 cpp_file.additional_includes = zng.additional_includes.0;
48 let mut rust_file = RustFile::default();
49 cpp_file.trait_defs = zng
50 .traits
51 .iter()
52 .map(|(key, value)| (key.clone(), rust_file.add_builder_for_dyn_trait(value)))
53 .collect();
54 if zng.convert_panic_to_exception.0 {
55 rust_file.enable_panic_to_exception();
56 cpp_file.panic_to_exception = true;
57 }
58 for ty_def in zng.types {
59 let ty = &ty_def.ty;
60 let is_copy = ty_def.wellknown_traits.contains(&ZngurWellknownTrait::Copy);
61 match ty_def.layout {
62 LayoutPolicy::StackAllocated { size, align } => {
63 rust_file.add_static_size_assert(&ty, size);
64 rust_file.add_static_align_assert(&ty, align);
65 }
66 LayoutPolicy::HeapAllocated => (),
67 LayoutPolicy::OnlyByRef => (),
68 }
69 if is_copy {
70 rust_file.add_static_is_copy_assert(&ty);
71 }
72 let mut cpp_methods = vec![];
73 let mut constructors = vec![];
74 let mut fields = vec![];
75 let mut wellknown_traits = vec![];
76 for constructor in ty_def.constructors {
77 match constructor.name {
78 Some(name) => {
79 let rust_link_names = rust_file
80 .add_constructor(&format!("{}::{}", ty, name), &constructor.inputs);
81 cpp_methods.push(CppMethod {
82 name: cpp_handle_keyword(&name).to_owned(),
83 kind: ZngurMethodReceiver::Static,
84 sig: CppFnSig {
85 rust_link_name: rust_link_names.constructor,
86 inputs: constructor.inputs.iter().map(|x| x.1.into_cpp()).collect(),
87 output: ty.into_cpp(),
88 },
89 });
90 cpp_methods.push(CppMethod {
91 name: format!("matches_{}", name),
92 kind: ZngurMethodReceiver::Ref(Mutability::Not),
93 sig: CppFnSig {
94 rust_link_name: rust_link_names.match_check,
95 inputs: vec![ty.into_cpp().into_ref()],
96 output: CppType::from("uint8_t"),
97 },
98 });
99 }
100 None => {
101 let rust_link_name = rust_file
102 .add_constructor(&format!("{}", ty), &constructor.inputs)
103 .constructor;
104 constructors.push(CppFnSig {
105 rust_link_name,
106 inputs: constructor.inputs.iter().map(|x| x.1.into_cpp()).collect(),
107 output: ty.into_cpp(),
108 });
109 }
110 }
111 }
112 for field in ty_def.fields {
113 rust_file.add_field_assertions(&field, &ty_def.ty);
114 fields.push(field);
115 }
116 if let RustType::Tuple(fields) = &ty_def.ty {
117 if !fields.is_empty() {
118 let rust_link_name = rust_file.add_tuple_constructor(&fields);
119 constructors.push(CppFnSig {
120 rust_link_name,
121 inputs: fields.iter().map(|x| x.into_cpp()).collect(),
122 output: ty.into_cpp(),
123 });
124 }
125 }
126 let is_unsized = ty_def
127 .wellknown_traits
128 .contains(&ZngurWellknownTrait::Unsized);
129 for wellknown_trait in ty_def.wellknown_traits {
130 let data = rust_file.add_wellknown_trait(&ty, wellknown_trait, is_unsized);
131 wellknown_traits.push(data);
132 }
133 for method_details in ty_def.methods {
134 let ZngurMethodDetails {
135 data: method,
136 use_path,
137 deref,
138 } = method_details;
139 let (rusty_inputs, inputs) = real_inputs_of_method(&method, &ty);
140 let rust_link_name = rust_file.add_function(
141 &format!(
142 "<{}>::{}::<{}>",
143 deref.as_ref().unwrap_or(&ty),
144 method.name,
145 method.generics.iter().join(", "),
146 ),
147 &rusty_inputs,
148 &method.output,
149 use_path,
150 deref.is_some(),
151 );
152 cpp_methods.push(CppMethod {
153 name: cpp_handle_keyword(&method.name).to_owned(),
154 kind: method.receiver,
155 sig: CppFnSig {
156 rust_link_name,
157 inputs,
158 output: method.output.into_cpp(),
159 },
160 });
161 }
162 cpp_file.type_defs.push(CppTypeDefinition {
163 ty: ty.into_cpp(),
164 layout: rust_file.add_layout_policy_shim(&ty, ty_def.layout),
165 constructors,
166 fields,
167 methods: cpp_methods,
168 wellknown_traits,
169 cpp_value: ty_def.cpp_value.map(|mut cpp_value| {
170 cpp_value.0 = rust_file.add_cpp_value_bridge(&ty, &cpp_value.0);
171 cpp_value
172 }),
173 cpp_ref: ty_def.cpp_ref,
174 from_trait: if let RustType::Boxed(b) = &ty {
175 if let RustType::Dyn(tr, _) = b.as_ref() {
176 if let RustTrait::Fn {
177 name,
178 inputs,
179 output,
180 } = tr
181 {
182 if let Entry::Vacant(e) = cpp_file.trait_defs.entry(tr.clone()) {
183 let rust_link_name =
184 rust_file.add_builder_for_dyn_fn(name, inputs, output);
185 e.insert(CppTraitDefinition::Fn {
186 sig: CppFnSig {
187 rust_link_name,
188 inputs: inputs.iter().map(|x| x.into_cpp()).collect(),
189 output: output.into_cpp(),
190 },
191 });
192 }
193 }
194 Some(tr.clone())
195 } else {
196 None
197 }
198 } else {
199 None
200 },
201 from_trait_ref: if let RustType::Dyn(tr, _) = &ty {
202 Some(tr.clone())
203 } else {
204 None
205 },
206 });
207 }
208 for func in zng.funcs {
209 let rust_link_name = rust_file.add_function(
210 &func.path.to_string(),
211 &func.inputs,
212 &func.output,
213 None,
214 false,
215 );
216 cpp_file.fn_defs.push(CppFnDefinition {
217 name: CppPath::from_rust_path(&func.path.path),
218 sig: CppFnSig {
219 rust_link_name,
220 inputs: func.inputs.into_iter().map(|x| x.into_cpp()).collect(),
221 output: func.output.into_cpp(),
222 },
223 });
224 }
225 for func in zng.extern_cpp_funcs {
226 let rust_link_name =
227 rust_file.add_extern_cpp_function(&func.name, &func.inputs, &func.output);
228 cpp_file.exported_fn_defs.push(CppExportedFnDefinition {
229 name: func.name.clone(),
230 sig: CppFnSig {
231 rust_link_name,
232 inputs: func.inputs.into_iter().map(|x| x.into_cpp()).collect(),
233 output: func.output.into_cpp(),
234 },
235 });
236 }
237 for impl_block in zng.extern_cpp_impls {
238 let rust_link_names = rust_file.add_extern_cpp_impl(
239 &impl_block.ty,
240 impl_block.tr.as_ref(),
241 &impl_block.methods,
242 );
243 cpp_file.exported_impls.push(CppExportedImplDefinition {
244 tr: impl_block.tr.map(|x| x.into_cpp()),
245 ty: impl_block.ty.into_cpp(),
246 methods: impl_block
247 .methods
248 .iter()
249 .zip(&rust_link_names)
250 .map(|(method, link_name)| {
251 let (_, inputs) = real_inputs_of_method(method, &impl_block.ty);
252 (
253 cpp_handle_keyword(&method.name).to_owned(),
254 CppFnSig {
255 rust_link_name: link_name.clone(),
256 inputs,
257 output: method.output.into_cpp(),
258 },
259 )
260 })
261 .collect(),
262 });
263 }
264 let (h, cpp) = cpp_file.render();
265 (rust_file.text, h, cpp)
266 }
267}
268
269fn real_inputs_of_method(method: &ZngurMethod, ty: &RustType) -> (Vec<RustType>, Vec<CppType>) {
270 let receiver_type = match method.receiver {
271 ZngurMethodReceiver::Static => None,
272 ZngurMethodReceiver::Ref(m) => Some(RustType::Ref(m, Box::new(ty.clone()))),
273 ZngurMethodReceiver::Move => Some(ty.clone()),
274 };
275 let rusty_inputs = receiver_type
276 .into_iter()
277 .chain(method.inputs.clone())
278 .collect::<Vec<_>>();
279 let inputs = rusty_inputs.iter().map(|x| x.into_cpp()).collect_vec();
280 (rusty_inputs, inputs)
281}