1mod calls;
57mod enhanced;
58mod enhanced_calls;
59
60use crate::adapter::interface::{
61 self, Attribute, BindingError, Operation, Parameter, RETURN_MEMBER, Service, ServiceMapping,
62};
63use crate::adapter::java::java_member_type;
64use zerodds_ast::Ast;
65use zerodds_lang_cst::common::{Doc, Ident, Path};
66use zerodds_lang_cst::java;
67
68pub const OUTER: &str = "ZdServices";
76
77const RPC: &str = "org.omg.dds.rpc";
80const RPC_IMPL: &str = "org.zerodds.ddsrpc";
83
84pub fn java_interface_items(ast: &Ast) -> Result<Vec<java::TypeDecl>, BindingError> {
90 java_interface_items_with(ast, ServiceMapping::Basic)
91}
92
93pub fn java_interface_items_with(
104 ast: &Ast,
105 mapping: ServiceMapping,
106) -> Result<Vec<java::TypeDecl>, BindingError> {
107 let mut pending: Vec<(Vec<String>, java::Member)> = Vec::new();
108 let mut declared_hashes: Vec<(Vec<String>, String)> = Vec::new();
111 for service in interface::services(ast)? {
112 let chain: Vec<String> = interface::module_chain(ast, service.module)?
113 .into_iter()
114 .map(|(_, name)| java::escape_keyword(&name))
115 .collect();
116 let members = if mapping.is_enhanced() {
117 enhanced::members(ast, &Service { mapping, ..service })?
118 } else {
119 members_of(ast, &service, &chain, &mut declared_hashes)?
120 };
121 for member in members {
122 pending.push((chain.clone(), member));
123 }
124 }
125 pending.extend(
127 crate::emit::type_pair_java::java_type_pair_members(ast).map_err(BindingError::Codec)?,
128 );
129 if pending.is_empty() {
130 return Ok(Vec::new());
131 }
132 Ok(vec![java::TypeDecl::Class(java::Class {
133 doc: Doc::lines(&[
134 "The Java shape of every service one IDL file declares.",
135 "",
136 "DDS-RPC 1.0 §7.11.2. Each IDL module nests as a class, exactly as",
137 "the data types nest inside `ZdTypes`.",
138 ]),
139 annotations: Vec::new(),
140 modifiers: vec![java::Modifier::Public, java::Modifier::Final],
141 name: Ident::new(OUTER),
142 generics: Vec::new(),
143 extends: None,
144 implements: Vec::new(),
145 members: nest(&pending, 0),
146 })])
147}
148
149fn nest(items: &[(Vec<String>, java::Member)], depth: usize) -> Vec<java::Member> {
155 let mut out = Vec::new();
156 let mut index = 0;
157 while index < items.len() {
158 let Some((chain, member)) = items.get(index) else {
159 break;
160 };
161 let Some(name) = chain.get(depth) else {
162 out.push(member.clone());
163 index += 1;
164 continue;
165 };
166 let mut end = index;
167 while items
168 .get(end)
169 .is_some_and(|(other, _)| other.get(depth) == Some(name))
170 {
171 end += 1;
172 }
173 let Some(run) = items.get(index..end) else {
174 break;
175 };
176 out.push(java::Member::Nested(Box::new(java::TypeDecl::Class(
177 java::Class {
178 doc: Doc::line("An IDL module, as the class its services nest in."),
179 annotations: Vec::new(),
180 modifiers: vec![
181 java::Modifier::Public,
182 java::Modifier::Static,
183 java::Modifier::Final,
184 ],
185 name: Ident::new(name),
186 generics: Vec::new(),
187 extends: None,
188 implements: Vec::new(),
189 members: nest(run, depth + 1),
190 },
191 ))));
192 index = end;
193 }
194 out
195}
196
197fn members_of(
198 ast: &Ast,
199 service: &Service,
200 chain: &[String],
201 declared_hashes: &mut Vec<(Vec<String>, String)>,
202) -> Result<Vec<java::Member>, BindingError> {
203 let name = java::escape_keyword(&service.name);
204 let operations = service.all_operations();
205 let mut out = Vec::new();
206
207 for operation in &operations {
211 out.push(holder(
212 &interface::in_struct_name(&name, &java::escape_keyword(&operation.name)),
213 request_fields(ast, service, operation)?,
214 Doc::line("The `in` and `inout` parameters of one call. DDS-RPC 1.0 §7.5.1.1.4."),
215 ));
216 out.push(holder(
217 &interface::out_struct_name(&name, &java::escape_keyword(&operation.name)),
218 reply_fields(ast, service, operation)?,
219 Doc::line(
220 "The return value and the `out` and `inout` parameters of one call. DDS-RPC 1.0 \
221 §7.5.1.1.5.",
222 ),
223 ));
224 }
225 for operation in &operations {
228 let spelled = java::escape_keyword(&operation.name);
229 out.push(hash_constant(
230 &interface::operation_hash_constant(&name, &spelled),
231 interface::operation_hash(&operation.name),
232 "The case label of one operation. DDS-RPC 1.0 §7.5.1.1.6 rules 4 and 5a.",
233 ));
234 }
235 for operation in &operations {
236 for raised in &operation.raises {
237 let key = (chain.to_vec(), raised.scoped_name.clone());
238 if declared_hashes.contains(&key) {
239 continue;
240 }
241 declared_hashes.push(key);
242 out.push(hash_constant(
243 &interface::exception_hash_constant(&java::escape_keyword(&raised.name)),
244 interface::exception_hash(&raised.scoped_name),
245 "The case label of one raised exception. DDS-RPC 1.0 §7.5.1.1.5 rule 4.",
246 ));
247 }
248 }
249 for operation in &operations {
250 out.push(result_union_of(ast, &name, operation)?);
251 }
252 out.push(union_of(
253 &interface::call_union_name(&name),
254 &operations,
255 &name,
256 false,
257 ));
258 out.push(union_of(
259 &interface::return_union_name(&name),
260 &operations,
261 &name,
262 true,
263 ));
264 out.push(holder(
265 &interface::request_type_name(&name),
266 vec![
267 field(qualified(RPC, "RequestHeader"), "header"),
268 field(
269 local(&interface::call_union_name(&name)),
270 interface::DATA_MEMBER,
271 ),
272 ],
273 Doc::line("The request topic type. DDS-RPC 1.0 §7.5.1.1.6."),
274 ));
275 out.push(holder(
276 &interface::reply_type_name(&name),
277 vec![
278 field(qualified(RPC, "ReplyHeader"), "header"),
279 field(
280 local(&interface::return_union_name(&name)),
281 interface::DATA_MEMBER,
282 ),
283 ],
284 Doc::line("The reply topic type. DDS-RPC 1.0 §7.5.1.1.7."),
285 ));
286 let scope: Vec<String> = interface::module_chain(ast, service.module)?
290 .into_iter()
291 .map(|(_, module)| module)
292 .collect();
293 out.push(support_class(service, &name, &scope));
294
295 out.push(sync_interface(ast, service, &name)?);
296 out.push(async_interface(ast, service, &name)?);
297
298 if service.is_remote() {
299 out.extend(calls::members(ast, service, &name)?);
302 out.push(endpoint(
303 &format!("{name}Requester"),
304 "Requester",
305 &name,
306 "The service's `Requester`, over its two topic types. DDS-RPC 1.0 §7.11.2.2.1.",
307 ));
308 out.push(endpoint(
309 &format!("{name}Replier"),
310 "Replier",
311 &name,
312 "The service's `Replier`, over its two topic types. DDS-RPC 1.0 §7.11.2.2.1.",
313 ));
314 out.push(client_interface(&name));
315 out.push(service_interface(&name));
316 }
317 Ok(out)
318}
319
320fn request_fields(
323 ast: &Ast,
324 service: &Service,
325 operation: &Operation,
326) -> Result<Vec<java::Member>, BindingError> {
327 let mut out = Vec::new();
328 for param in operation.inputs() {
329 out.push(field(
330 java_member_type(ast, param.type_, &at(service, operation, ¶m.name))?,
331 &java::escape_keyword(¶m.name),
332 ));
333 }
334 if out.is_empty() {
338 out.push(field(
339 qualified(RPC, "UnusedMember"),
340 interface::DUMMY_MEMBER,
341 ));
342 }
343 Ok(out)
344}
345
346fn reply_fields(
347 ast: &Ast,
348 service: &Service,
349 operation: &Operation,
350) -> Result<Vec<java::Member>, BindingError> {
351 let mut out = Vec::new();
355 for param in operation.outputs() {
356 out.push(field(
357 java_member_type(ast, param.type_, &at(service, operation, ¶m.name))?,
358 &java::escape_keyword(¶m.name),
359 ));
360 }
361 if let Some(returns) = operation.returns {
362 let taken: Vec<String> = operation
363 .outputs()
364 .map(|param| java::escape_keyword(¶m.name))
365 .collect();
366 out.push(field(
367 java_member_type(ast, returns, &at(service, operation, "return"))?,
368 &return_member_name(&taken),
369 ));
370 }
371 if out.is_empty() {
373 out.push(field(
374 qualified(RPC, "UnusedMember"),
375 interface::DUMMY_MEMBER,
376 ));
377 }
378 Ok(out)
379}
380
381fn return_member_name(taken: &[String]) -> String {
386 if !taken.iter().any(|name| name == RETURN_MEMBER) {
387 return RETURN_MEMBER.to_string();
388 }
389 for n in 1..=i32::MAX {
390 let candidate = format!("{RETURN_MEMBER}{n}");
391 if !taken.iter().any(|name| name == &candidate) {
392 return candidate;
393 }
394 }
395 format!("{RETURN_MEMBER} ")
398}
399
400fn union_of(name: &str, operations: &[Operation], service: &str, reply: bool) -> java::Member {
401 let mut members = vec![
402 discriminator_field(),
403 case_field(
405 qualified(RPC, "UnknownOperation"),
406 interface::UNKNOWN_OP_MEMBER,
407 "Live on the `default` case. DDS-RPC 1.0 §7.5.1.1.6 rule 3.",
408 ),
409 ];
410 for operation in operations {
411 let spelled = java::escape_keyword(&operation.name);
412 let case = if reply {
413 interface::result_union_name(service, &spelled)
414 } else {
415 interface::in_struct_name(service, &spelled)
416 };
417 members.push(case_field(
418 local(&case),
419 &spelled,
420 &format!(
421 "Live when `_d` is `{}`.",
422 interface::operation_hash_constant(service, &spelled)
423 ),
424 ));
425 }
426 holder(
427 name,
428 members,
429 Doc::line(if reply {
430 "The reply union, without the `oneway` operations. DDS-RPC 1.0 §7.5.1.1.7."
431 } else {
432 "The request union over every operation. DDS-RPC 1.0 §7.5.1.1.6."
433 }),
434 )
435}
436
437fn discriminator_field() -> java::Member {
439 java::Member::Field(java::Field {
440 doc: Doc::line("Which case this sample carries. DDS-RPC 1.0 §7.5.1.1.6 rule 2."),
441 annotations: Vec::new(),
442 modifiers: vec![java::Modifier::Public],
443 type_: java::Type::Primitive(java::Primitive::Int),
444 name: Ident::new("_d"),
445 value: None,
446 })
447}
448
449fn case_field(type_: java::Type, name: &str, doc: &str) -> java::Member {
451 java::Member::Field(java::Field {
452 doc: Doc::line(doc),
453 annotations: Vec::new(),
454 modifiers: vec![java::Modifier::Public],
455 type_,
456 name: Ident::new(name),
457 value: None,
458 })
459}
460
461fn hash_constant(name: &str, value: i32, doc: &str) -> java::Member {
464 java::Member::Field(java::Field {
465 doc: Doc::line(doc),
466 annotations: Vec::new(),
467 modifiers: vec![
468 java::Modifier::Public,
469 java::Modifier::Static,
470 java::Modifier::Final,
471 ],
472 type_: java::Type::Primitive(java::Primitive::Int),
473 name: Ident::new(name),
474 value: Some(java::Expr::Literal(zerodds_lang_cst::common::Literal::Int(
475 value.to_string(),
476 ))),
477 })
478}
479
480fn result_union_of(
482 ast: &Ast,
483 name: &str,
484 operation: &Operation,
485) -> Result<java::Member, BindingError> {
486 let spelled = java::escape_keyword(&operation.name);
487 let mut members = vec![
488 discriminator_field(),
489 case_field(
490 local(&interface::out_struct_name(name, &spelled)),
491 interface::RESULT_MEMBER,
492 "Live when `_d` is `dds::RETCODE_OK` (0). DDS-RPC 1.0 §7.5.1.1.5 rule 3a.",
493 ),
494 ];
495 members.push(case_field(
497 qualified(RPC, "UnknownException"),
498 "unknownEx",
499 "Live on the `default` case. DDS-RPC 1.0 §7.5.1.1.5.",
500 ));
501 for raised in &operation.raises {
502 let exception = java::escape_keyword(&raised.name);
503 members.push(case_field(
504 exception_type(ast, raised)?,
505 &interface::exception_member_name(&exception),
506 &format!(
507 "Live when `_d` is `{}`. DDS-RPC 1.0 §7.5.1.1.5 rule 5.",
508 interface::exception_hash_constant(&exception)
509 ),
510 ));
511 }
512 Ok(holder(
513 &interface::result_union_name(name, &spelled),
514 members,
515 Doc::line("The outcome of one operation. DDS-RPC 1.0 §7.5.1.1.5."),
516 ))
517}
518
519fn exception_type(ast: &Ast, raised: &interface::Raise) -> Result<java::Type, BindingError> {
522 let node = ast
523 .exception(raised.id)
524 .map_err(|_| interface::InterfaceError::NoNode {
525 kind: "exception",
526 at: raised.name.clone(),
527 })?;
528 calls::data_type(ast, node.module, &raised.name)
529}
530
531fn support_class(service: &Service, name: &str, scope: &[String]) -> java::Member {
534 support_class_with(service, name, scope, true, None)
535}
536
537fn support_class_with(
540 service: &Service,
541 name: &str,
542 scope: &[String],
543 factories: bool,
544 descriptions: Option<&(
545 crate::emit::interface_rust::type_information::JavaDescription,
546 crate::emit::interface_rust::type_information::JavaDescription,
547 )>,
548) -> java::Member {
549 let style = interface::BindingStyle::FunctionCall;
550 let mut members = vec![
551 topic_constant("REQUEST_TOPIC_NAME", &service.request_topic_name(style)),
552 topic_constant("REPLY_TOPIC_NAME", &service.reply_topic_name(style)),
553 topic_constant("SERVICE_NAME", &service.service_name),
554 topic_constant("INTERFACE_NAME", &service.qualified_name),
558 topic_constant(
559 "REQUEST_TYPE_NAME",
560 &scoped(scope, &interface::request_type_name(name)),
561 ),
562 topic_constant(
563 "REPLY_TYPE_NAME",
564 &scoped(scope, &interface::reply_type_name(name)),
565 ),
566 ];
567 for (at, alias) in service.topic_aliases(style, false).into_iter().enumerate() {
569 members.push(topic_constant(&format!("REQUEST_TOPIC_ALIAS_{at}"), &alias));
570 }
571 for (at, alias) in service.topic_aliases(style, true).into_iter().enumerate() {
572 members.push(topic_constant(&format!("REPLY_TOPIC_ALIAS_{at}"), &alias));
573 }
574 if factories {
575 members.extend(calls::support_methods(service, name, descriptions));
576 }
577 holder(
578 &format!("{name}Support"),
579 members,
580 Doc::line("The service's type support: the names of its two topics and its two types."),
581 )
582}
583
584fn scoped(scope: &[String], name: &str) -> String {
586 let mut out = String::new();
587 for module in scope {
588 out.push_str(module);
589 out.push_str("::");
590 }
591 out.push_str(name);
592 out
593}
594
595fn topic_constant(name: &str, topic: &str) -> java::Member {
596 java::Member::Field(java::Field {
597 doc: Doc::line("A topic name. DDS-RPC 1.0 §7.4.1.1."),
598 annotations: Vec::new(),
599 modifiers: vec![
600 java::Modifier::Public,
601 java::Modifier::Static,
602 java::Modifier::Final,
603 ],
604 type_: java::Type::named(Path::single("String")),
605 name: Ident::new(name),
606 value: Some(java::Expr::Literal(zerodds_lang_cst::common::Literal::Str(
607 topic.to_string(),
608 ))),
609 })
610}
611
612fn sync_interface(ast: &Ast, service: &Service, name: &str) -> Result<java::Member, BindingError> {
615 let mut members = Vec::new();
616 for operation in &service.operations {
617 members.push(java::Member::Method(sync_method(
618 ast, service, name, operation,
619 )?));
620 }
621 for attribute in &service.attributes {
622 members.extend(sync_accessors(ast, service, attribute)?);
623 }
624 Ok(java::Member::Nested(Box::new(java::TypeDecl::Interface(
625 java::Interface {
626 doc: Doc::line(
627 "The service, as the interface DDS-RPC 1.0 §7.11.1.1.2 rule 1 requires and \
628 §7.11.2.3 carries into Java.",
629 ),
630 annotations: Vec::new(),
631 modifiers: vec![java::Modifier::Public],
632 name: Ident::new(name),
633 generics: Vec::new(),
634 extends: base_types(ast, service, "")?,
635 members,
636 },
637 ))))
638}
639
640fn sync_method(
641 ast: &Ast,
642 service: &Service,
643 name: &str,
644 operation: &Operation,
645) -> Result<java::Method, BindingError> {
646 let returns = if operation.has_outputs() {
649 local(&interface::out_struct_name(
650 name,
651 &java::escape_keyword(&operation.name),
652 ))
653 } else if let Some(target) = operation.returns {
654 java_member_type(ast, target, &at(service, operation, "return"))?
655 } else {
656 java::Type::Void
657 };
658 let mut params = Vec::new();
659 for param in operation.inputs() {
660 params.push(java_param(ast, service, operation, param)?);
661 }
662 Ok(method(
663 &java::escape_keyword(&operation.name),
664 returns,
665 params,
666 raises_doc(operation),
667 ))
668}
669
670fn sync_accessors(
671 ast: &Ast,
672 service: &Service,
673 attribute: &Attribute,
674) -> Result<Vec<java::Member>, BindingError> {
675 let at = format!("{}::{}", service.name, attribute.name);
676 let type_ = java_member_type(ast, attribute.type_, &at)?;
677 let spelled = java::escape_keyword(&attribute.name);
678 let mut out = vec![java::Member::Method(method(
679 &spelled,
680 type_.clone(),
681 Vec::new(),
682 Doc::line("An attribute accessor. DDS-RPC 1.0 §7.11.1.1.2 rule 6.f."),
683 ))];
684 if !attribute.readonly {
685 out.push(java::Member::Method(method(
686 &spelled,
687 java::Type::Void,
688 vec![java::Param {
689 annotations: Vec::new(),
690 final_: false,
691 type_,
692 name: Ident::new(&spelled),
693 varargs: false,
694 }],
695 Doc::line("An attribute accessor. DDS-RPC 1.0 §7.11.1.1.2 rule 6.f."),
696 )));
697 }
698 Ok(out)
699}
700
701fn async_interface(ast: &Ast, service: &Service, name: &str) -> Result<java::Member, BindingError> {
702 let mut members = Vec::new();
703 for operation in &service.operations {
704 members.push(java::Member::Method(async_method(
705 ast, service, name, operation,
706 )?));
707 }
708 for attribute in &service.attributes {
709 let at = format!("{}::{}", service.name, attribute.name);
710 let type_ = java_member_type(ast, attribute.type_, &at)?;
711 let spelled = java::escape_keyword(&attribute.name);
712 members.push(java::Member::Method(method(
713 &format!("{spelled}_async"),
714 future_of(boxed(type_.clone())),
715 Vec::new(),
716 Doc::line("An attribute accessor. DDS-RPC 1.0 §7.11.1.1.2 rule 7.f."),
717 )));
718 if !attribute.readonly {
719 members.push(java::Member::Method(method(
720 &format!("{spelled}_async"),
721 future_of(java::Type::named(Path::single("Void"))),
722 vec![java::Param {
723 annotations: Vec::new(),
724 final_: false,
725 type_,
726 name: Ident::new(&spelled),
727 varargs: false,
728 }],
729 Doc::line("An attribute accessor. DDS-RPC 1.0 §7.11.1.1.2 rule 7.g."),
730 )));
731 }
732 }
733 Ok(java::Member::Nested(Box::new(java::TypeDecl::Interface(
734 java::Interface {
735 doc: Doc::line("The service's asynchronous half. DDS-RPC 1.0 §7.11.1.1.2 rule 1."),
736 annotations: Vec::new(),
737 modifiers: vec![java::Modifier::Public],
738 name: Ident::new(&format!("{name}Async")),
739 generics: Vec::new(),
740 extends: base_types(ast, service, "Async")?,
741 members,
742 },
743 ))))
744}
745
746fn async_method(
747 ast: &Ast,
748 service: &Service,
749 name: &str,
750 operation: &Operation,
751) -> Result<java::Method, BindingError> {
752 let payload = if operation.has_outputs() {
755 local(&interface::out_struct_name(
756 name,
757 &java::escape_keyword(&operation.name),
758 ))
759 } else if let Some(target) = operation.returns {
760 boxed(java_member_type(
761 ast,
762 target,
763 &at(service, operation, "return"),
764 )?)
765 } else {
766 java::Type::named(Path::single("Void"))
767 };
768 let mut params = Vec::new();
769 for param in operation.inputs() {
770 params.push(java_param(ast, service, operation, param)?);
771 }
772 Ok(method(
773 &format!("{}_async", java::escape_keyword(&operation.name)),
774 future_of(payload),
775 params,
776 raises_doc(operation),
777 ))
778}
779
780fn endpoint(name: &str, template: &str, service: &str, doc: &str) -> java::Member {
793 let params = java::Type::named(path(RPC, &format!("{template}Params")));
796 java::Member::Nested(Box::new(java::TypeDecl::Class(java::Class {
797 doc: Doc::line(doc),
798 annotations: Vec::new(),
799 modifiers: vec![java::Modifier::Public, java::Modifier::Static],
800 name: Ident::new(name),
801 generics: Vec::new(),
802 extends: Some(java::Type::generic(
803 path(RPC_IMPL, &format!("{template}Impl")),
804 vec![
805 local(&interface::request_type_name(service)),
806 local(&interface::reply_type_name(service)),
807 ],
808 )),
809 implements: Vec::new(),
810 members: vec![java::Member::Constructor(java::Constructor {
811 doc: Doc::line("The generic endpoint, over this service's two topic types."),
812 annotations: Vec::new(),
813 modifiers: vec![java::Modifier::Public],
814 name: Ident::new(name),
815 params: vec![java::Param {
816 annotations: Vec::new(),
817 final_: false,
818 type_: params,
819 name: Ident::new("params"),
820 varargs: false,
821 }],
822 throws: Vec::new(),
823 body: java::Block {
824 statements: vec![java::Statement::Expr(java::Expr::MethodCall {
825 receiver: None,
826 name: Ident::new("super"),
827 args: vec![java::Expr::Path(Path::single("params"))],
828 })],
829 },
830 })],
831 })))
832}
833
834fn client_interface(name: &str) -> java::Member {
840 java::Member::Nested(Box::new(java::TypeDecl::Interface(java::Interface {
841 doc: Doc::line("The function-call client. DDS-RPC 1.0 §7.11.1.5.4, §7.11.2.3."),
842 annotations: Vec::new(),
843 modifiers: vec![java::Modifier::Public],
844 name: Ident::new(&format!("{name}Client")),
845 generics: Vec::new(),
846 extends: vec![
847 local(name),
848 local(&format!("{name}Async")),
849 java::Type::named(path(RPC, "ClientEndpoint")),
850 ],
851 members: Vec::new(),
852 })))
853}
854
855fn service_interface(name: &str) -> java::Member {
857 java::Member::Nested(Box::new(java::TypeDecl::Interface(java::Interface {
858 doc: Doc::line("The function-call service. DDS-RPC 1.0 §7.11.1.5.1, §7.11.2.3."),
859 annotations: Vec::new(),
860 modifiers: vec![java::Modifier::Public],
861 name: Ident::new(&format!("{name}Service")),
862 generics: Vec::new(),
863 extends: vec![java::Type::named(path(RPC, "ServiceEndpoint"))],
864 members: vec![java::Member::Method(method(
865 "implementation",
866 local(name),
867 Vec::new(),
868 Doc::line("The implementation this service dispatches into."),
869 ))],
870 })))
871}
872
873fn java_param(
876 ast: &Ast,
877 service: &Service,
878 operation: &Operation,
879 param: &Parameter,
880) -> Result<java::Param, BindingError> {
881 Ok(java::Param {
882 annotations: Vec::new(),
883 final_: false,
884 type_: java_member_type(ast, param.type_, &at(service, operation, ¶m.name))?,
885 name: Ident::new(&java::escape_keyword(¶m.name)),
886 varargs: false,
887 })
888}
889
890fn method(name: &str, returns: java::Type, params: Vec<java::Param>, doc: Doc) -> java::Method {
891 java::Method {
892 doc,
893 annotations: Vec::new(),
894 modifiers: Vec::new(),
897 generics: Vec::new(),
898 returns,
899 name: Ident::new(name),
900 params,
901 throws: Vec::new(),
903 body: None,
904 }
905}
906
907fn future_of(payload: java::Type) -> java::Type {
909 java::Type::generic(path(RPC, "Future"), vec![payload])
910}
911
912fn boxed(type_: java::Type) -> java::Type {
914 match type_ {
915 java::Type::Primitive(primitive) => java::Type::named(Path::single(primitive.boxed())),
916 java::Type::Void => java::Type::named(Path::single("Void")),
917 other => other,
918 }
919}
920
921fn base_types(ast: &Ast, service: &Service, suffix: &str) -> Result<Vec<java::Type>, BindingError> {
924 service
925 .bases
926 .iter()
927 .map(|base| {
928 let module = interface::service(ast, base.id)?.module;
929 enhanced::holder_type(
930 ast,
931 service.module,
932 module,
933 &format!("{}{suffix}", java::escape_keyword(&base.name)),
934 )
935 })
936 .collect()
937}
938
939fn holder(name: &str, members: Vec<java::Member>, doc: Doc) -> java::Member {
940 java::Member::Nested(Box::new(java::TypeDecl::Class(java::Class {
941 doc,
942 annotations: Vec::new(),
943 modifiers: vec![
944 java::Modifier::Public,
945 java::Modifier::Static,
946 java::Modifier::Final,
947 ],
948 name: Ident::new(name),
949 generics: Vec::new(),
950 extends: None,
951 implements: Vec::new(),
952 members,
953 })))
954}
955
956fn field(type_: java::Type, name: &str) -> java::Member {
957 java::Member::Field(java::Field {
958 doc: Doc::none(),
959 annotations: Vec::new(),
960 modifiers: vec![java::Modifier::Public],
961 type_,
962 name: Ident::new(name),
963 value: None,
964 })
965}
966
967fn local(name: &str) -> java::Type {
968 java::Type::named(Path::single(name))
969}
970
971fn path(package: &str, name: &str) -> Path {
972 let mut segments: Vec<&str> = package.split('.').collect();
973 segments.push(name);
974 Path::new(&segments)
975}
976
977fn qualified(package: &str, name: &str) -> java::Type {
978 java::Type::named(path(package, name))
979}
980
981fn raises_doc(operation: &Operation) -> Doc {
984 operation
985 .raises_line()
986 .map_or_else(Doc::none, |line| Doc::line(&line))
987}
988
989fn at(service: &Service, operation: &Operation, what: &str) -> String {
991 format!("{}::{}({what})", service.name, operation.name)
992}