1mod enhanced;
70pub mod type_information;
71
72use core::fmt;
73use std::collections::BTreeSet;
74
75use zerodds_ast::{
76 Ast, ExceptionNode, ModuleNode, ParamDirection, PrimitiveKind, TypeKind, TypeNode,
77};
78use zerodds_ast_core::Id;
79use zerodds_ast_layout::key::{Encoding, Endianness, LayoutKey};
80use zerodds_lang_cst::common::{Doc, Ident, Literal, Path, Visibility};
81use zerodds_lang_cst::rust;
82
83use crate::adapter::cst::{MaterialiseError, data_derives, rust_type_of};
84use crate::adapter::interface::{
85 self, BindingError, InterfaceError, Operation, RETURN_MEMBER, Raise, Service,
86};
87use crate::encode::{EncodeError, MemberWrite, Position, read_for, write_of, write_value_to};
88
89#[derive(Debug)]
91pub enum RustInterfaceError {
92 Interface(InterfaceError),
94 Materialise(MaterialiseError),
96 Codec(EncodeError),
98}
99
100impl From<InterfaceError> for RustInterfaceError {
101 fn from(error: InterfaceError) -> Self {
102 Self::Interface(error)
103 }
104}
105impl From<BindingError> for RustInterfaceError {
106 fn from(error: BindingError) -> Self {
107 match error {
108 BindingError::Interface(error) => Self::Interface(error),
109 BindingError::Materialise(error) => Self::Materialise(error),
110 BindingError::Codec(reason) => Self::Materialise(MaterialiseError::UnknownType {
113 type_name: format!("a codec: {reason}"),
114 }),
115 }
116 }
117}
118impl From<MaterialiseError> for RustInterfaceError {
119 fn from(error: MaterialiseError) -> Self {
120 Self::Materialise(error)
121 }
122}
123impl From<EncodeError> for RustInterfaceError {
124 fn from(error: EncodeError) -> Self {
125 Self::Codec(error)
126 }
127}
128
129impl fmt::Display for RustInterfaceError {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match self {
132 Self::Interface(error) => write!(f, "{error}"),
133 Self::Materialise(error) => write!(f, "{error:?}"),
134 Self::Codec(error) => write!(f, "{error}"),
135 }
136 }
137}
138
139#[must_use]
142pub fn rpc_layout_key() -> LayoutKey {
143 LayoutKey::data(Encoding::PlainCdr2, Endianness::Little)
144}
145
146#[must_use]
150pub fn rpc_layout_key_xcdr1() -> LayoutKey {
151 LayoutKey::data(Encoding::PlainCdr1, Endianness::Little)
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162enum CodecVariant {
163 Xcdr2,
164 Xcdr1,
165}
166
167impl CodecVariant {
168 fn suffix(self) -> &'static str {
169 match self {
170 Self::Xcdr2 => "",
171 Self::Xcdr1 => crate::encode::SECONDARY_SUFFIX,
172 }
173 }
174
175 fn key(self) -> LayoutKey {
176 match self {
177 Self::Xcdr2 => rpc_layout_key(),
178 Self::Xcdr1 => rpc_layout_key_xcdr1(),
179 }
180 }
181
182 fn encode_name(self) -> String {
183 format!("encode{}", self.suffix())
184 }
185
186 fn decode_name(self) -> String {
187 format!("decode{}", self.suffix())
188 }
189}
190
191pub fn rust_interface_pairs(
202 ast: &Ast,
203) -> Result<Vec<(Id<ModuleNode>, rust::Item)>, RustInterfaceError> {
204 rust_interface_pairs_with(ast, interface::ServiceMapping::Basic)
205}
206
207pub fn rust_interface_pairs_with(
218 ast: &Ast,
219 mapping: interface::ServiceMapping,
220) -> Result<Vec<(Id<ModuleNode>, rust::Item)>, RustInterfaceError> {
221 let mut out = Vec::new();
222 let capable = xcdr1_capable_types(ast);
223 for service in interface::services(ast)? {
224 if mapping.is_enhanced() {
225 enhanced::declare(ast, &Service { mapping, ..service }, &mut out)?;
226 } else {
227 declare(ast, &service, &capable, &mut out)?;
228 }
229 }
230 Ok(out)
231}
232
233#[must_use]
238pub fn xcdr1_capable_types(ast: &Ast) -> BTreeSet<Id<TypeNode>> {
239 ast.types()
240 .filter(|(id, node)| {
241 !node.name.is_empty()
242 && crate::encode::has_codec(&node.kind)
243 && crate::encode::rust_codec_pair(ast, *id, rpc_layout_key_xcdr1()).is_ok()
244 })
245 .map(|(id, _)| id)
246 .collect()
247}
248
249const MAX_REACHABLE_TYPES: usize = 4096;
254
255pub(crate) fn type_closed(
260 ast: &Ast,
261 target: Id<TypeNode>,
262 capable: &BTreeSet<Id<TypeNode>>,
263 seen: &mut BTreeSet<Id<TypeNode>>,
264) -> bool {
265 if !seen.insert(target) {
266 return true;
267 }
268 if seen.len() > MAX_REACHABLE_TYPES {
269 return false;
270 }
271 let Ok(node) = ast.type_(target) else {
272 return false;
273 };
274 if crate::encode::has_codec(&node.kind) && !capable.contains(&target) {
275 return false;
276 }
277 let reaches = |reference: &zerodds_ast_core::Ref<TypeNode>,
278 seen: &mut BTreeSet<Id<TypeNode>>| {
279 reference
280 .resolved()
281 .is_some_and(|id| type_closed(ast, id, capable, seen))
282 };
283 let member_closed = |member: Id<zerodds_ast::MemberNode>, seen: &mut BTreeSet<Id<TypeNode>>| {
284 ast.member(member)
285 .is_ok_and(|member| reaches(&member.type_, seen))
286 };
287 match &node.kind {
288 TypeKind::Structure { base, members } => {
289 base.as_ref().is_none_or(|base| reaches(base, seen))
290 && members.iter().all(|m| member_closed(*m, seen))
291 }
292 TypeKind::Union {
293 discriminator,
294 cases,
295 ..
296 } => {
297 reaches(discriminator, seen)
298 && cases.iter().all(|case| {
299 ast.case(*case)
300 .is_ok_and(|case| member_closed(case.member, seen))
301 })
302 }
303 TypeKind::Alias { aliased } => reaches(aliased, seen),
304 TypeKind::Sequence { element, .. } | TypeKind::Array { element, .. } => {
305 reaches(element, seen)
306 }
307 TypeKind::Map { key, value, .. } => reaches(key, seen) && reaches(value, seen),
308 TypeKind::ValueBox { boxed } => reaches(boxed, seen),
309 TypeKind::Valuetype { bases, state, .. } => {
310 bases.iter().all(|b| reaches(b, seen)) && state.iter().all(|m| member_closed(*m, seen))
311 }
312 _ => true,
313 }
314}
315
316fn fields_closed(ast: &Ast, fields: &[FieldSpec], capable: &BTreeSet<Id<TypeNode>>) -> bool {
317 fields.iter().all(|field| match field {
318 FieldSpec::Octet(_) => true,
319 FieldSpec::Real(_, target) => type_closed(ast, *target, capable, &mut BTreeSet::new()),
320 })
321}
322
323fn declare(
324 ast: &Ast,
325 service: &Service,
326 capable: &BTreeSet<Id<TypeNode>>,
327 out: &mut Vec<(Id<ModuleNode>, rust::Item)>,
328) -> Result<(), RustInterfaceError> {
329 let module = service.module;
330 let ops = service.all_operations();
331
332 let mut exception_ok: Vec<(Id<ExceptionNode>, bool)> = Vec::new();
346 for raise in exceptions_of(&ops) {
347 let exc_module = exception_module(ast, raise.id)?;
348 out.push((exc_module, exception_codec_item(ast, raise.id)?));
349 let ok = exception_fields_closed(ast, raise.id, capable)?;
350 if ok {
351 out.push((
352 exc_module,
353 exception_codec_item_with(ast, raise.id, CodecVariant::Xcdr1)?,
354 ));
355 }
356 exception_ok.push((raise.id, ok));
357 }
358
359 let mut in_ok: Vec<bool> = Vec::new();
360 let mut result_ok: Vec<bool> = Vec::new();
361 for op in &ops {
362 out.push((module, rust::Item::Struct(in_struct(ast, service, op)?)));
363 let in_name = interface::in_struct_name(&service.name, &op.name);
364 out.push((
365 module,
366 struct_codec_item(ast, module, &in_name, &in_fields(op))?,
367 ));
368 let ok = fields_closed(ast, &in_fields(op), capable);
369 if ok {
370 out.push((
371 module,
372 struct_codec_item_with(ast, module, &in_name, &in_fields(op), CodecVariant::Xcdr1)?,
373 ));
374 }
375 in_ok.push(ok);
376 out.push((module, rust::Item::Struct(out_struct(ast, service, op)?)));
377 let out_name = interface::out_struct_name(&service.name, &op.name);
378 out.push((
379 module,
380 struct_codec_item(ast, module, &out_name, &out_fields(op))?,
381 ));
382 let out_ok = fields_closed(ast, &out_fields(op), capable);
383 if out_ok {
384 out.push((
385 module,
386 struct_codec_item_with(
387 ast,
388 module,
389 &out_name,
390 &out_fields(op),
391 CodecVariant::Xcdr1,
392 )?,
393 ));
394 }
395 let raised_ok = op
396 .raises
397 .iter()
398 .all(|raise| exception_ok.iter().any(|(id, ok)| *id == raise.id && *ok));
399 result_ok.push(out_ok && raised_ok);
400 }
401
402 for (op, ok) in ops.iter().zip(&result_ok) {
403 let (item, codec) = result_union(ast, module, service, op)?;
404 out.push((module, item));
405 out.push((module, codec));
406 if *ok {
407 out.push((
408 module,
409 union_codec_with(
410 &interface::result_union_name(&service.name, &op.name),
411 &result_union_cases(ast, module, service, op)?,
412 None,
413 CodecVariant::Xcdr1,
414 )?,
415 ));
416 }
417 }
418
419 let (call_item, call_codec) = call_union(service, &ops)?;
420 out.push((module, call_item));
421 out.push((module, call_codec));
422 let call_ok = in_ok.iter().all(|ok| *ok);
423 if call_ok {
424 out.push((
425 module,
426 union_codec_with(
427 &interface::call_union_name(&service.name),
428 &call_union_cases(service, &ops),
429 Some(UNKNOWN_OP_VARIANT),
430 CodecVariant::Xcdr1,
431 )?,
432 ));
433 }
434
435 let (return_item, return_codec) = return_union(service, &ops)?;
436 out.push((module, return_item));
437 out.push((module, return_codec));
438 let return_ok = ops.iter().zip(&result_ok).all(|(_, ok)| *ok);
439 if return_ok {
440 out.push((
441 module,
442 union_codec_with(
443 &interface::return_union_name(&service.name),
444 &return_union_cases(service, &ops),
445 Some(UNKNOWN_OP_VARIANT),
446 CodecVariant::Xcdr1,
447 )?,
448 ));
449 }
450
451 out.push((module, rust::Item::Struct(request_struct(service))));
452 out.push((module, rust::Item::Struct(reply_struct(service))));
453
454 let exceptions = exceptions_of(&ops);
455 out.push((
456 module,
457 rust::Item::Enum(error_enum(ast, module, service, &exceptions)?),
458 ));
459
460 out.push((
467 module,
468 dds_type_impl(
469 &interface::call_union_name(&service.name),
470 &interface::call_union_name(&service.name),
471 call_ok,
472 ),
473 ));
474 out.push((
475 module,
476 dds_type_impl(
477 &interface::return_union_name(&service.name),
478 &interface::return_union_name(&service.name),
479 return_ok,
480 ),
481 ));
482
483 if service.is_remote() {
484 out.push((module, rust::Item::Trait(service_trait(ast, service)?)));
485 out.push((module, rust::Item::Struct(client_struct(ast, service)?)));
486 out.push((module, client_impl(ast, service)?));
487 out.push((module, rust::Item::Struct(dispatcher_struct(service))));
488 for item in dispatcher_impl(ast, service)? {
489 out.push((module, item));
490 }
491 }
492
493 Ok(())
494}
495
496enum FieldSpec {
503 Real(String, Id<TypeNode>),
504 Octet(String),
505}
506
507fn in_fields(op: &Operation) -> Vec<FieldSpec> {
508 in_fields_styled(op, interface::EmptyOperationStyle::DummyMember)
509}
510
511fn in_fields_styled(op: &Operation, empty: interface::EmptyOperationStyle) -> Vec<FieldSpec> {
514 let mut out: Vec<FieldSpec> = op
515 .inputs()
516 .map(|p| FieldSpec::Real(rust::escape_keyword(&p.name), p.type_))
517 .collect();
518 if out.is_empty() && empty == interface::EmptyOperationStyle::DummyMember {
519 out.push(FieldSpec::Octet(interface::DUMMY_MEMBER.to_string()));
520 }
521 out
522}
523
524fn out_fields(op: &Operation) -> Vec<FieldSpec> {
525 out_fields_styled(op, interface::EmptyOperationStyle::DummyMember)
526}
527
528fn out_fields_styled(op: &Operation, empty: interface::EmptyOperationStyle) -> Vec<FieldSpec> {
531 let mut out: Vec<FieldSpec> = op
532 .outputs()
533 .map(|p| FieldSpec::Real(rust::escape_keyword(&p.name), p.type_))
534 .collect();
535 if let Some(returns) = op.returns {
536 let taken: Vec<&str> = out
537 .iter()
538 .map(|f| match f {
539 FieldSpec::Real(name, _) | FieldSpec::Octet(name) => name.as_str(),
540 })
541 .collect();
542 out.push(FieldSpec::Real(return_member_name(&taken), returns));
543 }
544 if out.is_empty() && empty == interface::EmptyOperationStyle::DummyMember {
545 out.push(FieldSpec::Octet(interface::DUMMY_MEMBER.to_string()));
546 }
547 out
548}
549
550fn return_member_name(taken: &[&str]) -> String {
554 if !taken.contains(&RETURN_MEMBER) {
555 return RETURN_MEMBER.to_string();
556 }
557 for n in 1..=i32::MAX {
558 let candidate = format!("{RETURN_MEMBER}{n}");
559 if !taken.contains(&candidate.as_str()) {
560 return candidate;
561 }
562 }
563 format!("{RETURN_MEMBER}_")
564}
565
566fn field(name: &str, type_: rust::Type, doc: &str) -> rust::Field {
567 rust::Field {
568 doc: Doc::line(doc),
569 attrs: Vec::new(),
570 visibility: Visibility::Public,
571 name: Ident::new(name),
572 type_,
573 }
574}
575
576fn allow_non_camel_case() -> rust::Attr {
582 rust::Attr {
583 path: Path::single("allow"),
584 args: vec![rust::AttrArg::Word(Ident::new("non_camel_case_types"))],
585 }
586}
587
588fn rust_field_type(
589 ast: &Ast,
590 from: Id<ModuleNode>,
591 spec: &FieldSpec,
592) -> Result<rust::Type, RustInterfaceError> {
593 Ok(match spec {
594 FieldSpec::Real(_, target) => rust_type_of(ast, from, Some(*target))?,
595 FieldSpec::Octet(_) => rust::Type::Primitive(rust::Primitive::U8),
596 })
597}
598
599fn struct_of(name: &str, fields: Vec<rust::Field>, doc: Doc) -> rust::Struct {
600 rust::Struct {
601 body_text: zerodds_lang_cst::common::BodyText::none(),
602 doc,
603 attrs: vec![allow_non_camel_case()],
604 derives: data_derives(),
605 visibility: Visibility::Public,
606 name: Ident::new(name),
607 generics: Vec::new(),
608 body: rust::StructBody::Named(fields),
609 }
610}
611
612fn in_struct(
613 ast: &Ast,
614 service: &Service,
615 op: &Operation,
616) -> Result<rust::Struct, RustInterfaceError> {
617 let mut fields = Vec::new();
618 for spec in in_fields_styled(op, service.mapping.empty_style()) {
619 let type_ = rust_field_type(ast, service.module, &spec)?;
620 let name = match &spec {
621 FieldSpec::Real(name, _) | FieldSpec::Octet(name) => name.clone(),
622 };
623 let doc = field_doc(&spec);
624 fields.push(field(&name, type_, &doc));
625 }
626 Ok(struct_of(
627 &interface::in_struct_name(&service.name, &op.name),
628 fields,
629 Doc::line("The `in` and `inout` parameters of one call. DDS-RPC 1.0 §7.5.1.1.4."),
630 ))
631}
632
633fn field_doc(spec: &FieldSpec) -> String {
636 match spec {
637 FieldSpec::Real(name, _) => format!("The `{name}` parameter."),
638 FieldSpec::Octet(name) => format!(
639 "`{name}`: the operation carries nothing else here. DDS-RPC 1.0 §7.5.1.1.4 rule 3c / \
640 §7.5.1.1.5 rule 3c."
641 ),
642 }
643}
644
645fn out_struct(
646 ast: &Ast,
647 service: &Service,
648 op: &Operation,
649) -> Result<rust::Struct, RustInterfaceError> {
650 let mut fields = Vec::new();
651 for spec in out_fields_styled(op, service.mapping.empty_style()) {
652 let type_ = rust_field_type(ast, service.module, &spec)?;
653 let name = match &spec {
654 FieldSpec::Real(name, _) | FieldSpec::Octet(name) => name.clone(),
655 };
656 let doc = if name == RETURN_MEMBER || name.starts_with(RETURN_MEMBER) {
657 "The operation's return value. DDS-RPC 1.0 §7.5.1.1.5 rule 3b.".to_string()
658 } else {
659 field_doc(&spec)
660 };
661 fields.push(field(&name, type_, &doc));
662 }
663 Ok(struct_of(
664 &interface::out_struct_name(&service.name, &op.name),
665 fields,
666 Doc::line(
667 "The return value and the `out`/`inout` parameters of one call. DDS-RPC 1.0 §7.5.1.1.5.",
668 ),
669 ))
670}
671
672fn request_struct(service: &Service) -> rust::Struct {
673 struct_of(
674 &interface::request_type_name(&service.name),
675 vec![
676 field(
677 "header",
678 rust::Type::named(Path::new(&["zerodds_rpc", "common_types", "RequestHeader"])),
679 "The request header. DDS-RPC 1.0 §7.5.1.1.6 rule 2.",
680 ),
681 field(
682 interface::DATA_MEMBER,
683 rust::Type::named(Path::single(&interface::call_union_name(&service.name))),
684 "The call, as a union over every operation. DDS-RPC 1.0 §7.5.1.1.6 rule 2.",
685 ),
686 ],
687 Doc::lines(&[
688 "The request topic type. DDS-RPC 1.0 §7.5.1.1.6.",
689 "",
690 "Declared for wire-shape parity with the C++ and Java bindings; \
691 `zerodds_rpc::requester::Requester` frames the header itself \
692 (`crates/rpc/src/wire_codec.rs`) and takes the `data` type — \
693 this interface's `_Call`/`_Return` — as its own two type \
694 parameters, so this type is never constructed by the \
695 generated client or dispatcher.",
696 ]),
697 )
698}
699
700fn reply_struct(service: &Service) -> rust::Struct {
701 struct_of(
702 &interface::reply_type_name(&service.name),
703 vec![
704 field(
705 "header",
706 rust::Type::named(Path::new(&["zerodds_rpc", "common_types", "ReplyHeader"])),
707 "The reply header. DDS-RPC 1.0 §7.5.1.1.7 rule 3.",
708 ),
709 field(
710 interface::DATA_MEMBER,
711 rust::Type::named(Path::single(&interface::return_union_name(&service.name))),
712 "The outcome, as a union over every non-`oneway` operation. \
713 DDS-RPC 1.0 §7.5.1.1.7 rule 3.",
714 ),
715 ],
716 Doc::line("The reply topic type. DDS-RPC 1.0 §7.5.1.1.7. See `Request`'s own doc."),
717 )
718}
719
720fn position_for(ast: &Ast, target: Id<TypeNode>) -> Position {
723 Position {
724 behaviour: ast.effective_try_construct(None, Some(target)),
725 written: None,
726 }
727}
728
729fn member_write_with(
730 ast: &Ast,
731 from: Id<ModuleNode>,
732 target: Id<TypeNode>,
733 variant: CodecVariant,
734) -> MemberWrite {
735 write_of(
736 ast,
737 from,
738 target,
739 variant.key(),
740 variant.suffix(),
741 position_for(ast, target),
742 )
743}
744
745pub(crate) fn emheader_length_code(ast: &Ast, from: Id<ModuleNode>, target: Id<TypeNode>) -> u8 {
752 enhanced::length_code(&member_write_with(ast, from, target, CodecVariant::Xcdr2))
753}
754
755fn self_field(name: &str) -> rust::Expr {
756 rust::Expr::Field {
757 receiver: Box::new(rust::Expr::Path(Path::single("self"))),
758 name: Ident::new(name),
759 }
760}
761
762fn struct_codec_item(
767 ast: &Ast,
768 from: Id<ModuleNode>,
769 type_name: &str,
770 fields: &[FieldSpec],
771) -> Result<rust::Item, RustInterfaceError> {
772 struct_codec_item_with(ast, from, type_name, fields, CodecVariant::Xcdr2)
773}
774
775fn struct_codec_item_with(
777 ast: &Ast,
778 from: Id<ModuleNode>,
779 type_name: &str,
780 fields: &[FieldSpec],
781 variant: CodecVariant,
782) -> Result<rust::Item, RustInterfaceError> {
783 let mut encode_statements = Vec::new();
784 let mut decode_statements = Vec::new();
785 let mut struct_fields = Vec::new();
786 for spec in fields {
787 let (name, kind) = match spec {
788 FieldSpec::Real(name, target) => {
789 (name.clone(), member_write_with(ast, from, *target, variant))
790 }
791 FieldSpec::Octet(name) => (name.clone(), MemberWrite::Primitive(PrimitiveKind::Octet)),
792 };
793 encode_statements.push(write_value_to("w", &name, &kind, self_field(&name), false)?);
794 decode_statements.push(rust::Statement::Let {
795 name: Ident::new(&name),
796 type_: None,
797 value: Some(rust::Expr::Try(Box::new(read_for(&name, &kind)?))),
798 mutable: false,
799 });
800 struct_fields.push((Ident::new(&name), rust::Expr::Path(Path::single(&name))));
801 }
802 if fields.is_empty() {
803 for (statements, name) in [(&mut encode_statements, "w"), (&mut decode_statements, "r")] {
806 statements.push(rust::Statement::Let {
807 name: Ident::new("_"),
808 type_: None,
809 value: Some(rust::Expr::Ref {
810 target: Box::new(rust::Expr::Path(Path::single(name))),
811 mutable: false,
812 }),
813 mutable: false,
814 });
815 }
816 }
817 encode_statements.push(rust::Statement::Tail(ok_unit()));
818 decode_statements.push(rust::Statement::Tail(rust::Expr::Call {
819 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
820 args: vec![rust::Expr::StructLit {
821 path: Path::single("Self"),
822 fields: struct_fields,
823 base: None,
824 }],
825 }));
826 Ok(rust::Item::Impl(rust::Impl {
827 doc: Doc::none(),
828 attrs: Vec::new(),
829 generics: Vec::new(),
830 trait_: None,
831 self_type: rust::Type::named(Path::single(type_name)),
832 items: vec![
833 encode_fn_item_named(&variant.encode_name(), type_name, encode_statements),
834 decode_fn_item_named(&variant.decode_name(), type_name, decode_statements),
835 ],
836 }))
837}
838
839fn ok_unit() -> rust::Expr {
840 rust::Expr::Call {
841 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
842 args: vec![rust::Expr::Tuple(Vec::new())],
843 }
844}
845
846fn writer_ref_type() -> rust::Type {
847 rust::Type::Ref {
848 target: Box::new(rust::Type::named(Path::single("Writer"))),
849 mutable: true,
850 lifetime: None,
851 }
852}
853
854fn reader_ref_type() -> rust::Type {
855 rust::Type::Ref {
856 target: Box::new(rust::Type::named(Path::single("Reader"))),
857 mutable: true,
858 lifetime: None,
859 }
860}
861
862fn wire_error_result(ok: rust::Type) -> rust::Type {
863 rust::Type::generic(
864 Path::single("Result"),
865 vec![ok, rust::Type::named(Path::single("WireError"))],
866 )
867}
868
869fn encode_fn_item_named(
870 name: &str,
871 type_name: &str,
872 statements: Vec<rust::Statement>,
873) -> rust::Item {
874 rust::Item::Function(rust::Function {
875 doc: Doc::line(&format!(
876 "Writes `{type_name}` to `w` with `crate::encode::rust_codec`'s CDR encoding."
877 )),
878 attrs: Vec::new(),
879 visibility: Visibility::Public,
880 name: Ident::new(name),
881 generics: Vec::new(),
882 params: vec![
883 rust::Param {
884 name: None,
885 type_: rust::Type::unit(),
886 receiver: Some(rust::Receiver::Ref),
887 },
888 rust::Param {
889 name: Some(Ident::new("w")),
890 type_: writer_ref_type(),
891 receiver: None,
892 },
893 ],
894 returns: Some(wire_error_result(rust::Type::unit())),
895 body: Some(rust::Block { statements }),
896 })
897}
898
899fn decode_fn_item_named(
900 name: &str,
901 type_name: &str,
902 statements: Vec<rust::Statement>,
903) -> rust::Item {
904 rust::Item::Function(rust::Function {
905 doc: Doc::line(&format!(
906 "Reads a `{type_name}` from `r` with `crate::encode::rust_codec`'s CDR decoding."
907 )),
908 attrs: Vec::new(),
909 visibility: Visibility::Public,
910 name: Ident::new(name),
911 generics: Vec::new(),
912 params: vec![rust::Param {
913 name: Some(Ident::new("r")),
914 type_: reader_ref_type(),
915 receiver: None,
916 }],
917 returns: Some(wire_error_result(rust::Type::named(Path::single("Self")))),
918 body: Some(rust::Block { statements }),
919 })
920}
921
922fn exceptions_of(ops: &[Operation]) -> Vec<Raise> {
925 let mut out: Vec<Raise> = Vec::new();
926 for op in ops {
927 for raise in &op.raises {
928 if !out.iter().any(|seen| seen.id == raise.id) {
929 out.push(raise.clone());
930 }
931 }
932 }
933 out
934}
935
936fn exception_module(
937 ast: &Ast,
938 id: Id<ExceptionNode>,
939) -> Result<Id<ModuleNode>, RustInterfaceError> {
940 let node = ast.exception(id).map_err(|_| InterfaceError::NoNode {
941 kind: "exception",
942 at: "the model".to_string(),
943 })?;
944 Ok(node.module)
945}
946
947fn exception_path(
953 ast: &Ast,
954 from: Id<ModuleNode>,
955 id: Id<ExceptionNode>,
956) -> Result<Path, RustInterfaceError> {
957 let node = ast.exception(id).map_err(|_| InterfaceError::NoNode {
958 kind: "exception",
959 at: "the model".to_string(),
960 })?;
961 relative_module_path(ast, from, node.module, &node.name)
962}
963
964fn relative_module_path(
972 ast: &Ast,
973 from: Id<ModuleNode>,
974 target_module: Id<ModuleNode>,
975 leaf: &str,
976) -> Result<Path, RustInterfaceError> {
977 let here = interface::module_chain(ast, from)?;
978 let there = interface::module_chain(ast, target_module)?;
979 let shared = here
980 .iter()
981 .zip(there.iter())
982 .take_while(|(a, b)| a.0 == b.0)
983 .count();
984 let mut segments: Vec<String> = Vec::new();
985 for _ in shared..here.len() {
986 segments.push("super".to_string());
987 }
988 for (_, name) in there.iter().skip(shared) {
989 segments.push(rust::escape_keyword(name));
990 }
991 segments.push(rust::escape_keyword(leaf));
992 let borrowed: Vec<&str> = segments.iter().map(String::as_str).collect();
993 Ok(Path::new(&borrowed))
994}
995
996fn exception_codec_item(
997 ast: &Ast,
998 id: Id<ExceptionNode>,
999) -> Result<rust::Item, RustInterfaceError> {
1000 exception_codec_item_with(ast, id, CodecVariant::Xcdr2)
1001}
1002
1003fn exception_fields_closed(
1006 ast: &Ast,
1007 id: Id<ExceptionNode>,
1008 capable: &BTreeSet<Id<TypeNode>>,
1009) -> Result<bool, RustInterfaceError> {
1010 let node = ast.exception(id).map_err(|_| InterfaceError::NoNode {
1011 kind: "exception",
1012 at: "the model".to_string(),
1013 })?;
1014 Ok(node.members.iter().all(|member| {
1015 ast.member(*member).is_ok_and(|member| {
1016 member
1017 .type_
1018 .resolved()
1019 .is_some_and(|target| type_closed(ast, target, capable, &mut BTreeSet::new()))
1020 })
1021 }))
1022}
1023
1024fn exception_codec_item_with(
1025 ast: &Ast,
1026 id: Id<ExceptionNode>,
1027 variant: CodecVariant,
1028) -> Result<rust::Item, RustInterfaceError> {
1029 let node = ast.exception(id).map_err(|_| InterfaceError::NoNode {
1030 kind: "exception",
1031 at: "the model".to_string(),
1032 })?;
1033 let module = node.module;
1034 let mut fields = Vec::new();
1035 for member in &node.members {
1036 let member_node = ast.member(*member).map_err(|_| InterfaceError::NoNode {
1037 kind: "parameter",
1038 at: node.name.clone(),
1039 })?;
1040 let Some(target) = member_node.type_.resolved() else {
1041 return Err(InterfaceError::Unresolved {
1042 construct: "exception member type",
1043 at: format!("{}::{}", node.name, member_node.name),
1044 }
1045 .into());
1046 };
1047 fields.push(FieldSpec::Real(
1048 rust::escape_keyword(&member_node.name),
1049 target,
1050 ));
1051 }
1052 struct_codec_item_with(ast, module, &node.name, &fields, variant)
1053}
1054
1055struct UnionCase {
1068 variant: String,
1069 payload: Path,
1070 discriminator: i32,
1071 doc: String,
1072}
1073
1074fn tagged_union(
1084 name: &str,
1085 doc: Doc,
1086 cases: Vec<UnionCase>,
1087 default_variant: Option<&str>,
1088) -> Result<(rust::Item, rust::Item), RustInterfaceError> {
1089 let mut variants = Vec::new();
1090 for case in &cases {
1091 variants.push(rust::Variant {
1092 doc: Doc::line(&case.doc),
1093 attrs: Vec::new(),
1094 name: Ident::new(&case.variant),
1095 body: rust::VariantBody::Tuple(vec![rust::Type::named(case.payload.clone())]),
1096 discriminant: None,
1097 });
1098 }
1099 if let Some(default_variant) = default_variant {
1100 variants.push(rust::Variant {
1101 doc: Doc::line(
1102 "The default case: a discriminator this binding does not recognise. \
1103 DDS-RPC 1.0 §7.5.1.1.6 rule 3 / §7.5.1.1.7 rule 3.",
1104 ),
1105 attrs: Vec::new(),
1106 name: Ident::new(default_variant),
1107 body: rust::VariantBody::Tuple(vec![rust::Type::Primitive(rust::Primitive::U8)]),
1108 discriminant: None,
1109 });
1110 }
1111 let item = rust::Item::Enum(rust::Enum {
1112 doc,
1113 attrs: vec![allow_non_camel_case()],
1114 derives: data_derives(),
1115 visibility: Visibility::Public,
1116 name: Ident::new(name),
1117 generics: Vec::new(),
1118 variants,
1119 });
1120
1121 let codec = union_codec_with(name, &cases, default_variant, CodecVariant::Xcdr2)?;
1122 Ok((item, codec))
1123}
1124
1125fn union_codec_with(
1129 name: &str,
1130 cases: &[UnionCase],
1131 default_variant: Option<&str>,
1132 variant: CodecVariant,
1133) -> Result<rust::Item, RustInterfaceError> {
1134 let mut encode_arms = Vec::new();
1135 let mut decode_arms = Vec::new();
1136 for case in cases {
1137 encode_arms.push(rust::MatchArm {
1138 pattern: rust::Pattern::TupleStruct {
1139 path: Path::new(&["Self", &case.variant]),
1140 elements: vec![rust::Pattern::Binding(Ident::new("value"))],
1141 },
1142 guard: None,
1143 body: encode_case_body(case.discriminator, "value", variant)?,
1144 });
1145 decode_arms.push(rust::MatchArm {
1146 pattern: rust::Pattern::Literal(Literal::int(i128::from(case.discriminator))),
1147 guard: None,
1148 body: decode_case_body(&case.variant, &case.payload, variant),
1149 });
1150 }
1151 if let Some(default_variant) = default_variant {
1152 encode_arms.push(rust::MatchArm {
1153 pattern: rust::Pattern::TupleStruct {
1154 path: Path::new(&["Self", default_variant]),
1155 elements: vec![rust::Pattern::Binding(Ident::new("value"))],
1156 },
1157 guard: None,
1158 body: encode_default_body("value")?,
1159 });
1160 decode_arms.push(rust::MatchArm {
1161 pattern: rust::Pattern::Wildcard,
1162 guard: None,
1163 body: decode_default_body(default_variant)?,
1164 });
1165 } else {
1166 decode_arms.push(rust::MatchArm {
1167 pattern: rust::Pattern::Wildcard,
1168 guard: None,
1169 body: refused_expr("an unrecognised DDS-RPC 1.0 §7.5.1.1.5 Result discriminator"),
1170 });
1171 }
1172
1173 let encode_statements = vec![rust::Statement::Tail(rust::Expr::Match {
1174 scrutinee: Box::new(rust::Expr::Path(Path::single("self"))),
1175 arms: encode_arms,
1176 })];
1177 let decode_statements = vec![
1178 rust::Statement::Let {
1179 name: Ident::new("disc"),
1180 type_: Some(rust::Type::Primitive(rust::Primitive::I32)),
1181 value: Some(rust::Expr::Try(Box::new(read_discriminator()?))),
1182 mutable: false,
1183 },
1184 rust::Statement::Tail(rust::Expr::Match {
1185 scrutinee: Box::new(rust::Expr::Path(Path::single("disc"))),
1186 arms: decode_arms,
1187 }),
1188 ];
1189 let codec = rust::Item::Impl(rust::Impl {
1190 doc: Doc::none(),
1191 attrs: Vec::new(),
1192 generics: Vec::new(),
1193 trait_: None,
1194 self_type: rust::Type::named(Path::single(name)),
1195 items: vec![
1196 encode_fn_item_named(&variant.encode_name(), name, encode_statements),
1197 decode_fn_item_named(&variant.decode_name(), name, decode_statements),
1198 ],
1199 });
1200 Ok(codec)
1201}
1202
1203fn discriminator_write(value: rust::Expr) -> Result<rust::Statement, RustInterfaceError> {
1210 Ok(write_value_to(
1211 "w",
1212 "_d",
1213 &MemberWrite::Primitive(PrimitiveKind::Int32),
1214 value,
1215 false,
1216 )?)
1217}
1218
1219fn read_discriminator() -> Result<rust::Expr, RustInterfaceError> {
1220 Ok(read_for(
1221 "_d",
1222 &MemberWrite::Primitive(PrimitiveKind::Int32),
1223 )?)
1224}
1225
1226fn encode_case_body(
1227 discriminator: i32,
1228 binding: &str,
1229 variant: CodecVariant,
1230) -> Result<rust::Expr, RustInterfaceError> {
1231 Ok(rust::Expr::Block(rust::Block {
1232 statements: vec![
1233 discriminator_write(rust::Expr::Literal(Literal::int(i128::from(discriminator))))?,
1234 rust::Statement::Tail(rust::Expr::MethodCall {
1235 receiver: Box::new(rust::Expr::Path(Path::single(binding))),
1236 method: Ident::new(&variant.encode_name()),
1237 args: vec![rust::Expr::Path(Path::single("w"))],
1238 }),
1239 ],
1240 }))
1241}
1242
1243fn encode_default_body(binding: &str) -> Result<rust::Expr, RustInterfaceError> {
1244 Ok(rust::Expr::Block(rust::Block {
1245 statements: vec![
1246 discriminator_write(rust::Expr::Literal(Literal::int(0)))?,
1247 write_value_to(
1248 "w",
1249 "unknown_op",
1250 &MemberWrite::Primitive(PrimitiveKind::Octet),
1251 rust::Expr::Path(Path::single(binding)),
1252 true,
1253 )?,
1254 rust::Statement::Tail(ok_unit()),
1255 ],
1256 }))
1257}
1258
1259fn decode_case_body(variant: &str, payload: &Path, codec: CodecVariant) -> rust::Expr {
1260 rust::Expr::Call {
1261 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
1262 args: vec![rust::Expr::Call {
1263 callee: Box::new(rust::Expr::Path(Path::new(&["Self", variant]))),
1264 args: vec![rust::Expr::Try(Box::new(rust::Expr::Call {
1265 callee: Box::new(rust::Expr::Path(payload.join(&codec.decode_name()))),
1266 args: vec![rust::Expr::Path(Path::single("r"))],
1267 }))],
1268 }],
1269 }
1270}
1271
1272fn decode_default_body(default_variant: &str) -> Result<rust::Expr, RustInterfaceError> {
1273 Ok(rust::Expr::Call {
1274 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
1275 args: vec![rust::Expr::Call {
1276 callee: Box::new(rust::Expr::Path(Path::new(&["Self", default_variant]))),
1277 args: vec![rust::Expr::Try(Box::new(read_for(
1278 "unknown_op",
1279 &MemberWrite::Primitive(PrimitiveKind::Octet),
1280 )?))],
1281 }],
1282 })
1283}
1284
1285fn refused_expr(why: &str) -> rust::Expr {
1286 rust::Expr::Call {
1287 callee: Box::new(rust::Expr::Path(Path::single("Err"))),
1288 args: vec![rust::Expr::Call {
1289 callee: Box::new(rust::Expr::Path(Path::new(&["WireError", "refused"]))),
1290 args: vec![
1291 rust::Expr::MethodCall {
1292 receiver: Box::new(rust::Expr::Path(Path::single("r"))),
1293 method: Ident::new("position"),
1294 args: Vec::new(),
1295 },
1296 rust::Expr::Literal(Literal::string(why)),
1297 ],
1298 }],
1299 }
1300}
1301
1302fn result_union(
1305 ast: &Ast,
1306 module: Id<ModuleNode>,
1307 service: &Service,
1308 op: &Operation,
1309) -> Result<(rust::Item, rust::Item), RustInterfaceError> {
1310 let cases = result_union_cases(ast, module, service, op)?;
1311 let name = interface::result_union_name(&service.name, &op.name);
1312 tagged_union(
1313 &name,
1314 Doc::line(&format!(
1315 "The outcome of `{}`. DDS-RPC 1.0 §7.5.1.1.5.",
1316 op.name
1317 )),
1318 cases,
1319 None,
1320 )
1321}
1322
1323fn result_union_cases(
1325 ast: &Ast,
1326 module: Id<ModuleNode>,
1327 service: &Service,
1328 op: &Operation,
1329) -> Result<Vec<UnionCase>, RustInterfaceError> {
1330 let mut cases = vec![UnionCase {
1331 variant: "Ok".to_string(),
1332 payload: Path::single(&interface::out_struct_name(&service.name, &op.name)),
1333 discriminator: interface::RETCODE_OK,
1334 doc: "`dds::RETCODE_OK`. DDS-RPC 1.0 §7.5.1.1.5 rule 3a.".to_string(),
1335 }];
1336 for raise in &op.raises {
1337 cases.push(UnionCase {
1338 variant: rust::escape_keyword(&raise.name),
1339 payload: exception_path(ast, module, raise.id)?,
1340 discriminator: interface::exception_hash(&raise.scoped_name),
1341 doc: format!("The `{}` case. DDS-RPC 1.0 §7.5.1.1.5 rule 5.", raise.name),
1342 });
1343 }
1344 Ok(cases)
1345}
1346
1347fn exception_type(
1348 ast: &Ast,
1349 from: Id<ModuleNode>,
1350 id: Id<ExceptionNode>,
1351) -> Result<rust::Type, RustInterfaceError> {
1352 Ok(rust::Type::named(exception_path(ast, from, id)?))
1353}
1354
1355fn call_union(
1356 service: &Service,
1357 ops: &[Operation],
1358) -> Result<(rust::Item, rust::Item), RustInterfaceError> {
1359 let cases = call_union_cases(service, ops);
1360 tagged_union(
1361 &interface::call_union_name(&service.name),
1362 Doc::line("The request union over every operation. DDS-RPC 1.0 §7.5.1.1.6."),
1363 cases,
1364 Some(UNKNOWN_OP_VARIANT),
1365 )
1366}
1367
1368fn call_union_cases(service: &Service, ops: &[Operation]) -> Vec<UnionCase> {
1370 ops.iter()
1371 .map(|op| UnionCase {
1372 variant: variant_name(&op.name),
1373 payload: Path::single(&interface::in_struct_name(&service.name, &op.name)),
1374 discriminator: interface::operation_hash(&op.name),
1375 doc: format!("`{}`. DDS-RPC 1.0 §7.5.1.1.6 rule 5.", op.name),
1376 })
1377 .collect()
1378}
1379
1380fn return_union(
1381 service: &Service,
1382 ops: &[Operation],
1383) -> Result<(rust::Item, rust::Item), RustInterfaceError> {
1384 let cases = return_union_cases(service, ops);
1385 tagged_union(
1386 &interface::return_union_name(&service.name),
1387 Doc::line("The reply union. DDS-RPC 1.0 §7.5.1.1.7."),
1388 cases,
1389 Some(UNKNOWN_OP_VARIANT),
1390 )
1391}
1392
1393fn return_union_cases(service: &Service, ops: &[Operation]) -> Vec<UnionCase> {
1395 ops.iter()
1396 .map(|op| UnionCase {
1397 variant: variant_name(&op.name),
1398 payload: Path::single(&interface::result_union_name(&service.name, &op.name)),
1399 discriminator: interface::operation_hash(&op.name),
1400 doc: format!("`{}`. DDS-RPC 1.0 §7.5.1.1.7 rule 5b.", op.name),
1401 })
1402 .collect()
1403}
1404
1405const UNKNOWN_OP_VARIANT: &str = "UnknownOp";
1411
1412const UNKNOWN_EX_VARIANT: &str = "UnknownEx";
1416
1417fn variant_name(name: &str) -> String {
1421 let mut out = String::new();
1422 for segment in name.split('_') {
1423 let mut chars = segment.chars();
1424 if let Some(first) = chars.next() {
1425 out.extend(first.to_uppercase());
1426 out.extend(chars);
1427 }
1428 }
1429 if out.is_empty() {
1430 out.push_str("Op");
1431 }
1432 out
1433}
1434
1435fn field_expr(receiver: &str, field_name: &str) -> rust::Expr {
1436 rust::Expr::Field {
1437 receiver: Box::new(rust::Expr::Path(Path::single(receiver))),
1438 name: Ident::new(field_name),
1439 }
1440}
1441
1442fn field_vis(name: &str, type_: rust::Type, visibility: Visibility, doc: &str) -> rust::Field {
1443 rust::Field {
1444 doc: Doc::line(doc),
1445 attrs: Vec::new(),
1446 visibility,
1447 name: Ident::new(name),
1448 type_,
1449 }
1450}
1451
1452fn error_type_name(service: &Service) -> String {
1453 format!("{}Error", service.name)
1454}
1455
1456pub(crate) fn dds_type_impl(type_name: &str, dds_name: &str, xcdr1: bool) -> rust::Item {
1471 let type_name_const = rust::Item::Const(rust::Const {
1472 doc: Doc::line(
1473 "Informational only: `zerodds_rpc::requester::Requester` never \
1474 registers this as a DDS topic type — it frames the request/reply \
1475 header itself and writes the sample through `DdsType::encode` \
1476 directly. See `crates/rpc/src/wire_codec.rs`.",
1477 ),
1478 attrs: Vec::new(),
1479 visibility: Visibility::Private,
1480 name: Ident::new("TYPE_NAME"),
1481 type_: rust::Type::Ref {
1482 target: Box::new(rust::Type::Primitive(rust::Primitive::Str)),
1483 mutable: false,
1484 lifetime: Some(Ident::new("static")),
1485 },
1486 value: rust::Expr::Literal(Literal::string(dds_name)),
1487 });
1488
1489 let encode_fn = rust::Item::Function(rust::Function {
1490 doc: Doc::none(),
1491 attrs: Vec::new(),
1492 visibility: Visibility::Private,
1493 name: Ident::new("encode"),
1494 generics: Vec::new(),
1495 params: vec![
1496 rust::Param {
1497 name: None,
1498 type_: rust::Type::unit(),
1499 receiver: Some(rust::Receiver::Ref),
1500 },
1501 rust::Param {
1502 name: Some(Ident::new("out")),
1503 type_: rust::Type::Ref {
1504 target: Box::new(rust::Type::generic(
1505 Path::single("Vec"),
1506 vec![rust::Type::Primitive(rust::Primitive::U8)],
1507 )),
1508 mutable: true,
1509 lifetime: None,
1510 },
1511 receiver: None,
1512 },
1513 ],
1514 returns: Some(rust::Type::generic(
1515 Path::single("Result"),
1516 vec![
1517 rust::Type::unit(),
1518 rust::Type::named(Path::new(&["zerodds_dcps", "dds_type", "EncodeError"])),
1519 ],
1520 )),
1521 body: Some(rust::Block {
1522 statements: vec![
1523 rust::Statement::Let {
1524 name: Ident::new("w"),
1525 type_: None,
1526 value: Some(rust::Expr::Call {
1527 callee: Box::new(rust::Expr::Path(Path::new(&[
1528 "zerodds_wire",
1529 "Writer",
1530 "with_max_align",
1531 ]))),
1532 args: vec![
1533 rust::Expr::Path(Path::new(&["zerodds_wire", "Endianness", "Little"])),
1534 rust::Expr::Literal(Literal::int(4)),
1535 ],
1536 }),
1537 mutable: true,
1538 },
1539 rust::Statement::Semi(rust::Expr::Try(Box::new(rust::Expr::Call {
1540 callee: Box::new(rust::Expr::Path(Path::new(&["Self", "encode"]))),
1541 args: vec![
1542 rust::Expr::Path(Path::single("self")),
1543 rust::Expr::Ref {
1544 target: Box::new(rust::Expr::Path(Path::single("w"))),
1545 mutable: true,
1546 },
1547 ],
1548 }))),
1549 rust::Statement::Semi(rust::Expr::MethodCall {
1550 receiver: Box::new(rust::Expr::Path(Path::single("out"))),
1551 method: Ident::new("extend_from_slice"),
1552 args: vec![rust::Expr::Ref {
1553 target: Box::new(rust::Expr::MethodCall {
1554 receiver: Box::new(rust::Expr::Path(Path::single("w"))),
1555 method: Ident::new("finish"),
1556 args: Vec::new(),
1557 }),
1558 mutable: false,
1559 }],
1560 }),
1561 rust::Statement::Tail(ok_unit()),
1562 ],
1563 }),
1564 });
1565
1566 let decode_fn = rust::Item::Function(rust::Function {
1567 doc: Doc::none(),
1568 attrs: Vec::new(),
1569 visibility: Visibility::Private,
1570 name: Ident::new("decode"),
1571 generics: Vec::new(),
1572 params: vec![rust::Param {
1573 name: Some(Ident::new("bytes")),
1574 type_: rust::Type::Ref {
1575 target: Box::new(rust::Type::Slice(Box::new(rust::Type::Primitive(
1576 rust::Primitive::U8,
1577 )))),
1578 mutable: false,
1579 lifetime: None,
1580 },
1581 receiver: None,
1582 }],
1583 returns: Some(rust::Type::generic(
1584 Path::single("Result"),
1585 vec![
1586 rust::Type::named(Path::single("Self")),
1587 rust::Type::named(Path::new(&["zerodds_dcps", "dds_type", "DecodeError"])),
1588 ],
1589 )),
1590 body: Some(rust::Block {
1591 statements: vec![
1592 rust::Statement::Let {
1593 name: Ident::new("r"),
1594 type_: None,
1595 value: Some(rust::Expr::Call {
1596 callee: Box::new(rust::Expr::Path(Path::new(&[
1597 "zerodds_wire",
1598 "Reader",
1599 "with_max_align",
1600 ]))),
1601 args: vec![
1602 rust::Expr::Path(Path::single("bytes")),
1603 rust::Expr::Path(Path::new(&["zerodds_wire", "Endianness", "Little"])),
1604 rust::Expr::Literal(Literal::int(4)),
1605 ],
1606 }),
1607 mutable: true,
1608 },
1609 rust::Statement::Tail(rust::Expr::Call {
1610 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
1611 args: vec![rust::Expr::Try(Box::new(rust::Expr::Call {
1612 callee: Box::new(rust::Expr::Path(Path::new(&["Self", "decode"]))),
1613 args: vec![rust::Expr::Ref {
1614 target: Box::new(rust::Expr::Path(Path::single("r"))),
1615 mutable: true,
1616 }],
1617 }))],
1618 }),
1619 ],
1620 }),
1621 });
1622
1623 rust::Item::Impl(rust::Impl {
1624 doc: Doc::none(),
1625 attrs: Vec::new(),
1626 generics: Vec::new(),
1627 trait_: Some(rust::Type::named(Path::new(&[
1628 "zerodds_dcps",
1629 "dds_type",
1630 "DdsType",
1631 ]))),
1632 self_type: rust::Type::named(Path::single(type_name)),
1633 items: {
1634 let mut items = vec![type_name_const, encode_fn, decode_fn];
1635 if xcdr1 {
1639 items.extend(continuing_codec_fns());
1640 }
1641 items
1642 },
1643 })
1644}
1645
1646fn continuing_codec_fns() -> Vec<rust::Item> {
1657 fn path(parts: &[&str]) -> rust::Expr {
1658 rust::Expr::Path(Path::new(parts))
1659 }
1660 fn call(parts: &[&str], args: Vec<rust::Expr>) -> rust::Expr {
1661 rust::Expr::Call {
1662 callee: Box::new(path(parts)),
1663 args,
1664 }
1665 }
1666 fn local(name: &str) -> rust::Expr {
1667 rust::Expr::Path(Path::single(name))
1668 }
1669 fn mut_ref(name: &str) -> rust::Expr {
1670 rust::Expr::Ref {
1671 target: Box::new(local(name)),
1672 mutable: true,
1673 }
1674 }
1675 fn let_(name: &str, value: rust::Expr, mutable: bool) -> rust::Statement {
1676 rust::Statement::Let {
1677 name: Ident::new(name),
1678 type_: None,
1679 value: Some(value),
1680 mutable,
1681 }
1682 }
1683 fn method(receiver: rust::Expr, name: &str, args: Vec<rust::Expr>) -> rust::Expr {
1684 rust::Expr::MethodCall {
1685 receiver: Box::new(receiver),
1686 method: Ident::new(name),
1687 args,
1688 }
1689 }
1690 fn lit(n: i128) -> rust::Expr {
1691 rust::Expr::Literal(Literal::int(n))
1692 }
1693 let little = || path(&["zerodds_wire", "Endianness", "Little"]);
1694 let self_param = rust::Param {
1695 name: None,
1696 type_: rust::Type::unit(),
1697 receiver: Some(rust::Receiver::Ref),
1698 };
1699 let bytes_param = |name: &str| rust::Param {
1700 name: Some(Ident::new(name)),
1701 type_: rust::Type::Ref {
1702 target: Box::new(rust::Type::Slice(Box::new(rust::Type::Primitive(
1703 rust::Primitive::U8,
1704 )))),
1705 mutable: false,
1706 lifetime: None,
1707 },
1708 receiver: None,
1709 };
1710 let vec_u8_mut = rust::Type::Ref {
1711 target: Box::new(rust::Type::generic(
1712 Path::single("Vec"),
1713 vec![rust::Type::Primitive(rust::Primitive::U8)],
1714 )),
1715 mutable: true,
1716 lifetime: None,
1717 };
1718 let bool_param = |name: &str| rust::Param {
1719 name: Some(Ident::new(name)),
1720 type_: rust::Type::Primitive(rust::Primitive::Bool),
1721 receiver: None,
1722 };
1723 let encode_result = || {
1724 rust::Type::generic(
1725 Path::single("Result"),
1726 vec![
1727 rust::Type::unit(),
1728 rust::Type::named(Path::new(&["zerodds_dcps", "dds_type", "EncodeError"])),
1729 ],
1730 )
1731 };
1732 let decode_result = || {
1733 rust::Type::generic(
1734 Path::single("Result"),
1735 vec![
1736 rust::Type::named(Path::single("Self")),
1737 rust::Type::named(Path::new(&["zerodds_dcps", "dds_type", "DecodeError"])),
1738 ],
1739 )
1740 };
1741 let function = |name: &str,
1742 params: Vec<rust::Param>,
1743 returns: rust::Type,
1744 statements: Vec<rust::Statement>| {
1745 rust::Item::Function(rust::Function {
1746 doc: Doc::none(),
1747 attrs: Vec::new(),
1748 visibility: Visibility::Private,
1749 name: Ident::new(name),
1750 generics: Vec::new(),
1751 params,
1752 returns: Some(returns),
1753 body: Some(rust::Block { statements }),
1754 })
1755 };
1756 let cap = || rust::Expr::If {
1758 condition: Box::new(local("xcdr1")),
1759 then: rust::Block {
1760 statements: vec![rust::Statement::Tail(lit(8))],
1761 },
1762 otherwise: Some(rust::Block {
1763 statements: vec![rust::Statement::Tail(lit(4))],
1764 }),
1765 };
1766
1767 let by_version = |xcdr1_fn: &str, xcdr2_fn: &str, args: Vec<rust::Expr>| rust::Expr::If {
1770 condition: Box::new(local("xcdr1")),
1771 then: rust::Block {
1772 statements: vec![rust::Statement::Tail(call(
1773 &["Self", xcdr1_fn],
1774 args.clone(),
1775 ))],
1776 },
1777 otherwise: Some(rust::Block {
1778 statements: vec![rust::Statement::Tail(call(&["Self", xcdr2_fn], args))],
1779 }),
1780 };
1781
1782 let encode_xcdr1 = function(
1784 "encode_xcdr1",
1785 vec![
1786 self_param.clone(),
1787 rust::Param {
1788 name: Some(Ident::new("out")),
1789 type_: vec_u8_mut.clone(),
1790 receiver: None,
1791 },
1792 ],
1793 encode_result(),
1794 vec![
1795 let_(
1796 "w",
1797 call(
1798 &["zerodds_wire", "Writer", "with_max_align"],
1799 vec![little(), lit(8)],
1800 ),
1801 true,
1802 ),
1803 rust::Statement::Semi(rust::Expr::Try(Box::new(call(
1804 &["Self", "encode_xcdr1"],
1805 vec![local("self"), mut_ref("w")],
1806 )))),
1807 rust::Statement::Semi(method(
1808 local("out"),
1809 "extend_from_slice",
1810 vec![rust::Expr::Ref {
1811 target: Box::new(method(local("w"), "finish", Vec::new())),
1812 mutable: false,
1813 }],
1814 )),
1815 rust::Statement::Tail(call(&["Ok"], vec![rust::Expr::Path(Path::single("()"))])),
1816 ],
1817 );
1818
1819 let decode_xcdr1 = function(
1821 "decode_xcdr1",
1822 vec![bytes_param("bytes")],
1823 decode_result(),
1824 vec![
1825 let_(
1826 "r",
1827 call(
1828 &["zerodds_wire", "Reader", "with_max_align"],
1829 vec![local("bytes"), little(), lit(8)],
1830 ),
1831 true,
1832 ),
1833 rust::Statement::Tail(call(
1834 &["Ok"],
1835 vec![rust::Expr::Try(Box::new(call(
1836 &["Self", "decode_xcdr1"],
1837 vec![mut_ref("r")],
1838 )))],
1839 )),
1840 ],
1841 );
1842
1843 let encode_continuing = function(
1845 "encode_continuing",
1846 vec![
1847 self_param,
1848 rust::Param {
1849 name: Some(Ident::new("prefix")),
1850 type_: vec_u8_mut,
1851 receiver: None,
1852 },
1853 bool_param("xcdr1"),
1854 ],
1855 encode_result(),
1856 vec![
1857 let_(
1858 "w",
1859 call(
1860 &["zerodds_wire", "Writer", "continuing"],
1861 vec![
1862 little(),
1863 cap(),
1864 call(&["core", "mem", "take"], vec![local("prefix")]),
1865 ],
1866 ),
1867 true,
1868 ),
1869 rust::Statement::Semi(rust::Expr::Try(Box::new(by_version(
1870 "encode_xcdr1",
1871 "encode",
1872 vec![local("self"), mut_ref("w")],
1873 )))),
1874 rust::Statement::Semi(rust::Expr::Assign {
1875 target: Box::new(rust::Expr::Deref(Box::new(local("prefix")))),
1876 value: Box::new(method(local("w"), "finish", Vec::new())),
1877 }),
1878 rust::Statement::Tail(call(&["Ok"], vec![rust::Expr::Path(Path::single("()"))])),
1879 ],
1880 );
1881
1882 let decode_continuing = function(
1884 "decode_continuing",
1885 vec![
1886 bytes_param("bytes"),
1887 rust::Param {
1888 name: Some(Ident::new("offset")),
1889 type_: rust::Type::named(Path::single("usize")),
1890 receiver: None,
1891 },
1892 bool_param("xcdr1"),
1893 ],
1894 decode_result(),
1895 vec![
1896 let_(
1897 "r",
1898 call(
1899 &["zerodds_wire", "Reader", "with_max_align"],
1900 vec![local("bytes"), little(), cap()],
1901 ),
1902 true,
1903 ),
1904 rust::Statement::Semi(rust::Expr::Try(Box::new(method(
1905 local("r"),
1906 "skip_to",
1907 vec![local("offset")],
1908 )))),
1909 rust::Statement::Tail(call(
1910 &["Ok"],
1911 vec![rust::Expr::Try(Box::new(by_version(
1912 "decode_xcdr1",
1913 "decode",
1914 vec![mut_ref("r")],
1915 )))],
1916 )),
1917 ],
1918 );
1919
1920 vec![
1921 encode_xcdr1,
1922 decode_xcdr1,
1923 encode_continuing,
1924 decode_continuing,
1925 ]
1926}
1927
1928fn error_enum(
1931 ast: &Ast,
1932 module: Id<ModuleNode>,
1933 service: &Service,
1934 exceptions: &[Raise],
1935) -> Result<rust::Enum, RustInterfaceError> {
1936 let mut variants = Vec::new();
1937 for raise in exceptions {
1938 variants.push(rust::Variant {
1939 doc: Doc::line(&format!(
1940 "The declared `{}` exception. DDS-RPC 1.0 §7.5.1.1.5.",
1941 raise.name
1942 )),
1943 attrs: Vec::new(),
1944 name: Ident::new(&rust::escape_keyword(&raise.name)),
1945 body: rust::VariantBody::Tuple(vec![exception_type(ast, module, raise.id)?]),
1946 discriminant: None,
1947 });
1948 }
1949 variants.push(rust::Variant {
1950 doc: Doc::lines(&[
1951 "A DDS-RPC 1.0 §7.11.1.3 remote/system exception, or a transport",
1952 "failure `zerodds_rpc::requester::Requester` reports — a timeout,",
1953 "an encode/decode failure, or a DCPS error.",
1954 ]),
1955 attrs: Vec::new(),
1956 name: Ident::new("Remote"),
1957 body: rust::VariantBody::Tuple(vec![rust::Type::named(Path::new(&[
1958 "zerodds_rpc",
1959 "error",
1960 "RpcError",
1961 ]))]),
1962 discriminant: None,
1963 });
1964 Ok(rust::Enum {
1965 doc: Doc::line(&format!("Every way a call to `{}` can fail.", service.name)),
1966 attrs: Vec::new(),
1967 derives: data_derives(),
1968 visibility: Visibility::Public,
1969 name: Ident::new(&error_type_name(service)),
1970 generics: Vec::new(),
1971 variants,
1972 })
1973}
1974
1975#[derive(Clone, PartialEq, Eq)]
1983enum OutFieldSource {
1984 Return,
1985 Out(String),
1986 InOut(String),
1987}
1988
1989fn same_source(a: &OutFieldSource, b: &OutFieldSource) -> bool {
1990 matches!((a, b), (OutFieldSource::Return, OutFieldSource::Return))
1991 || matches!((a, b), (OutFieldSource::Out(x), OutFieldSource::Out(y)) if x == y)
1992}
1993
1994fn out_field_map(op: &Operation) -> Vec<(String, OutFieldSource)> {
1997 let mut out = Vec::new();
1998 for p in op.outputs() {
1999 let field_name = rust::escape_keyword(&p.name);
2000 let source = match p.direction {
2001 ParamDirection::InOut => OutFieldSource::InOut(p.name.clone()),
2002 _ => OutFieldSource::Out(p.name.clone()),
2003 };
2004 out.push((field_name, source));
2005 }
2006 if op.returns.is_some() {
2007 let taken: Vec<&str> = out.iter().map(|(name, _)| name.as_str()).collect();
2008 out.push((return_member_name(&taken), OutFieldSource::Return));
2009 }
2010 out
2011}
2012
2013fn ok_positions(op: &Operation) -> Vec<OutFieldSource> {
2020 let mut out = Vec::new();
2021 if op.returns.is_some() {
2022 out.push(OutFieldSource::Return);
2023 }
2024 for p in op
2025 .params
2026 .iter()
2027 .filter(|p| matches!(p.direction, ParamDirection::Out))
2028 {
2029 out.push(OutFieldSource::Out(p.name.clone()));
2030 }
2031 out
2032}
2033
2034fn position_index(
2040 positions: &[OutFieldSource],
2041 target: &OutFieldSource,
2042 op: &Operation,
2043) -> Result<usize, RustInterfaceError> {
2044 positions
2045 .iter()
2046 .position(|p| same_source(p, target))
2047 .ok_or_else(|| {
2048 RustInterfaceError::Interface(InterfaceError::Unresolved {
2049 construct: "out value's position in the Ok value",
2050 at: op.name.clone(),
2051 })
2052 })
2053}
2054
2055fn ok_component_expr(
2058 positions: &[OutFieldSource],
2059 source: &OutFieldSource,
2060 binding: &str,
2061 op: &Operation,
2062) -> Result<rust::Expr, RustInterfaceError> {
2063 if positions.len() <= 1 {
2064 Ok(rust::Expr::Path(Path::single(binding)))
2065 } else {
2066 let index = position_index(positions, source, op)?;
2067 Ok(field_expr(binding, &index.to_string()))
2068 }
2069}
2070
2071fn ok_type(
2072 ast: &Ast,
2073 module: Id<ModuleNode>,
2074 op: &Operation,
2075) -> Result<rust::Type, RustInterfaceError> {
2076 let mut parts = Vec::new();
2077 if let Some(target) = op.returns {
2078 parts.push(rust_type_of(ast, module, Some(target))?);
2079 }
2080 for p in op
2081 .params
2082 .iter()
2083 .filter(|p| matches!(p.direction, ParamDirection::Out))
2084 {
2085 parts.push(rust_type_of(ast, module, Some(p.type_))?);
2086 }
2087 Ok(match parts.len() {
2088 0 => rust::Type::unit(),
2089 1 => parts.remove(0),
2090 _ => rust::Type::Tuple(parts),
2091 })
2092}
2093
2094fn op_return_type(
2095 ast: &Ast,
2096 module: Id<ModuleNode>,
2097 service: &Service,
2098 op: &Operation,
2099) -> Result<rust::Type, RustInterfaceError> {
2100 Ok(rust::Type::generic(
2101 Path::single("Result"),
2102 vec![
2103 ok_type(ast, module, op)?,
2104 rust::Type::named(Path::single(&error_type_name(service))),
2105 ],
2106 ))
2107}
2108
2109fn op_params(
2117 ast: &Ast,
2118 module: Id<ModuleNode>,
2119 op: &Operation,
2120) -> Result<Vec<rust::Param>, RustInterfaceError> {
2121 let mut params = vec![rust::Param {
2122 name: None,
2123 type_: rust::Type::unit(),
2124 receiver: Some(rust::Receiver::Ref),
2125 }];
2126 for p in &op.params {
2127 let base = rust_type_of(ast, module, Some(p.type_))?;
2128 match p.direction {
2129 ParamDirection::In => params.push(rust::Param {
2130 name: Some(Ident::new(&rust::escape_keyword(&p.name))),
2131 type_: base,
2132 receiver: None,
2133 }),
2134 ParamDirection::InOut => params.push(rust::Param {
2135 name: Some(Ident::new(&rust::escape_keyword(&p.name))),
2136 type_: rust::Type::Ref {
2137 target: Box::new(base),
2138 mutable: true,
2139 lifetime: None,
2140 },
2141 receiver: None,
2142 }),
2143 ParamDirection::Out => {}
2144 }
2145 }
2146 Ok(params)
2147}
2148
2149fn method_doc(op: &Operation) -> String {
2155 op.raises_line().unwrap_or_else(|| {
2156 format!(
2157 "Generated from the IDL operation `{}`; raises nothing.",
2158 op.name
2159 )
2160 })
2161}
2162
2163fn trait_method_signature(
2164 ast: &Ast,
2165 service: &Service,
2166 op: &Operation,
2167) -> Result<rust::Function, RustInterfaceError> {
2168 Ok(rust::Function {
2169 doc: Doc::line(&method_doc(op)),
2170 attrs: Vec::new(),
2171 visibility: Visibility::Private,
2172 name: Ident::new(&rust::escape_keyword(&op.name)),
2173 generics: Vec::new(),
2174 params: op_params(ast, service.module, op)?,
2175 returns: Some(op_return_type(ast, service.module, service, op)?),
2176 body: None,
2177 })
2178}
2179
2180fn base_interface_path(
2181 ast: &Ast,
2182 from: Id<ModuleNode>,
2183 base: &interface::Base,
2184 suffix: &str,
2185) -> Result<Path, RustInterfaceError> {
2186 let node = ast.interface(base.id).map_err(|_| InterfaceError::NoNode {
2187 kind: "base interface",
2188 at: base.name.clone(),
2189 })?;
2190 relative_module_path(ast, from, node.module, &format!("{}{suffix}", node.name))
2191}
2192
2193fn service_trait(ast: &Ast, service: &Service) -> Result<rust::Trait, RustInterfaceError> {
2194 let mut supertraits = Vec::new();
2195 for base in &service.bases {
2196 supertraits.push(rust::Type::named(base_interface_path(
2197 ast,
2198 service.module,
2199 base,
2200 "",
2201 )?));
2202 }
2203 let mut items = Vec::new();
2204 for op in service.all_operations() {
2205 items.push(rust::Item::Function(trait_method_signature(
2206 ast, service, &op,
2207 )?));
2208 }
2209 let mut doc = Doc::line(&format!(
2210 "The `{}` service. DDS-RPC 1.0 §7.5.1.1.3, §7.11.",
2211 service.name
2212 ));
2213 doc.push(
2214 "`out`/`inout` parameters are `&mut` arguments; a pure `out` parameter \
2215 besides the declared return travels back through the `Ok` value instead \
2216 — `()` for none, the bare value for one, a tuple (return first, then \
2217 every `out` in declaration order) for more than one.",
2218 );
2219 Ok(rust::Trait {
2220 doc,
2221 attrs: Vec::new(),
2222 visibility: Visibility::Public,
2223 name: Ident::new(&rust::escape_keyword(&service.name)),
2224 generics: Vec::new(),
2225 supertraits,
2226 items,
2227 })
2228}
2229
2230fn client_type_name(service: &Service) -> String {
2233 format!("{}Client", service.name)
2234}
2235
2236fn client_field_name(base: &interface::Base) -> String {
2237 format!("base_{}", base.name.to_lowercase())
2238}
2239
2240fn client_struct(ast: &Ast, service: &Service) -> Result<rust::Struct, RustInterfaceError> {
2241 let mut fields = vec![field_vis(
2242 "requester",
2243 rust::Type::generic(
2244 Path::new(&["zerodds_rpc", "requester", "Requester"]),
2245 vec![
2246 rust::Type::named(Path::single(&interface::call_union_name(&service.name))),
2247 rust::Type::named(Path::single(&interface::return_union_name(&service.name))),
2248 ],
2249 ),
2250 Visibility::Private,
2251 "The `Requester` this interface's own operations send `${I}_Call`s \
2252 through and read `${I}_Return`s back from — DDS-RPC 1.0 §7.9.",
2253 )];
2254 for base in &service.bases {
2255 let path = base_interface_path(ast, service.module, base, "Client")?;
2256 fields.push(field_vis(
2257 &client_field_name(base),
2258 rust::Type::named(path),
2259 Visibility::Public,
2260 &format!(
2261 "The `{}` client this interface's own base composes — DDS-RPC 1.0 \
2262 §7.5.1.1.8 keeps its topic separate from this one's.",
2263 base.name
2264 ),
2265 ));
2266 }
2267 let mut doc = Doc::line("A typed DDS-RPC client. DDS-RPC 1.0 §7.11.1.5.4/§7.9.");
2268 doc.push(
2269 "A base interface's operations are not repeated here — DDS-RPC 1.0 keeps a \
2270 base's topic separate from a derived interface's own (§7.5.1.1.8) — they \
2271 are reachable through the base client field this struct composes, one per \
2272 parent interface, so a call to an inherited operation is one field access \
2273 away rather than absent.",
2274 );
2275 Ok(rust::Struct {
2276 body_text: zerodds_lang_cst::common::BodyText::none(),
2277 doc,
2278 attrs: Vec::new(),
2279 derives: Vec::new(),
2280 visibility: Visibility::Public,
2281 name: Ident::new(&client_type_name(service)),
2282 generics: Vec::new(),
2283 body: rust::StructBody::Named(fields),
2284 })
2285}
2286
2287fn dcps_participant_type() -> rust::Type {
2288 rust::Type::Ref {
2289 target: Box::new(rust::Type::named(Path::new(&[
2290 "zerodds_dcps",
2291 "participant",
2292 "DomainParticipant",
2293 ]))),
2294 mutable: false,
2295 lifetime: None,
2296 }
2297}
2298
2299fn rpc_qos_type() -> rust::Type {
2300 rust::Type::Ref {
2301 target: Box::new(rust::Type::named(Path::new(&[
2302 "zerodds_rpc",
2303 "qos_profile",
2304 "RpcQos",
2305 ]))),
2306 mutable: false,
2307 lifetime: None,
2308 }
2309}
2310
2311fn rpc_result_of(ok: rust::Type) -> rust::Type {
2312 rust::Type::generic(Path::new(&["zerodds_rpc", "error", "RpcResult"]), vec![ok])
2313}
2314
2315fn try_call(callee: Path, args: Vec<rust::Expr>) -> rust::Expr {
2316 rust::Expr::Try(Box::new(rust::Expr::Call {
2317 callee: Box::new(rust::Expr::Path(callee)),
2318 args,
2319 }))
2320}
2321
2322fn owned_string_literal(text: &str) -> rust::Expr {
2325 rust::Expr::MethodCall {
2326 receiver: Box::new(rust::Expr::Literal(Literal::string(text))),
2327 method: Ident::new("to_string"),
2328 args: Vec::new(),
2329 }
2330}
2331
2332fn optional_owned_string(value: Option<&str>) -> rust::Expr {
2335 match value {
2336 Some(text) => rust::Expr::Call {
2337 callee: Box::new(rust::Expr::Path(Path::single("Some"))),
2338 args: vec![owned_string_literal(text)],
2339 },
2340 None => rust::Expr::Path(Path::new(&["None"])),
2341 }
2342}
2343
2344fn annotated_topics_expr(service: &Service) -> rust::Expr {
2355 rust::Expr::Ref {
2356 target: Box::new(rust::Expr::StructLit {
2357 path: Path::new(&["zerodds_rpc", "topic_naming", "AnnotatedTopics"]),
2358 fields: vec![
2359 (
2360 Ident::new("service_name"),
2361 optional_owned_string(Some(service.service_name.as_str())),
2362 ),
2363 (
2364 Ident::new("request_topic"),
2365 optional_owned_string(service.request_topic.as_deref()),
2366 ),
2367 (
2368 Ident::new("reply_topic"),
2369 optional_owned_string(service.reply_topic.as_deref()),
2370 ),
2371 (
2372 Ident::new("overrides"),
2373 rust::Expr::Call {
2374 callee: Box::new(rust::Expr::Path(Path::new(&[
2375 "zerodds_rpc",
2376 "topic_naming",
2377 "TopicNameOverrides",
2378 "none",
2379 ]))),
2380 args: Vec::new(),
2381 },
2382 ),
2383 ],
2384 base: None,
2385 }),
2386 mutable: false,
2387 }
2388}
2389
2390fn endpoint_options_expr(ast: &Ast, service: &Service) -> Result<rust::Expr, RustInterfaceError> {
2396 Ok(rust::Expr::Ref {
2397 target: Box::new(endpoint_options_value(ast, service)?),
2398 mutable: false,
2399 })
2400}
2401
2402fn endpoint_options_value(ast: &Ast, service: &Service) -> Result<rust::Expr, RustInterfaceError> {
2404 let scope = interface::module_chain(ast, service.module)?
2405 .into_iter()
2406 .map(|(_, name)| name)
2407 .collect::<Vec<_>>()
2408 .join("::");
2409 let mut options = rust::Expr::Call {
2410 callee: Box::new(rust::Expr::Path(Path::new(&[
2411 "zerodds_rpc",
2412 "endpoint_options",
2413 "RpcEndpointOptions",
2414 "for_interface",
2415 ]))),
2416 args: vec![
2417 rust::Expr::Literal(Literal::string(&scope)),
2418 rust::Expr::Literal(Literal::string(&service.name)),
2419 ],
2420 };
2421 for base in &service.bases {
2422 options = rust::Expr::MethodCall {
2423 receiver: Box::new(options),
2424 method: Ident::new("with_parent"),
2425 args: vec![rust::Expr::Literal(Literal::string(&base.qualified_name))],
2426 };
2427 }
2428 if service.mapping.is_enhanced() {
2429 options = rust::Expr::MethodCall {
2430 receiver: Box::new(options),
2431 method: Ident::new("with_mapping"),
2432 args: vec![rust::Expr::Path(Path::new(&[
2433 "zerodds_rpc",
2434 "endpoint_options",
2435 "RpcMapping",
2436 "Enhanced",
2437 ]))],
2438 };
2439 let model = interface::enhanced_service(ast, service, service.mapping.empty_style())?;
2443 if let Ok(objects) = type_information::service_type_objects(ast, service, &model) {
2444 options = rust::Expr::MethodCall {
2445 receiver: Box::new(options),
2446 method: Ident::new("with_type_information"),
2447 args: vec![type_information_expr(&objects)],
2448 };
2449 }
2450 }
2451 Ok(options)
2452}
2453
2454fn type_information_expr(objects: &type_information::ServiceTypeObjects) -> rust::Expr {
2457 let topic = |topic: &type_information::TopicTypeObjects| rust::Expr::StructLit {
2458 path: Path::new(&["zerodds_rpc", "endpoint_options", "TopicTypeBytes"]),
2459 fields: vec![
2460 (
2461 Ident::new("object"),
2462 rust::Expr::Literal(Literal::Bytes(topic.object.clone())),
2463 ),
2464 (
2465 Ident::new("dependencies"),
2466 rust::Expr::Ref {
2467 target: Box::new(rust::Expr::Array(
2468 topic
2469 .dependencies
2470 .iter()
2471 .map(|bytes| rust::Expr::Literal(Literal::Bytes(bytes.clone())))
2472 .collect(),
2473 )),
2474 mutable: false,
2475 },
2476 ),
2477 ],
2478 base: None,
2479 };
2480 rust::Expr::StructLit {
2481 path: Path::new(&["zerodds_rpc", "endpoint_options", "RpcTypeInformation"]),
2482 fields: vec![
2483 (Ident::new("request"), topic(&objects.request)),
2484 (Ident::new("reply"), topic(&objects.reply)),
2485 ],
2486 base: None,
2487 }
2488}
2489
2490fn binding_style_expr() -> rust::Expr {
2491 rust::Expr::Path(Path::new(&[
2492 "zerodds_rpc",
2493 "topic_naming",
2494 "BindingStyle",
2495 "FunctionCall",
2496 ]))
2497}
2498
2499fn client_impl(ast: &Ast, service: &Service) -> Result<rust::Item, RustInterfaceError> {
2500 let mut new_fields = vec![(
2501 Ident::new("requester"),
2502 try_call(
2503 Path::new(&["zerodds_rpc", "requester", "Requester", "with_options"]),
2504 vec![
2505 rust::Expr::Path(Path::single("participant")),
2506 rust::Expr::Literal(Literal::string(&service.qualified_name)),
2507 rust::Expr::Literal(Literal::string("")),
2508 rust::Expr::Path(Path::single("qos")),
2509 binding_style_expr(),
2510 annotated_topics_expr(service),
2511 endpoint_options_expr(ast, service)?,
2512 ],
2513 ),
2514 )];
2515 for base in &service.bases {
2516 let path = base_interface_path(ast, service.module, base, "Client")?;
2517 new_fields.push((
2518 Ident::new(&client_field_name(base)),
2519 try_call(
2520 path.join("new"),
2521 vec![
2522 rust::Expr::Path(Path::single("participant")),
2523 rust::Expr::Path(Path::single("qos")),
2524 ],
2525 ),
2526 ));
2527 }
2528 let new_fn = rust::Item::Function(rust::Function {
2529 doc: Doc::line(
2530 "A client bound to a `DomainParticipant`, one `Requester` per interface level.",
2531 ),
2532 attrs: Vec::new(),
2533 visibility: Visibility::Public,
2534 name: Ident::new("new"),
2535 generics: Vec::new(),
2536 params: vec![
2537 rust::Param {
2538 name: Some(Ident::new("participant")),
2539 type_: dcps_participant_type(),
2540 receiver: None,
2541 },
2542 rust::Param {
2543 name: Some(Ident::new("qos")),
2544 type_: rpc_qos_type(),
2545 receiver: None,
2546 },
2547 ],
2548 returns: Some(rpc_result_of(rust::Type::named(Path::single("Self")))),
2549 body: Some(rust::Block {
2550 statements: vec![rust::Statement::Tail(rust::Expr::Call {
2551 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
2552 args: vec![rust::Expr::StructLit {
2553 path: Path::single("Self"),
2554 fields: new_fields,
2555 base: None,
2556 }],
2557 })],
2558 }),
2559 });
2560
2561 let options_fn = rust::Item::Function(rust::Function {
2562 doc: Doc::line(
2563 "The options this interface's endpoints are created with: the DDS type names of \
2564 the topics, the base interfaces, the service mapping and, under the Enhanced \
2565 mapping, the TypeObjects of the topic types. An endpoint written by hand for \
2566 the same topics, on a participant that also holds this binding, has to use \
2567 them: a topic name is reused only with the same type metadata.",
2568 ),
2569 attrs: Vec::new(),
2570 visibility: Visibility::Public,
2571 name: Ident::new("endpoint_options"),
2572 generics: Vec::new(),
2573 params: Vec::new(),
2574 returns: Some(rust::Type::named(Path::new(&[
2575 "zerodds_rpc",
2576 "endpoint_options",
2577 "RpcEndpointOptions",
2578 ]))),
2579 body: Some(rust::Block {
2580 statements: vec![rust::Statement::Tail(endpoint_options_value(ast, service)?)],
2581 }),
2582 });
2583 let mut items = vec![new_fn, options_fn];
2584 if let Some(request_profile) = service.qos_request_profile.as_deref() {
2585 items.push(declared_qos_fn(
2586 "new_with_declared_qos",
2587 "A client whose QoS is the one `@Qos` declares for this interface (DDS-RPC 1.0 §7.10.1).",
2588 request_profile,
2589 service.qos_reply_profile.as_deref(),
2590 None,
2591 rpc_result_of(rust::Type::named(Path::single("Self"))),
2592 &["Self", "new"],
2593 ));
2594 }
2595 for op in service.all_operations() {
2596 items.push(rust::Item::Function(client_method(ast, service, &op)?));
2597 }
2598
2599 Ok(rust::Item::Impl(rust::Impl {
2600 doc: Doc::none(),
2601 attrs: Vec::new(),
2602 generics: Vec::new(),
2603 trait_: None,
2604 self_type: rust::Type::named(Path::single(&client_type_name(service))),
2605 items,
2606 }))
2607}
2608
2609fn declared_qos_fn(
2613 name: &str,
2614 doc: &str,
2615 request_profile: &str,
2616 reply_profile: Option<&str>,
2617 receiver: Option<rust::Receiver>,
2618 returns: rust::Type,
2619 delegate: &[&str],
2620) -> rust::Item {
2621 let reply = match reply_profile {
2622 Some(url) => rust::Expr::Call {
2623 callee: Box::new(rust::Expr::Path(Path::single("Some"))),
2624 args: vec![rust::Expr::Literal(Literal::string(url))],
2625 },
2626 None => rust::Expr::Path(Path::single("None")),
2627 };
2628 let load_qos = rust::Statement::Let {
2629 name: Ident::new("qos"),
2630 type_: None,
2631 value: Some(try_call(
2632 Path::new(&[
2633 "zerodds_rpc",
2634 "qos_profile",
2635 "RpcQos",
2636 "from_service_profiles",
2637 ]),
2638 vec![rust::Expr::Literal(Literal::string(request_profile)), reply],
2639 )),
2640 mutable: false,
2641 };
2642 let mut call_args = Vec::new();
2643 if receiver.is_some() {
2644 call_args.push(rust::Expr::Path(Path::single("self")));
2645 }
2646 call_args.push(rust::Expr::Path(Path::single("participant")));
2647 call_args.push(rust::Expr::Ref {
2648 target: Box::new(rust::Expr::Path(Path::single("qos"))),
2649 mutable: false,
2650 });
2651 let mut params = Vec::new();
2652 if let Some(receiver) = receiver {
2653 params.push(rust::Param {
2654 name: None,
2655 type_: rust::Type::unit(),
2656 receiver: Some(receiver),
2657 });
2658 }
2659 params.push(rust::Param {
2660 name: Some(Ident::new("participant")),
2661 type_: dcps_participant_type(),
2662 receiver: None,
2663 });
2664 rust::Item::Function(rust::Function {
2665 doc: Doc::line(doc),
2666 attrs: Vec::new(),
2667 visibility: Visibility::Public,
2668 name: Ident::new(name),
2669 generics: Vec::new(),
2670 params,
2671 returns: Some(returns),
2672 body: Some(rust::Block {
2673 statements: vec![
2674 load_qos,
2675 rust::Statement::Tail(rust::Expr::Call {
2676 callee: Box::new(rust::Expr::Path(Path::new(delegate))),
2677 args: call_args,
2678 }),
2679 ],
2680 }),
2681 })
2682}
2683
2684fn requester_field() -> rust::Expr {
2685 field_expr("self", "requester")
2686}
2687
2688fn map_err_remote(service: &Service, value: rust::Expr) -> rust::Expr {
2689 rust::Expr::MethodCall {
2690 receiver: Box::new(value),
2691 method: Ident::new("map_err"),
2692 args: vec![rust::Expr::Path(Path::new(&[
2693 &error_type_name(service),
2694 "Remote",
2695 ]))],
2696 }
2697}
2698
2699fn is_copy_primitive(type_: &rust::Type) -> bool {
2704 matches!(
2705 type_,
2706 rust::Type::Primitive(
2707 rust::Primitive::Bool
2708 | rust::Primitive::Char
2709 | rust::Primitive::I8
2710 | rust::Primitive::I16
2711 | rust::Primitive::I32
2712 | rust::Primitive::I64
2713 | rust::Primitive::I128
2714 | rust::Primitive::U8
2715 | rust::Primitive::U16
2716 | rust::Primitive::U32
2717 | rust::Primitive::U64
2718 | rust::Primitive::U128
2719 | rust::Primitive::F32
2720 | rust::Primitive::F64
2721 )
2722 )
2723}
2724
2725fn in_struct_literal(
2726 ast: &Ast,
2727 module: Id<ModuleNode>,
2728 op: &Operation,
2729 path: Path,
2730 empty: interface::EmptyOperationStyle,
2731) -> Result<rust::Expr, RustInterfaceError> {
2732 let mut fields = Vec::new();
2733 for p in op.inputs() {
2734 let name = rust::escape_keyword(&p.name);
2735 let value = if matches!(p.direction, ParamDirection::InOut) {
2744 let target = rust_type_of(ast, module, Some(p.type_))?;
2745 if is_copy_primitive(&target) {
2746 rust::Expr::Deref(Box::new(rust::Expr::Path(Path::single(&name))))
2747 } else {
2748 rust::Expr::MethodCall {
2749 receiver: Box::new(rust::Expr::Path(Path::single(&name))),
2750 method: Ident::new("clone"),
2751 args: Vec::new(),
2752 }
2753 }
2754 } else {
2755 rust::Expr::Path(Path::single(&name))
2756 };
2757 fields.push((Ident::new(&name), value));
2758 }
2759 if fields.is_empty() && empty == interface::EmptyOperationStyle::DummyMember {
2760 fields.push((
2761 Ident::new(interface::DUMMY_MEMBER),
2762 rust::Expr::Literal(Literal::int(0)),
2763 ));
2764 }
2765 Ok(rust::Expr::StructLit {
2766 path,
2767 fields,
2768 base: None,
2769 })
2770}
2771
2772fn client_method(
2780 ast: &Ast,
2781 service: &Service,
2782 op: &Operation,
2783) -> Result<rust::Function, RustInterfaceError> {
2784 let module = service.module;
2785 let params = op_params(ast, module, op)?;
2786 let returns = op_return_type(ast, module, service, op)?;
2787 let variant = variant_name(&op.name);
2788 let in_struct_path = Path::single(&interface::in_struct_name(&service.name, &op.name));
2789 let call_path = Path::new(&[&interface::call_union_name(&service.name), &variant]);
2790
2791 let build_payload = rust::Statement::Let {
2792 name: Ident::new("request_payload"),
2793 type_: None,
2794 value: Some(in_struct_literal(
2795 ast,
2796 module,
2797 op,
2798 in_struct_path,
2799 service.mapping.empty_style(),
2800 )?),
2801 mutable: false,
2802 };
2803 let build_call = rust::Statement::Let {
2804 name: Ident::new("call"),
2805 type_: None,
2806 value: Some(rust::Expr::Call {
2807 callee: Box::new(rust::Expr::Path(call_path)),
2808 args: vec![rust::Expr::Path(Path::single("request_payload"))],
2809 }),
2810 mutable: false,
2811 };
2812
2813 let send = rust::Expr::MethodCall {
2814 receiver: Box::new(requester_field()),
2815 method: Ident::new("send_request_blocking"),
2816 args: vec![
2817 rust::Expr::Ref {
2818 target: Box::new(rust::Expr::Path(Path::single("call"))),
2819 mutable: false,
2820 },
2821 rust::Expr::Path(Path::single("None")),
2822 ],
2823 };
2824 let reply_let = rust::Statement::Let {
2825 name: Ident::new("reply"),
2826 type_: None,
2827 value: Some(rust::Expr::Try(Box::new(map_err_remote(service, send)))),
2828 mutable: false,
2829 };
2830 let statements = vec![
2831 build_payload,
2832 build_call,
2833 reply_let,
2834 client_reply_match(service, op, &variant)?,
2835 ];
2836
2837 Ok(rust::Function {
2838 doc: Doc::line(&method_doc(op)),
2839 attrs: Vec::new(),
2840 visibility: Visibility::Public,
2841 name: Ident::new(&rust::escape_keyword(&op.name)),
2842 generics: Vec::new(),
2843 params,
2844 returns: Some(returns),
2845 body: Some(rust::Block { statements }),
2846 })
2847}
2848
2849fn client_reply_match(
2850 service: &Service,
2851 op: &Operation,
2852 variant: &str,
2853) -> Result<rust::Statement, RustInterfaceError> {
2854 let map = out_field_map(op);
2855 let positions = ok_positions(op);
2856 let mut assigns = Vec::new();
2857 let mut ok_values = Vec::new();
2858 for (field_name, source) in &map {
2859 match source {
2860 OutFieldSource::InOut(param_name) => {
2861 assigns.push(rust::Statement::Semi(rust::Expr::Assign {
2862 target: Box::new(rust::Expr::Deref(Box::new(rust::Expr::Path(Path::single(
2863 &rust::escape_keyword(param_name),
2864 ))))),
2865 value: Box::new(field_expr("out_val", field_name)),
2866 }));
2867 }
2868 OutFieldSource::Return | OutFieldSource::Out(_) => {
2869 let index = position_index(&positions, source, op)?;
2870 ok_values.push((index, field_expr("out_val", field_name)));
2871 }
2872 }
2873 }
2874 ok_values.sort_by_key(|(index, _)| *index);
2875 let ok_expr = match ok_values.len() {
2876 0 => ok_unit(),
2877 1 => rust::Expr::Call {
2878 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
2879 args: vec![ok_values.remove(0).1],
2880 },
2881 _ => rust::Expr::Call {
2882 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
2883 args: vec![rust::Expr::Tuple(
2884 ok_values.into_iter().map(|(_, expr)| expr).collect(),
2885 )],
2886 },
2887 };
2888 let mut ok_arm_statements = assigns;
2889 ok_arm_statements.push(rust::Statement::Tail(ok_expr));
2890
2891 let result_name = interface::result_union_name(&service.name, &op.name);
2892 let ok_binding = if map.is_empty() {
2898 rust::Pattern::Wildcard
2899 } else {
2900 rust::Pattern::Binding(Ident::new("out_val"))
2901 };
2902 let mut result_arms = vec![rust::MatchArm {
2903 pattern: rust::Pattern::TupleStruct {
2904 path: Path::new(&[&result_name, "Ok"]),
2905 elements: vec![ok_binding],
2906 },
2907 guard: None,
2908 body: rust::Expr::Block(rust::Block {
2909 statements: ok_arm_statements,
2910 }),
2911 }];
2912 for raise in &op.raises {
2913 let raise_variant = rust::escape_keyword(&raise.name);
2914 result_arms.push(rust::MatchArm {
2915 pattern: rust::Pattern::TupleStruct {
2916 path: Path::new(&[&result_name, &raise_variant]),
2917 elements: vec![rust::Pattern::Binding(Ident::new("exc"))],
2918 },
2919 guard: None,
2920 body: rust::Expr::Call {
2921 callee: Box::new(rust::Expr::Path(Path::single("Err"))),
2922 args: vec![rust::Expr::Call {
2923 callee: Box::new(rust::Expr::Path(Path::new(&[
2924 &error_type_name(service),
2925 &raise_variant,
2926 ]))),
2927 args: vec![rust::Expr::Path(Path::single("exc"))],
2928 }],
2929 },
2930 });
2931 }
2932
2933 if service.mapping.is_enhanced() {
2937 result_arms.push(rust::MatchArm {
2938 pattern: rust::Pattern::TupleStruct {
2939 path: Path::new(&[&result_name, UNKNOWN_EX_VARIANT]),
2940 elements: vec![rust::Pattern::Wildcard],
2941 },
2942 guard: None,
2943 body: rust::Expr::Call {
2944 callee: Box::new(rust::Expr::Path(Path::single("Err"))),
2945 args: vec![rust::Expr::Call {
2946 callee: Box::new(rust::Expr::Path(Path::new(&[
2947 &error_type_name(service),
2948 "Remote",
2949 ]))),
2950 args: vec![rust::Expr::Call {
2951 callee: Box::new(rust::Expr::Path(Path::new(&[
2952 "zerodds_rpc",
2953 "error",
2954 "RpcError",
2955 "RemoteException",
2956 ]))),
2957 args: vec![rust::Expr::MethodCall {
2958 receiver: Box::new(rust::Expr::Path(remote_exception_code(
2959 "UnknownException",
2960 ))),
2961 method: Ident::new("as_u32"),
2962 args: Vec::new(),
2963 }],
2964 }],
2965 }],
2966 },
2967 });
2968 }
2969
2970 let return_name = interface::return_union_name(&service.name);
2971 let outer_match = rust::Expr::Match {
2972 scrutinee: Box::new(rust::Expr::Path(Path::single("reply"))),
2973 arms: vec![
2974 rust::MatchArm {
2975 pattern: rust::Pattern::TupleStruct {
2976 path: Path::new(&[&return_name, variant]),
2977 elements: vec![rust::Pattern::Binding(Ident::new("result"))],
2978 },
2979 guard: None,
2980 body: rust::Expr::Match {
2981 scrutinee: Box::new(rust::Expr::Path(Path::single("result"))),
2982 arms: result_arms,
2983 },
2984 },
2985 rust::MatchArm {
2986 pattern: rust::Pattern::Wildcard,
2987 guard: None,
2988 body: rust::Expr::Call {
2989 callee: Box::new(rust::Expr::Path(Path::single("Err"))),
2990 args: vec![rust::Expr::Call {
2991 callee: Box::new(rust::Expr::Path(Path::new(&[
2992 &error_type_name(service),
2993 "Remote",
2994 ]))),
2995 args: vec![rust::Expr::Call {
2996 callee: Box::new(rust::Expr::Path(Path::new(&[
2997 "zerodds_rpc",
2998 "error",
2999 "RpcError",
3000 "Dcps",
3001 ]))),
3002 args: vec![rust::Expr::MethodCall {
3003 receiver: Box::new(rust::Expr::Literal(Literal::string(&format!(
3004 "reply carried a case other than `{}`",
3005 op.name
3006 )))),
3007 method: Ident::new("to_string"),
3008 args: Vec::new(),
3009 }],
3010 }],
3011 }],
3012 },
3013 },
3014 ],
3015 };
3016 Ok(rust::Statement::Tail(outer_match))
3017}
3018
3019fn dispatcher_type_name(service: &Service) -> String {
3022 format!("{}Dispatcher", service.name)
3023}
3024
3025fn remote_exception_code(variant: &str) -> Path {
3026 Path::new(&[
3027 "zerodds_rpc",
3028 "common_types",
3029 "RemoteExceptionCode",
3030 variant,
3031 ])
3032}
3033
3034fn service_bound(service: &Service) -> Vec<rust::Type> {
3035 vec![
3036 rust::Type::named(Path::single(&rust::escape_keyword(&service.name))),
3037 rust::Type::named(Path::single("Send")),
3038 rust::Type::named(Path::single("Sync")),
3039 rust::Type::named(Path::single("'static")),
3040 ]
3041}
3042
3043fn dispatcher_generic() -> rust::GenericParam {
3044 rust::GenericParam {
3046 name: Ident::new("S"),
3047 bounds: Vec::new(),
3048 lifetime: false,
3049 }
3050}
3051
3052fn dispatcher_struct(service: &Service) -> rust::Struct {
3053 let mut generic = dispatcher_generic();
3054 generic.bounds = service_bound(service);
3055 let mut doc =
3056 Doc::line("Serves one service implementation over a `zerodds_rpc::replier::Replier`.");
3057 doc.push(
3058 "A base interface's operations are dispatched by that base's own \
3059 `Dispatcher` and `Replier`, run alongside this one — DDS-RPC 1.0 keeps \
3060 a base's topic separate from a derived interface's own (§7.5.1.1.8), \
3061 and so does this binding.",
3062 );
3063 rust::Struct {
3064 body_text: zerodds_lang_cst::common::BodyText::none(),
3065 doc,
3066 attrs: Vec::new(),
3067 derives: Vec::new(),
3068 visibility: Visibility::Public,
3069 name: Ident::new(&dispatcher_type_name(service)),
3070 generics: vec![generic],
3071 body: rust::StructBody::Named(vec![field_vis(
3072 "service",
3073 rust::Type::named(Path::single("S")),
3074 Visibility::Private,
3075 "The implementation this dispatcher hands each `${I}_Call` to — \
3076 DDS-RPC 1.0 §7.10's Replier, over the `${I}` trait.",
3077 )]),
3078 }
3079}
3080
3081fn dispatcher_out_literal(
3082 service: &Service,
3083 op: &Operation,
3084 qualify: &impl Fn(&str) -> Result<Path, RustInterfaceError>,
3085) -> Result<rust::Expr, RustInterfaceError> {
3086 let map = out_field_map(op);
3087 let out_path = qualify(&interface::out_struct_name(&service.name, &op.name))?;
3088 if map.is_empty() {
3089 let fields = if service.mapping.empty_style() == interface::EmptyOperationStyle::DummyMember
3090 {
3091 vec![(
3092 Ident::new(interface::DUMMY_MEMBER),
3093 rust::Expr::Literal(Literal::int(0)),
3094 )]
3095 } else {
3096 Vec::new()
3097 };
3098 return Ok(rust::Expr::StructLit {
3099 path: out_path,
3100 fields,
3101 base: None,
3102 });
3103 }
3104 let positions = ok_positions(op);
3105 let mut fields = Vec::new();
3106 for (field_name, source) in &map {
3107 let expr = match source {
3108 OutFieldSource::InOut(param_name) => {
3109 rust::Expr::Path(Path::single(&rust::escape_keyword(param_name)))
3110 }
3111 OutFieldSource::Return | OutFieldSource::Out(_) => {
3112 ok_component_expr(&positions, source, "value", op)?
3113 }
3114 };
3115 fields.push((Ident::new(field_name), expr));
3116 }
3117 Ok(rust::Expr::StructLit {
3118 path: out_path,
3119 fields,
3120 base: None,
3121 })
3122}
3123
3124fn in_binding_pattern(op: &Operation) -> rust::Pattern {
3130 let reads_in = op
3131 .params
3132 .iter()
3133 .any(|p| matches!(p.direction, ParamDirection::In | ParamDirection::InOut));
3134 if reads_in {
3135 rust::Pattern::Binding(Ident::new("in_"))
3136 } else {
3137 rust::Pattern::Wildcard
3138 }
3139}
3140
3141fn dispatcher_call_args(op: &Operation) -> Vec<rust::Expr> {
3142 let mut args = Vec::new();
3143 for p in &op.params {
3144 let name = rust::escape_keyword(&p.name);
3145 match p.direction {
3146 ParamDirection::In => args.push(field_expr("in_", &name)),
3147 ParamDirection::InOut => args.push(rust::Expr::Ref {
3148 target: Box::new(rust::Expr::Path(Path::single(&name))),
3149 mutable: true,
3150 }),
3151 ParamDirection::Out => {}
3152 }
3153 }
3154 args
3155}
3156
3157fn dispatcher_inout_locals(op: &Operation) -> Vec<rust::Statement> {
3158 op.params
3159 .iter()
3160 .filter(|p| matches!(p.direction, ParamDirection::InOut))
3161 .map(|p| {
3162 let name = rust::escape_keyword(&p.name);
3163 rust::Statement::Let {
3164 name: Ident::new(&name),
3165 type_: None,
3166 value: Some(field_expr("in_", &name)),
3167 mutable: true,
3168 }
3169 })
3170 .collect()
3171}
3172
3173fn dispatcher_call_arm(
3175 service: &Service,
3176 op: &Operation,
3177) -> Result<rust::MatchArm, RustInterfaceError> {
3178 dispatcher_call_arm_via(service, service, op, &|name| Ok(Path::single(name)))
3179}
3180
3181fn dispatcher_call_arm_via(
3189 dispatching: &Service,
3190 declaring: &Service,
3191 op: &Operation,
3192 qualify: &impl Fn(&str) -> Result<Path, RustInterfaceError>,
3193) -> Result<rust::MatchArm, RustInterfaceError> {
3194 let service = dispatching;
3195 let variant = variant_name(&op.name);
3196 let call_name = interface::call_union_name(&service.name);
3197 let return_name = interface::return_union_name(&service.name);
3198
3199 let mut statements = dispatcher_inout_locals(op);
3200 let call_expr = rust::Expr::MethodCall {
3201 receiver: Box::new(field_expr("self", "service")),
3202 method: Ident::new(&rust::escape_keyword(&op.name)),
3203 args: dispatcher_call_args(op),
3204 };
3205
3206 let ok_pattern = if ok_positions(op).is_empty() {
3207 rust::Pattern::Wildcard
3208 } else {
3209 rust::Pattern::Binding(Ident::new("value"))
3210 };
3211 let result_name = interface::result_union_name(&declaring.name, &op.name);
3212 let ok_arm = rust::MatchArm {
3213 pattern: rust::Pattern::TupleStruct {
3214 path: Path::single("Ok"),
3215 elements: vec![ok_pattern],
3216 },
3217 guard: None,
3218 body: rust::Expr::Call {
3219 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
3220 args: vec![rust::Expr::Call {
3221 callee: Box::new(rust::Expr::Path(Path::new(&[&return_name, &variant]))),
3222 args: vec![rust::Expr::Call {
3223 callee: Box::new(rust::Expr::Path(qualify(&result_name)?.join("Ok"))),
3224 args: vec![dispatcher_out_literal(declaring, op, qualify)?],
3225 }],
3226 }],
3227 },
3228 };
3229 let mut arms = vec![ok_arm];
3230 for raise in &op.raises {
3231 let raise_variant = rust::escape_keyword(&raise.name);
3232 arms.push(rust::MatchArm {
3233 pattern: rust::Pattern::TupleStruct {
3234 path: Path::single("Err"),
3235 elements: vec![rust::Pattern::TupleStruct {
3236 path: qualify(&error_type_name(declaring))?.join(&raise_variant),
3237 elements: vec![rust::Pattern::Binding(Ident::new("exc"))],
3238 }],
3239 },
3240 guard: None,
3241 body: rust::Expr::Call {
3242 callee: Box::new(rust::Expr::Path(Path::single("Ok"))),
3243 args: vec![rust::Expr::Call {
3244 callee: Box::new(rust::Expr::Path(Path::new(&[&return_name, &variant]))),
3245 args: vec![rust::Expr::Call {
3246 callee: Box::new(rust::Expr::Path(
3247 qualify(&result_name)?.join(&raise_variant),
3248 )),
3249 args: vec![rust::Expr::Path(Path::single("exc"))],
3250 }],
3251 }],
3252 },
3253 });
3254 }
3255 arms.push(rust::MatchArm {
3262 pattern: rust::Pattern::Wildcard,
3263 guard: None,
3264 body: rust::Expr::Call {
3265 callee: Box::new(rust::Expr::Path(Path::single("Err"))),
3266 args: vec![rust::Expr::Path(remote_exception_code("UnknownException"))],
3267 },
3268 });
3269 statements.push(rust::Statement::Tail(rust::Expr::Match {
3270 scrutinee: Box::new(call_expr),
3271 arms,
3272 }));
3273
3274 Ok(rust::MatchArm {
3275 pattern: rust::Pattern::TupleStruct {
3276 path: Path::new(&[&call_name, &variant]),
3277 elements: vec![in_binding_pattern(op)],
3278 },
3279 guard: None,
3280 body: rust::Expr::Block(rust::Block { statements }),
3281 })
3282}
3283
3284fn dispatcher_handle_fn(
3285 service: &Service,
3286 ops: &[Operation],
3287) -> Result<rust::Item, RustInterfaceError> {
3288 let call_name = interface::call_union_name(&service.name);
3289 let mut arms: Vec<rust::MatchArm> = ops
3290 .iter()
3291 .map(|op| dispatcher_call_arm(service, op))
3292 .collect::<Result<_, _>>()?;
3293 arms.push(rust::MatchArm {
3294 pattern: rust::Pattern::TupleStruct {
3295 path: Path::new(&[&call_name, UNKNOWN_OP_VARIANT]),
3296 elements: vec![rust::Pattern::Wildcard],
3297 },
3298 guard: None,
3299 body: rust::Expr::Call {
3300 callee: Box::new(rust::Expr::Path(Path::single("Err"))),
3301 args: vec![rust::Expr::Path(remote_exception_code("Unsupported"))],
3302 },
3303 });
3304 Ok(rust::Item::Function(rust::Function {
3305 doc: Doc::none(),
3306 attrs: Vec::new(),
3307 visibility: Visibility::Private,
3308 name: Ident::new("handle"),
3309 generics: Vec::new(),
3310 params: vec![
3311 rust::Param {
3312 name: None,
3313 type_: rust::Type::unit(),
3314 receiver: Some(rust::Receiver::Ref),
3315 },
3316 rust::Param {
3317 name: Some(Ident::new("request")),
3318 type_: rust::Type::named(Path::single(&call_name)),
3319 receiver: None,
3320 },
3321 ],
3322 returns: Some(rust::Type::generic(
3323 Path::single("Result"),
3324 vec![
3325 rust::Type::named(Path::single(&interface::return_union_name(&service.name))),
3326 rust::Type::named(Path::new(&[
3327 "zerodds_rpc",
3328 "common_types",
3329 "RemoteExceptionCode",
3330 ])),
3331 ],
3332 )),
3333 body: Some(rust::Block {
3334 statements: vec![rust::Statement::Tail(rust::Expr::Match {
3335 scrutinee: Box::new(rust::Expr::Path(Path::single("request"))),
3336 arms,
3337 })],
3338 }),
3339 }))
3340}
3341
3342fn dispatcher_impl(ast: &Ast, service: &Service) -> Result<Vec<rust::Item>, RustInterfaceError> {
3343 let _ = ast;
3344 let mut generic = dispatcher_generic();
3345 generic.bounds = service_bound(service);
3346 let self_type = rust::Type::generic(
3347 Path::single(&dispatcher_type_name(service)),
3348 vec![rust::Type::named(Path::single("S"))],
3349 );
3350
3351 let new_fn = rust::Item::Function(rust::Function {
3352 doc: Doc::line("Wraps a service implementation, ready to serve."),
3353 attrs: Vec::new(),
3354 visibility: Visibility::Public,
3355 name: Ident::new("new"),
3356 generics: Vec::new(),
3357 params: vec![rust::Param {
3358 name: Some(Ident::new("service")),
3359 type_: rust::Type::named(Path::single("S")),
3360 receiver: None,
3361 }],
3362 returns: Some(rust::Type::named(Path::single("Self"))),
3363 body: Some(rust::Block {
3364 statements: vec![rust::Statement::Tail(rust::Expr::StructLit {
3365 path: Path::single("Self"),
3366 fields: vec![(
3367 Ident::new("service"),
3368 rust::Expr::Path(Path::single("service")),
3369 )],
3370 base: None,
3371 })],
3372 }),
3373 });
3374
3375 let call_name = interface::call_union_name(&service.name);
3376 let return_name = interface::return_union_name(&service.name);
3377 let serve_fn = rust::Item::Function(rust::Function {
3378 doc: Doc::line(
3379 "Registers a `Replier` for this interface level and starts serving. \
3380 Ticking it — reading requests, calling the service, writing replies \
3381 — is the caller's own event loop, as `zerodds_rpc::replier::Replier` \
3382 always leaves it.",
3383 ),
3384 attrs: Vec::new(),
3385 visibility: Visibility::Public,
3386 name: Ident::new("serve"),
3387 generics: Vec::new(),
3388 params: vec![
3389 rust::Param {
3390 name: None,
3391 type_: rust::Type::unit(),
3392 receiver: Some(rust::Receiver::Value),
3393 },
3394 rust::Param {
3395 name: Some(Ident::new("participant")),
3396 type_: dcps_participant_type(),
3397 receiver: None,
3398 },
3399 rust::Param {
3400 name: Some(Ident::new("qos")),
3401 type_: rpc_qos_type(),
3402 receiver: None,
3403 },
3404 ],
3405 returns: Some(rpc_result_of(rust::Type::generic(
3406 Path::new(&["zerodds_rpc", "replier", "Replier"]),
3407 vec![
3408 rust::Type::named(Path::single(&call_name)),
3409 rust::Type::named(Path::single(&return_name)),
3410 ],
3411 ))),
3412 body: Some(rust::Block {
3413 statements: vec![rust::Statement::Tail(rust::Expr::Call {
3414 callee: Box::new(rust::Expr::Path(Path::new(&[
3415 "zerodds_rpc",
3416 "replier",
3417 "Replier",
3418 "with_options",
3419 ]))),
3420 args: vec![
3421 rust::Expr::Path(Path::single("participant")),
3422 rust::Expr::Literal(Literal::string(&service.qualified_name)),
3423 rust::Expr::Literal(Literal::string("")),
3424 rust::Expr::Path(Path::single("qos")),
3425 binding_style_expr(),
3426 annotated_topics_expr(service),
3427 endpoint_options_expr(ast, service)?,
3428 rust::Expr::Call {
3429 callee: Box::new(rust::Expr::Path(Path::new(&[
3430 "std", "sync", "Arc", "new",
3431 ]))),
3432 args: vec![rust::Expr::Path(Path::single("self"))],
3433 },
3434 ],
3435 })],
3436 }),
3437 });
3438
3439 let inherent = rust::Item::Impl(rust::Impl {
3440 doc: Doc::none(),
3441 attrs: Vec::new(),
3442 generics: vec![generic.clone()],
3443 trait_: None,
3444 self_type: self_type.clone(),
3445 items: {
3446 let mut items = vec![new_fn, serve_fn];
3447 if let Some(request_profile) = service.qos_request_profile.as_deref() {
3448 items.push(declared_qos_fn(
3449 "serve_with_declared_qos",
3450 "Like `serve`, with the QoS `@Qos` declares for this interface (DDS-RPC 1.0 §7.10.1).",
3451 request_profile,
3452 service.qos_reply_profile.as_deref(),
3453 Some(rust::Receiver::Value),
3454 rpc_result_of(rust::Type::generic(
3455 Path::new(&["zerodds_rpc", "replier", "Replier"]),
3456 vec![
3457 rust::Type::named(Path::single(&call_name)),
3458 rust::Type::named(Path::single(&return_name)),
3459 ],
3460 )),
3461 &["Self", "serve"],
3462 ));
3463 }
3464 items
3465 },
3466 });
3467
3468 let ops = service.all_operations();
3469 let handler = rust::Item::Impl(rust::Impl {
3470 doc: Doc::none(),
3471 attrs: Vec::new(),
3472 generics: vec![generic],
3473 trait_: Some(rust::Type::generic(
3474 Path::new(&["zerodds_rpc", "replier", "ReplierHandler"]),
3475 vec![
3476 rust::Type::named(Path::single(&call_name)),
3477 rust::Type::named(Path::single(&return_name)),
3478 ],
3479 )),
3480 self_type,
3481 items: vec![dispatcher_handle_fn(service, &ops)?],
3482 });
3483
3484 Ok(vec![inherent, handler])
3485}