1extern crate alloc;
18use zerodds_idl::ast::types::{Export, InterfaceDef, OpDecl, ParamAttribute, ParamDecl, TypeSpec};
19
20use crate::error::Result;
21
22pub fn emit_interface(
25 out: &mut String,
26 i: &InterfaceDef,
27 registry: &crate::emitter::InterfaceRegistry<'_>,
28) -> Result<()> {
29 emit_interface_trait(out, i)?;
30 out.push('\n');
31 emit_interface_stub(out, i, registry)?;
32 out.push('\n');
33 emit_interface_skeleton(out, i)?;
34 Ok(())
35}
36
37fn emit_interface_trait(out: &mut String, i: &InterfaceDef) -> Result<()> {
38 let name = &i.name.text;
39 out.push_str("/// Generated by `zerodds-corba-rust` from IDL interface.\n");
40 emit_interface_exceptions_enum(out, i)?;
43 let repo_id = zerodds_corba_codegen::build_repository_id(&[], name, 1, 0);
47 let mut bases_iter = i.bases.iter().map(|s| {
48 s.parts
49 .iter()
50 .map(|p| p.text.as_str())
51 .collect::<Vec<_>>()
52 .join("::")
53 });
54 let bases_clause = if let Some(first) = bases_iter.next() {
55 let mut s = format!(": {first} + ::core::marker::Send + ::core::marker::Sync");
56 for base in bases_iter {
57 s.push_str(&format!(" + {base}"));
58 }
59 s
60 } else {
61 ": ::core::marker::Send + ::core::marker::Sync".to_string()
62 };
63 let repo_const = name.to_ascii_uppercase();
67 out.push_str(&format!(
68 "/// CORBA Repository-ID fuer `{name}` (Spec §10.7.3.1).\npub const {repo_const}_REPOSITORY_ID: &str = \"{repo_id}\";\n"
69 ));
70 out.push_str(&format!("pub trait {name}{bases_clause} {{\n"));
71 for export in &i.exports {
72 emit_export_trait_method(out, export)?;
73 }
74 out.push_str("}\n");
75 Ok(())
76}
77
78fn emit_interface_exceptions_enum(out: &mut String, i: &InterfaceDef) -> Result<()> {
81 let name = &i.name.text;
82 let mut all_raises: alloc::collections::BTreeSet<String> = alloc::collections::BTreeSet::new();
83 for export in &i.exports {
84 if let Export::Op(op) = export {
85 for raise in &op.raises {
86 let r = raise
87 .parts
88 .iter()
89 .map(|p| p.text.as_str())
90 .collect::<Vec<_>>()
91 .join("::");
92 all_raises.insert(r);
93 }
94 }
95 }
96 if all_raises.is_empty() {
97 return Ok(());
98 }
99 out.push_str(&format!(
100 "/// Exception-Enum fuer `{name}` — vereinigt alle in `raises`-Listen referenzierten User-Exceptions plus System-Exception (Spec §3.5).\n"
101 ));
102 out.push_str("#[derive(Debug, Clone)]\n");
103 out.push_str(&format!("pub enum {name}Error {{\n"));
104 out.push_str(
105 " /// CORBA-System-Exception (BAD_OPERATION, MARSHAL, OBJECT_NOT_EXIST, ...).\n",
106 );
107 out.push_str(" System(zerodds_corba_rust::CorbaException),\n");
108 for raise in &all_raises {
109 let variant = raise.split("::").last().unwrap_or(raise);
110 out.push_str(&format!(
111 " /// User-Exception `{raise}` (raised by `{name}`-Operations).\n"
112 ));
113 out.push_str(&format!(" {variant}({raise}),\n"));
114 }
115 out.push_str("}\n\n");
116 Ok(())
117}
118
119fn emit_export_trait_method(out: &mut String, export: &Export) -> Result<()> {
120 match export {
121 Export::Op(op) => emit_op_trait_method(out, op),
122 Export::Attr(attr) => emit_attr_trait_method(out, attr),
123 _ => Ok(()),
126 }
127}
128
129pub fn emit_op_trait_method_pub(out: &mut String, op: &OpDecl) -> Result<()> {
131 emit_op_trait_method(out, op)
132}
133
134pub fn emit_attr_trait_method_pub(
136 out: &mut String,
137 attr: &zerodds_idl::ast::types::AttrDecl,
138) -> Result<()> {
139 emit_attr_trait_method(out, attr)
140}
141
142fn emit_op_trait_method(out: &mut String, op: &OpDecl) -> Result<()> {
143 let method = &op.name.text;
144 let receiver = if op_is_state_changing(op) {
145 "&mut self"
146 } else {
147 "&self"
148 };
149 let params_str = render_params(&op.params)?;
150 let return_str = render_return(&op.return_type, &op.raises)?;
151 let comma = if params_str.is_empty() { "" } else { ", " };
152 out.push_str(&format!(
153 " /// IDL operation `{method}`.\n fn {method}({receiver}{comma}{params_str}) -> {return_str};\n"
154 ));
155 Ok(())
156}
157
158fn emit_attr_trait_method(
159 out: &mut String,
160 attr: &zerodds_idl::ast::types::AttrDecl,
161) -> Result<()> {
162 let name = &attr.name.text;
163 let ty = zerodds_idl_rust::type_map::rust_type_for(&attr.type_spec)?;
164
165 out.push_str(&format!(" /// IDL attribute `{name}` getter.\n"));
166 out.push_str(&format!(
167 " fn {name}(&self) -> ::core::result::Result<{ty}, zerodds_corba_rust::CorbaException>;\n"
168 ));
169 if !attr.readonly {
170 out.push_str(&format!(" /// IDL attribute `{name}` setter.\n"));
171 out.push_str(&format!(
172 " fn set_{name}(&mut self, value: {ty}) -> ::core::result::Result<(), zerodds_corba_rust::CorbaException>;\n"
173 ));
174 }
175 Ok(())
176}
177
178fn op_is_state_changing(op: &OpDecl) -> bool {
179 op.params
180 .iter()
181 .any(|p| matches!(p.attribute, ParamAttribute::Out | ParamAttribute::InOut))
182}
183
184fn render_params(params: &[ParamDecl]) -> Result<String> {
185 let mut out = String::new();
186 for (idx, p) in params.iter().enumerate() {
187 if idx > 0 {
188 out.push_str(", ");
189 }
190 let ty = zerodds_idl_rust::type_map::rust_type_for(&p.type_spec)?;
191 let name = &p.name.text;
192 match p.attribute {
193 ParamAttribute::In => {
194 out.push_str(&format!("{name}: {ty}"));
195 }
196 ParamAttribute::Out | ParamAttribute::InOut => {
197 out.push_str(&format!("{name}: &mut {ty}"));
198 }
199 }
200 }
201 Ok(out)
202}
203
204fn render_return(
205 return_type: &Option<TypeSpec>,
206 _raises: &[zerodds_idl::ast::types::ScopedName],
207) -> Result<String> {
208 let inner = match return_type {
209 None => "()".to_string(),
210 Some(ts) => zerodds_idl_rust::type_map::rust_type_for(ts)?,
211 };
212 Ok(format!(
218 "::core::result::Result<{inner}, zerodds_corba_rust::CorbaException>"
219 ))
220}
221
222fn emit_interface_stub(
223 out: &mut String,
224 i: &InterfaceDef,
225 registry: &crate::emitter::InterfaceRegistry<'_>,
226) -> Result<()> {
227 let name = &i.name.text;
228 let stub_name = format!("{name}Stub");
229 out.push_str(&format!(
230 "/// Client-Stub fuer `{name}` (sendet GIOP-Requests an einen Remote-Object).\n"
231 ));
232 out.push_str(&format!("pub struct {stub_name} {{\n"));
233 out.push_str(" /// Object-Reference (IOR) des Remote-Servants.\n");
234 out.push_str(" pub object_ref: zerodds_corba_rust::ObjectReference,\n");
235 out.push_str("}\n\n");
236
237 out.push_str(&format!("impl {stub_name} {{\n"));
238 out.push_str(" /// Konstruiert einen Stub mit einer ObjectReference.\n");
239 out.push_str(" #[must_use]\n");
240 out.push_str(" pub fn new(object_ref: zerodds_corba_rust::ObjectReference) -> Self {\n");
241 out.push_str(" Self { object_ref }\n");
242 out.push_str(" }\n");
243 out.push_str("}\n\n");
244
245 out.push_str(&format!("impl {name} for {stub_name} {{\n"));
247 for export in &i.exports {
248 emit_export_stub_impl(out, name, export)?;
249 }
250 out.push_str("}\n");
251
252 let mut visited = alloc::collections::BTreeSet::new();
256 for base in &i.bases {
257 emit_base_impls_recursive(out, &stub_name, base, registry, &mut visited)?;
258 }
259 Ok(())
260}
261
262fn emit_base_impls_recursive(
264 out: &mut String,
265 stub_name: &str,
266 base_path: &zerodds_idl::ast::types::ScopedName,
267 registry: &crate::emitter::InterfaceRegistry<'_>,
268 visited: &mut alloc::collections::BTreeSet<String>,
269) -> Result<()> {
270 let base_simple = base_path
271 .parts
272 .last()
273 .map(|p| p.text.clone())
274 .unwrap_or_default();
275 if base_simple.is_empty() || !visited.insert(base_simple.clone()) {
276 return Ok(());
277 }
278 let Some(base_def) = registry.get(&base_simple) else {
279 return Ok(());
281 };
282 let base_full_path = base_path
283 .parts
284 .iter()
285 .map(|p| p.text.as_str())
286 .collect::<Vec<_>>()
287 .join("::");
288 out.push_str(&format!(
289 "\n/// Base-Inheritance: `{base_simple}`-Methoden auf `{stub_name}` (Spec §3.6).\n"
290 ));
291 out.push_str(&format!("impl {base_full_path} for {stub_name} {{\n"));
292 for export in &base_def.exports {
293 emit_export_stub_impl(out, &base_simple, export)?;
294 }
295 out.push_str("}\n");
296 for grandbase in &base_def.bases {
297 emit_base_impls_recursive(out, stub_name, grandbase, registry, visited)?;
298 }
299 Ok(())
300}
301
302fn emit_export_stub_impl(out: &mut String, _iface: &str, export: &Export) -> Result<()> {
303 match export {
304 Export::Op(op) => emit_op_stub_impl(out, op),
305 Export::Attr(attr) => emit_attr_stub_impl(out, attr),
306 _ => Ok(()),
307 }
308}
309
310fn emit_op_stub_impl(out: &mut String, op: &OpDecl) -> Result<()> {
311 let method = &op.name.text;
312 let receiver = if op_is_state_changing(op) {
313 "&mut self"
314 } else {
315 "&self"
316 };
317 let params_str = render_params(&op.params)?;
318 let return_str = render_return(&op.return_type, &op.raises)?;
319 let comma = if params_str.is_empty() { "" } else { ", " };
320
321 out.push_str(&format!(
322 " fn {method}({receiver}{comma}{params_str}) -> {return_str} {{\n"
323 ));
324 out.push_str(&format!(
325 " // GIOP-Request: operation_name = \"{method}\".\n"
326 ));
327 out.push_str(
328 " // Phase-2: encode in_params + send via corba-iiop, await reply, decode.\n",
329 );
330 out.push_str(
331 " ::core::result::Result::Err(zerodds_corba_rust::CorbaException::SystemException {\n",
332 );
333 out.push_str(" minor: 0,\n");
334 out.push_str(" message: \"corba-rust stub: GIOP marshalling not yet wired\",\n");
335 out.push_str(" })\n");
336 out.push_str(" }\n");
337 Ok(())
338}
339
340fn emit_attr_stub_impl(out: &mut String, attr: &zerodds_idl::ast::types::AttrDecl) -> Result<()> {
341 let name = &attr.name.text;
342 let ty = zerodds_idl_rust::type_map::rust_type_for(&attr.type_spec)?;
343
344 out.push_str(&format!(
346 " fn {name}(&self) -> ::core::result::Result<{ty}, zerodds_corba_rust::CorbaException> {{\n"
347 ));
348 out.push_str(
349 " ::core::result::Result::Err(zerodds_corba_rust::CorbaException::SystemException {\n",
350 );
351 out.push_str(" minor: 0,\n");
352 out.push_str(&format!(
353 " message: \"corba-rust stub: attribute `{name}` getter not yet wired\",\n"
354 ));
355 out.push_str(" })\n");
356 out.push_str(" }\n");
357
358 if !attr.readonly {
359 out.push_str(&format!(
360 " fn set_{name}(&mut self, value: {ty}) -> ::core::result::Result<(), zerodds_corba_rust::CorbaException> {{\n"
361 ));
362 out.push_str(" let _ = value;\n");
363 out.push_str(
364 " ::core::result::Result::Err(zerodds_corba_rust::CorbaException::SystemException {\n",
365 );
366 out.push_str(" minor: 0,\n");
367 out.push_str(&format!(
368 " message: \"corba-rust stub: attribute `{name}` setter not yet wired\",\n"
369 ));
370 out.push_str(" })\n");
371 out.push_str(" }\n");
372 }
373 Ok(())
374}
375
376fn emit_interface_skeleton(out: &mut String, i: &InterfaceDef) -> Result<()> {
377 let name = &i.name.text;
378 out.push_str(&format!("/// Server-Skeleton-Dispatch fuer `{name}`.\n"));
379 out.push_str("/// Wird vom POA aufgerufen wenn ein GIOP-Request mit Target-Operation\n");
380 out.push_str(&format!(
381 "/// `{name}` empfangen wird. Dispatched die Operation an den Servant.\n"
382 ));
383 let name_lower = name.to_lowercase();
384 out.push_str(&format!(
385 "pub fn dispatch_{name_lower}(servant: &dyn {name}, operation: &str, _payload: &[u8]) -> zerodds_corba_rust::SkeletonResult {{\n"
386 ));
387 out.push_str(" match operation {\n");
388 for export in &i.exports {
389 emit_export_skeleton_arm(out, export);
390 }
391 out.push_str(" _ => zerodds_corba_rust::SkeletonResult::BadOperation,\n");
392 out.push_str(" }\n");
393 out.push_str("}\n");
394 Ok(())
395}
396
397fn emit_export_skeleton_arm(out: &mut String, export: &Export) {
398 match export {
399 Export::Op(op) => {
400 let method = &op.name.text;
401 out.push_str(&format!(
402 " \"{method}\" => {{\n // Phase-2: GIOP-Body decoden, servant.{method}(...) aufrufen, Reply encoden.\n let _ = servant;\n zerodds_corba_rust::SkeletonResult::NotYetWired\n }}\n"
403 ));
404 }
405 Export::Attr(attr) => {
406 let name = &attr.name.text;
407 out.push_str(&format!(
408 " \"_get_{name}\" => {{\n let _ = servant;\n zerodds_corba_rust::SkeletonResult::NotYetWired\n }}\n"
409 ));
410 if !attr.readonly {
411 out.push_str(&format!(
412 " \"_set_{name}\" => {{\n let _ = servant;\n zerodds_corba_rust::SkeletonResult::NotYetWired\n }}\n"
413 ));
414 }
415 }
416 _ => {}
417 }
418}