1use std::any::type_name;
6
7use crate::runtime::batch::BatchResult;
8use crate::runtime::batch_publishing::{BatchPublishingCall, BatchPublishingDef};
9use crate::runtime::context::Context;
10use crate::runtime::handler::HandlerOutcome;
11use crate::runtime::metadata::{OutgoingKind, OutgoingMessageMetadata};
12use crate::runtime::publishing::{PublishingCall, PublishingDef};
13use crate::runtime::router::{IncludeDef, forms};
14use crate::{CallerName, FixedName, Name, OutgoingDestination, Unnamed};
15
16use super::Handle;
17use super::axis::{
18 Axis, AxisDocs, Batch, BatchPair, BatchedAxis, Deserialized, Input, Message, Solo, SoloAxis,
19 SoloCarried, SoloDeserialized, SoloPair,
20};
21use super::docs::DocState;
22use super::eager::construct;
23use super::value::{
24 DeclaredDest, EncodedReply, HandleValue, NamedDest, ReplyValue, ResolvedDest, Sealed,
25 SerializedReply,
26};
27use super::verdict::{Batched, OneByOne};
28pub use crate::runtime::publish::Serialized;
31
32#[diagnostic::on_unimplemented(
40 message = "`{Self}` is not a reply value",
41 note = "a reply is a `serde::Serialize` value (the reply codec encodes it), a \
42 `Message<Headers, Payload>` pair of them, or a `#[derive(Serialized)]` type (its \
43 bytes leave as they are)"
44)]
45pub trait ReplyShape: Send + Sync {
46 #[doc(hidden)]
48 type Body: Send + Sync;
49 #[doc(hidden)]
51 type Headers;
52 type Wire;
54}
55
56impl<R: serde::Serialize + Send + Sync> ReplyShape for R {
57 type Body = R;
58 type Headers = ();
59 type Wire = EncodedReply;
60}
61
62impl<H, P> ReplyShape for Message<H, P>
63where
64 H: serde::Serialize + Send + Sync,
65 P: serde::Serialize + Send + Sync,
66{
67 type Body = P;
68 type Headers = H;
69 type Wire = EncodedReply;
70}
71
72#[doc(hidden)]
75pub trait ReplyHeadersSchema<Doc>: ReplyShape {
76 fn headers_schema() -> Option<String>;
77}
78
79impl<R: serde::Serialize + Send + Sync, Doc> ReplyHeadersSchema<Doc> for R {
80 fn headers_schema() -> Option<String> {
81 None
82 }
83}
84
85impl<H, P, Doc> ReplyHeadersSchema<Doc> for Message<H, P>
86where
87 H: serde::Serialize + Send + Sync,
88 P: serde::Serialize + Send + Sync,
89 Doc: DocState<H>,
90{
91 fn headers_schema() -> Option<String> {
92 Doc::schema()
93 }
94}
95
96#[doc(hidden)]
101pub trait WireDocs<R: ReplyShape + ?Sized, Doc> {
102 const SERIALIZED: bool;
104
105 fn payload_schema() -> Option<String>;
107
108 fn headers_schema() -> Option<String>;
110}
111
112impl<R, Doc> WireDocs<R, Doc> for EncodedReply
113where
114 R: ReplyShape + ReplyHeadersSchema<Doc>,
115 Doc: DocState<R::Body>,
116{
117 const SERIALIZED: bool = false;
118
119 fn payload_schema() -> Option<String> {
120 <Doc as DocState<R::Body>>::schema()
121 }
122
123 fn headers_schema() -> Option<String> {
124 <R as ReplyHeadersSchema<Doc>>::headers_schema()
125 }
126}
127
128impl<R: ReplyShape + ?Sized, Doc> WireDocs<R, Doc> for SerializedReply {
129 const SERIALIZED: bool = true;
130
131 fn payload_schema() -> Option<String> {
132 None
133 }
134
135 fn headers_schema() -> Option<String> {
136 None
137 }
138}
139
140#[doc(hidden)]
146pub trait ResolveDestination<Form> {
147 fn resolve(default: &str) -> &str;
149}
150
151impl<R: OutgoingDestination<Form = FixedName>> ResolveDestination<FixedName> for R {
152 fn resolve(_default: &str) -> &str {
153 <R as OutgoingDestination>::DESTINATION
154 }
155}
156
157impl<R: OutgoingDestination<Form = CallerName>> ResolveDestination<CallerName> for R {
158 fn resolve(default: &str) -> &str {
159 default
160 }
161}
162
163#[doc(hidden)]
166#[diagnostic::on_unimplemented(
167 message = "`{Self}` does not declare where it is published",
168 note = "derive `Outgoing` on the reply type: `#[outgoing(name = \"orders.done\")]` fixes the \
169 destination, and a derive without a name is published where the mount site says \
170 (`reply(\"..\")` on the attribute, `.to(\"..\")` on the chain). A name template has \
171 no placeholders to bind on the reply path."
172)]
173pub trait ReplyDestination {
174 fn destination(default: &str) -> &str;
176}
177
178#[diagnostic::do_not_recommend]
181impl<R> ReplyDestination for R
182where
183 R: ReplyShape<Body: OutgoingDestination>,
184 R::Body: ResolveDestination<<R::Body as OutgoingDestination>::Form>,
185{
186 fn destination(default: &str) -> &str {
187 <R::Body as ResolveDestination<<R::Body as OutgoingDestination>::Form>>::resolve(default)
188 }
189}
190
191#[doc(hidden)]
200pub trait DeclaresReply {
201 type Reply;
203
204 type Context;
207}
208
209impl<A, R, O, C, H, Doc, Dest> DeclaresReply
210 for Sealed<ReplyValue<HandleValue<A, R, O, C, H, Doc>, Dest>>
211{
212 type Reply = R;
213 type Context = C;
214}
215
216#[doc(hidden)]
225#[diagnostic::on_unimplemented(
226 message = "`{Self}` declares where it is published, so a transform cannot name its destination",
227 label = "this reply's destination is its type's own",
228 note = "drop `#[outgoing(name = \"..\")]` from the reply type and name the fallback at the \
229 mount site (`reply(\"dest\")` on the attribute, `.to(\"dest\")` on the chain), or \
230 publish the reply where its declaration says and mount a transform that declares \
231 `Destination = Reads`"
232)]
233pub trait RedirectableReply {}
234
235#[diagnostic::do_not_recommend]
238impl<R> RedirectableReply for R where R: ReplyShape<Body: OutgoingDestination<Form = CallerName>> {}
239
240#[doc(hidden)]
244#[must_use]
245pub fn reply_destination<R: ReplyDestination>(default: &'static str) -> ResolvedDest {
246 let name = R::destination(default);
247 ResolvedDest {
250 name,
251 ignored: (name != default).then_some(default),
252 }
253}
254
255#[doc(hidden)]
258#[must_use]
259pub fn declared_reply_destination<R>() -> ResolvedDest
260where
261 R: ReplyShape<Body: OutgoingDestination<Form = FixedName>>,
262{
263 ResolvedDest {
264 name: <R::Body as OutgoingDestination>::DESTINATION,
265 ignored: None,
266 }
267}
268
269#[doc(hidden)]
272pub trait ReplyDest<R>: Send + Sync {
273 fn name(&self) -> &str;
275
276 fn ignored(&self) -> Option<&str> {
279 None
280 }
281}
282
283impl<R> ReplyDest<R> for ResolvedDest {
286 fn name(&self) -> &str {
287 self.name
288 }
289
290 fn ignored(&self) -> Option<&str> {
291 self.ignored
292 }
293}
294
295impl<R: ReplyDestination> ReplyDest<R> for NamedDest {
296 fn name(&self) -> &str {
297 R::destination(&self.0)
298 }
299
300 fn ignored(&self) -> Option<&str> {
303 let site: &str = &self.0;
304 (R::destination(site) != site).then_some(site)
305 }
306}
307
308impl<R> ReplyDest<R> for DeclaredDest
309where
310 R: ReplyShape<Body: OutgoingDestination<Form = FixedName>>,
311{
312 fn name(&self) -> &str {
313 <R::Body as OutgoingDestination>::DESTINATION
314 }
315}
316
317#[doc(hidden)]
321#[diagnostic::on_unimplemented(
322 message = "this reply's wire does not mount on this input family",
323 note = "an encoded reply (`serde::Serialize`) mounts one-by-one and per batch; a \
324 `Serialized` (raw-byte) reply mounts one-by-one only"
325)]
326pub trait ReplyFormFor<Fam> {
327 type Form;
329 type SlotForm;
331}
332
333impl ReplyFormFor<OneByOne> for EncodedReply {
334 type Form = forms::Publishing;
335 type SlotForm = forms::PublishingOut;
336}
337
338impl ReplyFormFor<Batched> for EncodedReply {
339 type Form = forms::BatchPublishing;
340 type SlotForm = forms::BatchPublishingOut;
341}
342
343impl ReplyFormFor<OneByOne> for SerializedReply {
344 type Form = forms::RawReply;
345 type SlotForm = forms::RawReplyOut;
346}
347
348#[doc(hidden)]
352pub trait ReplyRoute<Fam> {
353 type Wire: ReplyFormFor<Fam>;
355 type Form;
357 type SlotForm;
359}
360
361impl<R> ReplyRoute<OneByOne> for R
362where
363 R: ReplyShape,
364 R::Wire: ReplyFormFor<OneByOne>,
365{
366 type Wire = R::Wire;
367 type Form = <R::Wire as ReplyFormFor<OneByOne>>::Form;
368 type SlotForm = <R::Wire as ReplyFormFor<OneByOne>>::SlotForm;
369}
370
371impl<R> ReplyRoute<Batched> for Vec<R>
372where
373 R: ReplyShape,
374 R::Wire: ReplyFormFor<Batched>,
375{
376 type Wire = R::Wire;
377 type Form = <R::Wire as ReplyFormFor<Batched>>::Form;
378 type SlotForm = <R::Wire as ReplyFormFor<Batched>>::SlotForm;
379}
380
381impl<A, R, C, H, Doc, Dest> IncludeDef
382 for Sealed<ReplyValue<HandleValue<A, R, (), C, H, Doc>, Dest>>
383where
384 A: Axis,
385 R: ReplyRoute<A::Family>,
386{
387 type Form = R::Form;
388}
389impl<A, R, C, H, Doc, Dest> PublishingDef
390 for Sealed<ReplyValue<HandleValue<A, R, (), C, H, Doc>, Dest>>
391where
392 A: SoloAxis,
393 R: ReplyShape<Wire: WireDocs<R, Doc>>,
394 C: Send + Sync,
395 H: Send + Sync,
396 Doc: AxisDocs<A> + Send + Sync,
397 Dest: ReplyDest<R>,
398{
399 type Input = A::Kind;
400 type Injections = ();
401 type Reply = R;
402 type Context = C;
403 type Source = Unnamed<Name>;
405
406 fn source(&self) -> Unnamed<Name> {
407 Unnamed::new()
408 }
409
410 fn reply_name(&self) -> &str {
411 self.0.dest.name()
412 }
413
414 fn ignored_reply_name(&self) -> Option<&str> {
415 self.0.dest.ignored()
416 }
417
418 fn description(&self) -> Option<&str> {
419 self.0.value.docs.description()
420 }
421
422 fn input_schema(&self) -> Option<String> {
423 self.0
424 .value
425 .docs
426 .input_schema
427 .clone()
428 .or_else(Doc::payload_schema)
429 }
430
431 fn headers_schema(&self) -> Option<String> {
432 self.0
433 .value
434 .docs
435 .headers_schema
436 .clone()
437 .or_else(Doc::headers_schema)
438 }
439
440 fn message_name(&self) -> Option<&'static str> {
441 self.0.value.docs.message_name
442 }
443
444 fn message_description(&self) -> Option<&'static str> {
445 self.0.value.docs.message_description
446 }
447
448 fn outgoing(&self) -> Vec<OutgoingMessageMetadata> {
449 if let Some(declared) = &self.0.value.docs.outgoing {
450 return declared.clone();
451 }
452 vec![
453 OutgoingMessageMetadata::new(self.reply_name().to_owned(), type_name::<R::Body>())
454 .with_payload_schema(<R::Wire as WireDocs<R, Doc>>::payload_schema())
455 .with_headers_schema(<R::Wire as WireDocs<R, Doc>>::headers_schema())
456 .with_serialized(<R::Wire as WireDocs<R, Doc>>::SERIALIZED)
457 .with_kind(OutgoingKind::Answer),
458 ]
459 }
460}
461
462impl<T, R, C, S, H, Doc, Dest> PublishingCall<S>
463 for Sealed<ReplyValue<HandleValue<Solo<T>, R, (), C, H, Doc>, Dest>>
464where
465 Self: PublishingDef<Input = <Solo<T> as Axis>::Kind, Injections = (), Reply = R, Context = C>,
466 T: Input<Axis = Solo<T>> + Send + Sync + 'static,
467 R: ReplyShape,
468 C: Send + Sync,
469 S: Send + Sync,
470 H: Handle<T, R, (), C, S>,
471{
472 async fn call(
473 &self,
474 input: &T,
475 _injections: &(),
476 ctx: &mut Context<'_, C, S>,
477 ) -> Result<R, HandlerOutcome> {
478 self.0.value.body.handle(input, &(), ctx).await
479 }
480}
481
482impl<T, R, C, S, H, Doc, Dest> PublishingCall<S>
483 for Sealed<ReplyValue<HandleValue<SoloCarried<T>, R, (), C, H, Doc>, Dest>>
484where
485 Self: PublishingDef<
486 Input = <SoloCarried<T> as Axis>::Kind,
487 Injections = (),
488 Reply = R,
489 Context = C,
490 >,
491 T: Input<Axis = SoloCarried<T>> + Clone + Send + Sync + 'static,
492 R: ReplyShape,
493 C: Send + Sync,
494 S: Send + Sync,
495 H: Handle<T, R, (), C, S>,
496{
497 async fn call(
498 &self,
499 input: &T,
500 _injections: &(),
501 ctx: &mut Context<'_, C, S>,
502 ) -> Result<R, HandlerOutcome> {
503 self.0.value.body.handle(input, &(), ctx).await
504 }
505}
506
507impl<F, R, C, S, H, Doc, Dest> PublishingCall<S>
508 for Sealed<ReplyValue<HandleValue<SoloDeserialized<F>, R, (), C, H, Doc>, Dest>>
509where
510 Self: PublishingDef<
511 Input = <SoloDeserialized<F> as Axis>::Kind,
512 Injections = (),
513 Reply = R,
514 Context = C,
515 >,
516 F: Deserialized + Send + Sync + 'static,
517 for<'p> F::Output<'p>: Input<Axis = SoloDeserialized<F>>,
518 R: ReplyShape,
519 C: Send + Sync,
520 S: Send + Sync,
521 H: for<'p> Handle<F::Output<'p>, R, (), C, S>,
522{
523 async fn call(
524 &self,
525 input: &[u8],
526 _injections: &(),
527 ctx: &mut Context<'_, C, S>,
528 ) -> Result<R, HandlerOutcome> {
529 let input = construct::<F, C, S>(input, ctx)?;
530 self.0.value.body.handle(&input, &(), ctx).await
531 }
532}
533
534impl<Hd, P, R, C, S, H, Doc, Dest> PublishingCall<S>
535 for Sealed<ReplyValue<HandleValue<SoloPair<Hd, P>, R, (), C, H, Doc>, Dest>>
536where
537 Self: PublishingDef<
538 Input = <SoloPair<Hd, P> as Axis>::Kind,
539 Injections = (),
540 Reply = R,
541 Context = C,
542 >,
543 Message<Hd, P>: Input<Axis = SoloPair<Hd, P>>,
544 Hd: Send + Sync + 'static,
545 P: Send + Sync + 'static,
546 R: ReplyShape,
547 C: Send + Sync,
548 S: Send + Sync,
549 H: Handle<Message<Hd, P>, R, (), C, S>,
550{
551 async fn call(
552 &self,
553 input: &Message<Hd, P>,
554 _injections: &(),
555 ctx: &mut Context<'_, C, S>,
556 ) -> Result<R, HandlerOutcome> {
557 self.0.value.body.handle(input, &(), ctx).await
558 }
559}
560impl<A, R, C, H, Doc, Dest> BatchPublishingDef
561 for Sealed<ReplyValue<HandleValue<A, Vec<R>, (), C, H, Doc>, Dest>>
562where
563 A: BatchedAxis,
564 R: ReplyShape<Wire: WireDocs<R, Doc>>,
565 C: Send + Sync,
566 H: Send + Sync,
567 Doc: AxisDocs<A> + Send + Sync,
568 Dest: ReplyDest<R>,
569{
570 type Input = A::Kind;
571 type Injections = ();
572 type Context = C;
573 type Reply = R;
574 type Source = Unnamed<Name>;
575
576 fn source(&self) -> Unnamed<Name> {
577 Unnamed::new()
578 }
579
580 fn reply_name(&self) -> &str {
581 self.0.dest.name()
582 }
583
584 fn ignored_reply_name(&self) -> Option<&str> {
585 self.0.dest.ignored()
586 }
587
588 fn description(&self) -> Option<&str> {
589 self.0.value.docs.description()
590 }
591
592 fn input_schema(&self) -> Option<String> {
593 self.0
594 .value
595 .docs
596 .input_schema
597 .clone()
598 .or_else(Doc::payload_schema)
599 }
600
601 fn headers_schema(&self) -> Option<String> {
602 self.0
603 .value
604 .docs
605 .headers_schema
606 .clone()
607 .or_else(Doc::headers_schema)
608 }
609
610 fn message_name(&self) -> Option<&'static str> {
611 self.0.value.docs.message_name
612 }
613
614 fn message_description(&self) -> Option<&'static str> {
615 self.0.value.docs.message_description
616 }
617
618 fn outgoing(&self) -> Vec<OutgoingMessageMetadata> {
619 if let Some(declared) = &self.0.value.docs.outgoing {
620 return declared.clone();
621 }
622 vec![
623 OutgoingMessageMetadata::new(self.reply_name().to_owned(), type_name::<R::Body>())
624 .with_payload_schema(<R::Wire as WireDocs<R, Doc>>::payload_schema())
625 .with_headers_schema(<R::Wire as WireDocs<R, Doc>>::headers_schema())
626 .with_serialized(<R::Wire as WireDocs<R, Doc>>::SERIALIZED)
627 .with_kind(OutgoingKind::Answer),
628 ]
629 }
630}
631
632pub(super) fn batch_reply_verdict<R>(
634 verdict: Result<Vec<R>, Vec<HandlerOutcome>>,
635 batch_len: usize,
636 subscription: &str,
637) -> Result<Vec<R>, BatchResult> {
638 match verdict {
639 Ok(replies) => {
640 assert!(
641 replies.len() == batch_len,
642 "subscriber '{subscription}' returned {} replies for a batch of {batch_len}",
643 replies.len(),
644 );
645 Ok(replies)
646 }
647 Err(outcomes) => {
648 assert!(
649 outcomes.len() == batch_len,
650 "subscriber '{subscription}' returned {} per-element outcomes for a batch of \
651 {batch_len}",
652 outcomes.len(),
653 );
654 Err(BatchResult::PerElement(outcomes))
655 }
656 }
657}
658
659impl<T, R, C, S, H, Doc, Dest> BatchPublishingCall<S>
660 for Sealed<ReplyValue<HandleValue<Batch<T>, Vec<R>, (), C, H, Doc>, Dest>>
661where
662 Self: BatchPublishingDef<
663 Input = <Batch<T> as Axis>::Kind,
664 Injections = (),
665 Context = C,
666 Reply = R,
667 >,
668 [T]: Input<Axis = Batch<T>>,
669 T: Send + Sync + 'static,
670 R: ReplyShape,
671 C: Send + Sync,
672 S: Send + Sync,
673 H: Handle<[T], Vec<R>, (), C, S>,
674{
675 async fn call(
676 &self,
677 batch: &[T],
678 _injections: &(),
679 ctx: &mut Context<'_, C, S>,
680 ) -> Result<Vec<R>, BatchResult> {
681 let verdict = self.0.value.body.handle(batch, &(), ctx).await;
682 batch_reply_verdict(verdict, batch.len(), ctx.name())
683 }
684}
685
686impl<Hd, P, R, C, S, H, Doc, Dest> BatchPublishingCall<S>
687 for Sealed<ReplyValue<HandleValue<BatchPair<Hd, P>, Vec<R>, (), C, H, Doc>, Dest>>
688where
689 Self: BatchPublishingDef<
690 Input = <BatchPair<Hd, P> as Axis>::Kind,
691 Injections = (),
692 Context = C,
693 Reply = R,
694 >,
695 [Message<Hd, P>]: Input<Axis = BatchPair<Hd, P>>,
696 Hd: Send + Sync + 'static,
697 P: Send + Sync + 'static,
698 R: ReplyShape,
699 C: Send + Sync,
700 S: Send + Sync,
701 H: Handle<[Message<Hd, P>], Vec<R>, (), C, S>,
702{
703 async fn call(
704 &self,
705 batch: &[Message<Hd, P>],
706 _injections: &(),
707 ctx: &mut Context<'_, C, S>,
708 ) -> Result<Vec<R>, BatchResult> {
709 let verdict = self.0.value.body.handle(batch, &(), ctx).await;
710 batch_reply_verdict(verdict, batch.len(), ctx.name())
711 }
712}