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 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 (`publish(\"..\")` 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 (`publish(\"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)]
243#[must_use]
244pub fn reply_destination<R: ReplyDestination>(default: &'static str) -> &'static str {
245 R::destination(default)
246}
247
248#[doc(hidden)]
251#[must_use]
252pub fn declared_reply_destination<R>() -> &'static str
253where
254 R: ReplyShape<Body: OutgoingDestination<Form = FixedName>>,
255{
256 <R::Body as OutgoingDestination>::DESTINATION
257}
258
259#[doc(hidden)]
262pub trait ReplyDest<R>: Send + Sync {
263 fn name(&self) -> &str;
265}
266
267impl<R> ReplyDest<R> for ResolvedDest {
270 fn name(&self) -> &str {
271 self.0
272 }
273}
274
275impl<R: ReplyDestination> ReplyDest<R> for NamedDest {
276 fn name(&self) -> &str {
277 R::destination(&self.0)
278 }
279}
280
281impl<R> ReplyDest<R> for DeclaredDest
282where
283 R: ReplyShape<Body: OutgoingDestination<Form = FixedName>>,
284{
285 fn name(&self) -> &str {
286 <R::Body as OutgoingDestination>::DESTINATION
287 }
288}
289
290#[doc(hidden)]
294#[diagnostic::on_unimplemented(
295 message = "this reply's wire does not mount on this input family",
296 note = "an encoded reply (`serde::Serialize`) mounts one-by-one and per batch; a \
297 `Serialized` (raw-byte) reply mounts one-by-one only"
298)]
299pub trait ReplyFormFor<Fam> {
300 type Form;
302 type SlotForm;
304}
305
306impl ReplyFormFor<OneByOne> for EncodedReply {
307 type Form = forms::Publishing;
308 type SlotForm = forms::PublishingOut;
309}
310
311impl ReplyFormFor<Batched> for EncodedReply {
312 type Form = forms::BatchPublishing;
313 type SlotForm = forms::BatchPublishingOut;
314}
315
316impl ReplyFormFor<OneByOne> for SerializedReply {
317 type Form = forms::RawReply;
318 type SlotForm = forms::RawReplyOut;
319}
320
321#[doc(hidden)]
325pub trait ReplyRoute<Fam> {
326 type Wire: ReplyFormFor<Fam>;
328 type Form;
330 type SlotForm;
332}
333
334impl<R> ReplyRoute<OneByOne> for R
335where
336 R: ReplyShape,
337 R::Wire: ReplyFormFor<OneByOne>,
338{
339 type Wire = R::Wire;
340 type Form = <R::Wire as ReplyFormFor<OneByOne>>::Form;
341 type SlotForm = <R::Wire as ReplyFormFor<OneByOne>>::SlotForm;
342}
343
344impl<R> ReplyRoute<Batched> for Vec<R>
345where
346 R: ReplyShape,
347 R::Wire: ReplyFormFor<Batched>,
348{
349 type Wire = R::Wire;
350 type Form = <R::Wire as ReplyFormFor<Batched>>::Form;
351 type SlotForm = <R::Wire as ReplyFormFor<Batched>>::SlotForm;
352}
353
354impl<A, R, C, H, Doc, Dest> IncludeDef
355 for Sealed<ReplyValue<HandleValue<A, R, (), C, H, Doc>, Dest>>
356where
357 A: Axis,
358 R: ReplyRoute<A::Family>,
359{
360 type Form = R::Form;
361}
362impl<A, R, C, H, Doc, Dest> PublishingDef
363 for Sealed<ReplyValue<HandleValue<A, R, (), C, H, Doc>, Dest>>
364where
365 A: SoloAxis,
366 R: ReplyShape<Wire: WireDocs<R, Doc>>,
367 C: Send + Sync,
368 H: Send + Sync,
369 Doc: AxisDocs<A> + Send + Sync,
370 Dest: ReplyDest<R>,
371{
372 type Input = A::Kind;
373 type Injections = ();
374 type Reply = R;
375 type Context = C;
376 type Source = Unnamed<Name>;
378
379 fn source(&self) -> Unnamed<Name> {
380 Unnamed::new()
381 }
382
383 fn reply_name(&self) -> &str {
384 self.0.dest.name()
385 }
386
387 fn description(&self) -> Option<&str> {
388 self.0.value.docs.description()
389 }
390
391 fn input_schema(&self) -> Option<String> {
392 self.0
393 .value
394 .docs
395 .input_schema
396 .clone()
397 .or_else(Doc::payload_schema)
398 }
399
400 fn headers_schema(&self) -> Option<String> {
401 self.0
402 .value
403 .docs
404 .headers_schema
405 .clone()
406 .or_else(Doc::headers_schema)
407 }
408
409 fn message_name(&self) -> Option<&'static str> {
410 self.0.value.docs.message_name
411 }
412
413 fn message_description(&self) -> Option<&'static str> {
414 self.0.value.docs.message_description
415 }
416
417 fn outgoing(&self) -> Vec<OutgoingMessageMetadata> {
418 if let Some(declared) = &self.0.value.docs.outgoing {
419 return declared.clone();
420 }
421 vec![
422 OutgoingMessageMetadata::new(self.reply_name().to_owned(), type_name::<R::Body>())
423 .with_payload_schema(<R::Wire as WireDocs<R, Doc>>::payload_schema())
424 .with_headers_schema(<R::Wire as WireDocs<R, Doc>>::headers_schema())
425 .with_serialized(<R::Wire as WireDocs<R, Doc>>::SERIALIZED)
426 .with_kind(OutgoingKind::Answer),
427 ]
428 }
429}
430
431impl<T, R, C, S, H, Doc, Dest> PublishingCall<S>
432 for Sealed<ReplyValue<HandleValue<Solo<T>, R, (), C, H, Doc>, Dest>>
433where
434 Self: PublishingDef<Input = <Solo<T> as Axis>::Kind, Injections = (), Reply = R, Context = C>,
435 T: Input<Axis = Solo<T>> + Send + Sync + 'static,
436 R: ReplyShape,
437 C: Send + Sync,
438 S: Send + Sync,
439 H: Handle<T, R, (), C, S>,
440{
441 async fn call(
442 &self,
443 input: &T,
444 _injections: &(),
445 ctx: &mut Context<'_, C, S>,
446 ) -> Result<R, HandlerOutcome> {
447 self.0.value.body.handle(input, &(), ctx).await
448 }
449}
450
451impl<F, R, C, S, H, Doc, Dest> PublishingCall<S>
452 for Sealed<ReplyValue<HandleValue<SoloDeserialized<F>, R, (), C, H, Doc>, Dest>>
453where
454 Self: PublishingDef<
455 Input = <SoloDeserialized<F> as Axis>::Kind,
456 Injections = (),
457 Reply = R,
458 Context = C,
459 >,
460 F: Deserialized + Send + Sync + 'static,
461 for<'p> F::Output<'p>: Input<Axis = SoloDeserialized<F>>,
462 R: ReplyShape,
463 C: Send + Sync,
464 S: Send + Sync,
465 H: for<'p> Handle<F::Output<'p>, R, (), C, S>,
466{
467 async fn call(
468 &self,
469 input: &[u8],
470 _injections: &(),
471 ctx: &mut Context<'_, C, S>,
472 ) -> Result<R, HandlerOutcome> {
473 let input = construct::<F, C, S>(input, ctx)?;
474 self.0.value.body.handle(&input, &(), ctx).await
475 }
476}
477
478impl<Hd, P, R, C, S, H, Doc, Dest> PublishingCall<S>
479 for Sealed<ReplyValue<HandleValue<SoloPair<Hd, P>, R, (), C, H, Doc>, Dest>>
480where
481 Self: PublishingDef<
482 Input = <SoloPair<Hd, P> as Axis>::Kind,
483 Injections = (),
484 Reply = R,
485 Context = C,
486 >,
487 Message<Hd, P>: Input<Axis = SoloPair<Hd, P>>,
488 Hd: Send + Sync + 'static,
489 P: Send + Sync + 'static,
490 R: ReplyShape,
491 C: Send + Sync,
492 S: Send + Sync,
493 H: Handle<Message<Hd, P>, R, (), C, S>,
494{
495 async fn call(
496 &self,
497 input: &Message<Hd, P>,
498 _injections: &(),
499 ctx: &mut Context<'_, C, S>,
500 ) -> Result<R, HandlerOutcome> {
501 self.0.value.body.handle(input, &(), ctx).await
502 }
503}
504impl<A, R, C, H, Doc, Dest> BatchPublishingDef
505 for Sealed<ReplyValue<HandleValue<A, Vec<R>, (), C, H, Doc>, Dest>>
506where
507 A: BatchedAxis,
508 R: ReplyShape<Wire: WireDocs<R, Doc>>,
509 C: Send + Sync,
510 H: Send + Sync,
511 Doc: AxisDocs<A> + Send + Sync,
512 Dest: ReplyDest<R>,
513{
514 type Input = A::Kind;
515 type Injections = ();
516 type Context = C;
517 type Reply = R;
518 type Source = Unnamed<Name>;
519
520 fn source(&self) -> Unnamed<Name> {
521 Unnamed::new()
522 }
523
524 fn reply_name(&self) -> &str {
525 self.0.dest.name()
526 }
527
528 fn description(&self) -> Option<&str> {
529 self.0.value.docs.description()
530 }
531
532 fn input_schema(&self) -> Option<String> {
533 self.0
534 .value
535 .docs
536 .input_schema
537 .clone()
538 .or_else(Doc::payload_schema)
539 }
540
541 fn headers_schema(&self) -> Option<String> {
542 self.0
543 .value
544 .docs
545 .headers_schema
546 .clone()
547 .or_else(Doc::headers_schema)
548 }
549
550 fn message_name(&self) -> Option<&'static str> {
551 self.0.value.docs.message_name
552 }
553
554 fn message_description(&self) -> Option<&'static str> {
555 self.0.value.docs.message_description
556 }
557
558 fn outgoing(&self) -> Vec<OutgoingMessageMetadata> {
559 if let Some(declared) = &self.0.value.docs.outgoing {
560 return declared.clone();
561 }
562 vec![
563 OutgoingMessageMetadata::new(self.reply_name().to_owned(), type_name::<R::Body>())
564 .with_payload_schema(<R::Wire as WireDocs<R, Doc>>::payload_schema())
565 .with_headers_schema(<R::Wire as WireDocs<R, Doc>>::headers_schema())
566 .with_serialized(<R::Wire as WireDocs<R, Doc>>::SERIALIZED)
567 .with_kind(OutgoingKind::Answer),
568 ]
569 }
570}
571
572pub(super) fn batch_reply_verdict<R>(
574 verdict: Result<Vec<R>, Vec<HandlerOutcome>>,
575 batch_len: usize,
576 subscription: &str,
577) -> Result<Vec<R>, BatchResult> {
578 match verdict {
579 Ok(replies) => {
580 assert!(
581 replies.len() == batch_len,
582 "subscriber '{subscription}' returned {} replies for a batch of {batch_len}",
583 replies.len(),
584 );
585 Ok(replies)
586 }
587 Err(outcomes) => {
588 assert!(
589 outcomes.len() == batch_len,
590 "subscriber '{subscription}' returned {} per-element outcomes for a batch of \
591 {batch_len}",
592 outcomes.len(),
593 );
594 Err(BatchResult::PerElement(outcomes))
595 }
596 }
597}
598
599impl<T, R, C, S, H, Doc, Dest> BatchPublishingCall<S>
600 for Sealed<ReplyValue<HandleValue<Batch<T>, Vec<R>, (), C, H, Doc>, Dest>>
601where
602 Self: BatchPublishingDef<
603 Input = <Batch<T> as Axis>::Kind,
604 Injections = (),
605 Context = C,
606 Reply = R,
607 >,
608 [T]: Input<Axis = Batch<T>>,
609 T: Send + Sync + 'static,
610 R: ReplyShape,
611 C: Send + Sync,
612 S: Send + Sync,
613 H: Handle<[T], Vec<R>, (), C, S>,
614{
615 async fn call(
616 &self,
617 batch: &[T],
618 _injections: &(),
619 ctx: &mut Context<'_, C, S>,
620 ) -> Result<Vec<R>, BatchResult> {
621 let verdict = self.0.value.body.handle(batch, &(), ctx).await;
622 batch_reply_verdict(verdict, batch.len(), ctx.name())
623 }
624}
625
626impl<Hd, P, R, C, S, H, Doc, Dest> BatchPublishingCall<S>
627 for Sealed<ReplyValue<HandleValue<BatchPair<Hd, P>, Vec<R>, (), C, H, Doc>, Dest>>
628where
629 Self: BatchPublishingDef<
630 Input = <BatchPair<Hd, P> as Axis>::Kind,
631 Injections = (),
632 Context = C,
633 Reply = R,
634 >,
635 [Message<Hd, P>]: Input<Axis = BatchPair<Hd, P>>,
636 Hd: Send + Sync + 'static,
637 P: Send + Sync + 'static,
638 R: ReplyShape,
639 C: Send + Sync,
640 S: Send + Sync,
641 H: Handle<[Message<Hd, P>], Vec<R>, (), C, S>,
642{
643 async fn call(
644 &self,
645 batch: &[Message<Hd, P>],
646 _injections: &(),
647 ctx: &mut Context<'_, C, S>,
648 ) -> Result<Vec<R>, BatchResult> {
649 let verdict = self.0.value.body.handle(batch, &(), ctx).await;
650 batch_reply_verdict(verdict, batch.len(), ctx.name())
651 }
652}