xmtp_api_d14n/protocol/traits/
dependency_resolution.rs1use std::{collections::HashSet, marker::PhantomData};
2
3use derive_builder::UninitializedFieldError;
4use xmtp_common::{MaybeSend, MaybeSync, RetryableError};
5use xmtp_proto::api::BodyError;
6
7use crate::protocol::{CursorStoreError, Envelope, EnvelopeError, types::RequiredDependency};
8
9pub struct Resolved<E> {
10 pub resolved: Vec<E>,
11 pub unresolved: Option<HashSet<RequiredDependency>>,
13}
14
15impl<E> Resolved<E> {
16 pub fn new(envelopes: Vec<E>, unresolved: Option<HashSet<RequiredDependency>>) -> Self {
17 Self {
18 resolved: envelopes,
19 unresolved,
20 }
21 }
22}
23
24#[xmtp_common::async_trait]
25pub trait ResolveDependencies: MaybeSend + MaybeSync {
26 type ResolvedEnvelope: Envelope<'static> + MaybeSend + MaybeSync;
27 async fn resolve(
33 &self,
34 missing: HashSet<RequiredDependency>,
35 ) -> Result<Resolved<Self::ResolvedEnvelope>, ResolutionError>;
36}
37
38#[xmtp_common::async_trait]
39impl<T> ResolveDependencies for &T
40where
41 T: ResolveDependencies,
42{
43 type ResolvedEnvelope = T::ResolvedEnvelope;
44 async fn resolve(
45 &self,
46 missing: HashSet<RequiredDependency>,
47 ) -> Result<Resolved<Self::ResolvedEnvelope>, ResolutionError> {
48 <T as ResolveDependencies>::resolve(*self, missing).await
49 }
50}
51
52pub struct NoopResolver;
54
55#[xmtp_common::async_trait]
56impl ResolveDependencies for NoopResolver {
57 type ResolvedEnvelope = ();
58 async fn resolve(
59 &self,
60 m: HashSet<RequiredDependency>,
61 ) -> Result<Resolved<()>, ResolutionError> {
62 Ok(Resolved {
63 resolved: vec![],
64 unresolved: Some(m),
65 })
66 }
67}
68
69#[derive(Clone, Copy, Default)]
70pub struct TypedNoopResolver<T> {
71 _marker: PhantomData<T>,
72}
73
74impl<T> TypedNoopResolver<T> {
75 pub fn new() -> Self {
76 Self {
77 _marker: PhantomData,
78 }
79 }
80}
81
82#[xmtp_common::async_trait]
83impl<T> ResolveDependencies for TypedNoopResolver<T>
84where
85 T: Envelope<'static>,
86{
87 type ResolvedEnvelope = T;
88 async fn resolve(
89 &self,
90 m: HashSet<RequiredDependency>,
91 ) -> Result<Resolved<T>, ResolutionError> {
92 Ok(Resolved {
93 resolved: Vec::<T>::new(),
94 unresolved: Some(m),
95 })
96 }
97}
98
99#[derive(thiserror::Error, Debug)]
100pub enum ResolutionError {
101 #[error(transparent)]
102 Envelope(#[from] EnvelopeError),
103 #[error(transparent)]
104 Body(#[from] BodyError),
105 #[error("{0}")]
106 Api(Box<dyn RetryableError>),
107 #[error(transparent)]
108 Build(#[from] UninitializedFieldError),
109 #[error("Resolution failed to find all missing dependant envelopes")]
110 ResolutionFailed,
111 #[error(transparent)]
112 Store(#[from] CursorStoreError),
113}
114
115impl RetryableError for ResolutionError {
116 fn is_retryable(&self) -> bool {
117 use ResolutionError::*;
118 match self {
119 Envelope(e) => e.is_retryable(),
120 Body(b) => b.is_retryable(),
121 Api(a) => a.is_retryable(),
122 Build(_) => false,
123 ResolutionFailed => false,
124 Store(s) => s.is_retryable(),
125 }
126 }
127}
128
129impl ResolutionError {
130 pub fn api<E: RetryableError + 'static>(e: E) -> Self {
131 ResolutionError::Api(Box::new(e))
132 }
133}