xmtp_api_d14n/protocol/traits/
envelope_collection.rs

1//! Traits and blanket implementations representing a collection of [`Envelope`]'s
2use xmtp_common::{MaybeSend, MaybeSync};
3use xmtp_proto::types::{Cursor, OrphanedEnvelope};
4
5use super::*;
6
7// TODO: Maybe we can do something with Iterators and Extractors
8// to avoid the Combinators like `CollectionExtractor`?
9/// A Generic Higher-Level Collection of Envelopes
10pub trait EnvelopeCollection<'env>: MaybeSend + MaybeSync {
11    /// Get the topic for an envelope
12    fn topics(&self) -> Result<Vec<Topic>, EnvelopeError>;
13    /// get the cursors for each envelope
14    fn cursors(&self) -> Result<Vec<Cursor>, EnvelopeError>;
15    /// Get the payload for an envelope
16    fn payloads(&self) -> Result<Vec<Payload>, EnvelopeError>;
17    /// get orphans for these envelopes
18    fn orphans(&self) -> Result<Vec<OrphanedEnvelope>, EnvelopeError>;
19    /// get the data field for each envelope as a sha256 hash
20    fn sha256_hashes(&self) -> Result<Vec<Vec<u8>>, EnvelopeError>;
21    /// Build the ClientEnvelope
22    fn client_envelopes(&self) -> Result<Vec<ClientEnvelope>, EnvelopeError>;
23    /// Try to get a group message from this Envelope
24    fn group_messages(&self) -> Result<Vec<Option<GroupMessage>>, EnvelopeError>;
25    /// Try to get a welcome message
26    fn welcome_messages(&self) -> Result<Vec<Option<WelcomeMessage>>, EnvelopeError>;
27    /// Length of the Collection
28    fn len(&self) -> usize;
29    /// Whether the Collection of Envelopes is empty
30    fn is_empty(&self) -> bool;
31    /// run a sequenced extraction on the envelopes in this collection
32    fn consume<E>(self) -> Result<Vec<<E as Extractor>::Output>, EnvelopeError>
33    where
34        for<'a> E: Default + Extractor + EnvelopeVisitor<'a>,
35        for<'a> EnvelopeError: From<<E as EnvelopeVisitor<'a>>::Error>,
36        Self: Sized;
37}
38
39impl<'env, T> EnvelopeCollection<'env> for Vec<T>
40where
41    T: ProtocolEnvelope<'env> + std::fmt::Debug + MaybeSend + MaybeSync,
42{
43    fn topics(&self) -> Result<Vec<Topic>, EnvelopeError> {
44        self.iter()
45            .map(|t| t.topic())
46            .collect::<Result<Vec<Topic>, _>>()
47    }
48
49    fn cursors(&self) -> Result<Vec<Cursor>, EnvelopeError> {
50        self.iter()
51            .map(|t| t.cursor())
52            .collect::<Result<Vec<Cursor>, _>>()
53    }
54
55    fn orphans(&self) -> Result<Vec<OrphanedEnvelope>, EnvelopeError> {
56        self.iter().map(|t| t.orphan()).collect::<Result<_, _>>()
57    }
58
59    fn payloads(&self) -> Result<Vec<Payload>, EnvelopeError> {
60        self.iter()
61            .map(|t| t.payload())
62            .collect::<Result<Vec<Payload>, _>>()
63    }
64
65    fn sha256_hashes(&self) -> Result<Vec<Vec<u8>>, EnvelopeError> {
66        self.iter()
67            .map(|t| t.sha256_hash())
68            .collect::<Result<Vec<Vec<u8>>, _>>()
69    }
70
71    fn client_envelopes(&self) -> Result<Vec<ClientEnvelope>, EnvelopeError> {
72        self.iter()
73            .map(|t| t.client_envelope())
74            .collect::<Result<Vec<ClientEnvelope>, EnvelopeError>>()
75    }
76
77    fn group_messages(&self) -> Result<Vec<Option<GroupMessage>>, EnvelopeError> {
78        self.iter()
79            .map(|t| t.group_message())
80            .collect::<Result<Vec<Option<GroupMessage>>, EnvelopeError>>()
81    }
82
83    fn welcome_messages(&self) -> Result<Vec<Option<WelcomeMessage>>, EnvelopeError> {
84        self.iter()
85            .map(|t| t.welcome_message())
86            .collect::<Result<Vec<Option<WelcomeMessage>>, EnvelopeError>>()
87    }
88
89    fn len(&self) -> usize {
90        Vec::len(self)
91    }
92
93    fn is_empty(&self) -> bool {
94        Vec::is_empty(self)
95    }
96
97    fn consume<E>(self) -> Result<Vec<<E as Extractor>::Output>, EnvelopeError>
98    where
99        for<'a> E: Default + Extractor + EnvelopeVisitor<'a>,
100        for<'a> EnvelopeError: From<<E as EnvelopeVisitor<'a>>::Error>,
101        Self: Sized,
102    {
103        SequencedExtractor::builder()
104            .envelopes(self)
105            .build::<E>()
106            .get()
107    }
108}
109
110/// Extension trait for an envelope collection which handles errors.
111pub trait TryEnvelopeCollectionExt<'env>: EnvelopeCollection<'env> {
112    /// run a sequenced extraction on the envelopes in this collection.
113    /// Flattens and returns errors into one Result<_, E>
114    #[allow(clippy::type_complexity)]
115    fn try_consume<E>(
116        self,
117    ) -> Result<
118        (
119            Vec<<E as TryExtractor>::Ok>,
120            Vec<<E as TryExtractor>::Error>,
121        ),
122        EnvelopeError,
123    >
124    where
125        for<'a> E: TryExtractor,
126        for<'a> E: Default + EnvelopeVisitor<'a>,
127        for<'a> EnvelopeError: From<<E as EnvelopeVisitor<'a>>::Error>,
128        EnvelopeError: From<<E as TryExtractor>::Error>,
129        Self: Sized,
130    {
131        let (success, failure): (Vec<_>, Vec<_>) =
132            self.consume::<E>()?.into_iter().partition_result();
133        Ok((success, failure))
134    }
135}
136
137impl<'env, T> TryEnvelopeCollectionExt<'env> for T where T: EnvelopeCollection<'env> {}