depo_api/request/
get_shares.rs1use std::collections::{HashMap, HashSet};
2
3use bc_envelope::prelude::*;
4use gstp::prelude::*;
5
6use crate::{
7 Error, GET_SHARES_FUNCTION, RECEIPT_PARAM, RECEIPT_PARAM_NAME, Result,
8 receipt::Receipt,
9 util::{Abbrev, FlankedFunction},
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct GetShares(HashSet<Receipt>);
18
19impl GetShares {
20 pub fn new<I, T>(iterable: I) -> Self
21 where
22 I: IntoIterator<Item = T>,
23 T: Clone + Into<Receipt>,
24 {
25 Self(
26 iterable
27 .into_iter()
28 .map(|item| item.clone().into())
29 .collect(),
30 )
31 }
32
33 pub fn new_all_shares() -> Self { Self(HashSet::new()) }
34
35 pub fn receipts(&self) -> &HashSet<Receipt> { &self.0 }
36}
37
38impl From<GetShares> for Expression {
39 fn from(value: GetShares) -> Self {
40 let mut expression = Expression::new(GET_SHARES_FUNCTION);
41 for receipt in value.0.into_iter() {
42 expression = expression.with_parameter(RECEIPT_PARAM, receipt);
43 }
44 expression
45 }
46}
47
48impl TryFrom<Expression> for GetShares {
49 type Error = Error;
50
51 fn try_from(expression: Expression) -> Result<Self> {
52 let receipts = expression
53 .objects_for_parameter(RECEIPT_PARAM)
54 .into_iter()
55 .map(|parameter| {
56 parameter.try_into().map_err(|e| Error::InvalidParameter {
57 parameter: RECEIPT_PARAM_NAME.to_string(),
58 message: format!("failed to convert to Receipt: {}", e),
59 })
60 })
61 .collect::<Result<HashSet<Receipt>>>()?;
62 Ok(Self(receipts))
63 }
64}
65
66impl std::fmt::Display for GetShares {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 f.write_fmt(format_args!(
69 "{} {}",
70 "getShares".flanked_function(),
71 self.receipts().abbrev()
72 ))
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct GetSharesResult(HashMap<Receipt, ByteString>);
82
83impl GetSharesResult {
84 pub fn new(receipt_to_data: HashMap<Receipt, ByteString>) -> Self {
85 Self(receipt_to_data)
86 }
87
88 pub fn receipt_to_data(&self) -> &HashMap<Receipt, ByteString> { &self.0 }
89
90 pub fn data_for_receipt(&self, receipt: &Receipt) -> Option<&ByteString> {
91 self.0.get(receipt)
92 }
93}
94
95impl From<GetSharesResult> for Envelope {
96 fn from(value: GetSharesResult) -> Self {
97 let mut result = known_values::OK_VALUE.to_envelope();
98 for (receipt, data) in value.0 {
99 result = result.add_assertion(receipt, data);
100 }
101 result
102 }
103}
104
105impl TryFrom<Envelope> for GetSharesResult {
106 type Error = Error;
107
108 fn try_from(envelope: Envelope) -> Result<Self> {
109 let mut receipt_to_data = HashMap::new();
110 for assertion in envelope.assertions() {
111 let receipt =
112 Receipt::try_from(assertion.try_predicate().map_err(|e| {
113 Error::InvalidEnvelope {
114 message: format!(
115 "failed to extract assertion predicate: {}",
116 e
117 ),
118 }
119 })?)
120 .map_err(|e| Error::InvalidParameter {
121 parameter: RECEIPT_PARAM_NAME.to_string(),
122 message: format!("invalid receipt in assertion: {}", e),
123 })?;
124 let object =
125 assertion.try_object().map_err(|e| Error::InvalidEnvelope {
126 message: format!(
127 "failed to extract assertion object: {}",
128 e
129 ),
130 })?;
131 let data = ByteString::try_from(object).map_err(|e| {
132 Error::InvalidEnvelope {
133 message: format!(
134 "failed to convert object to ByteString: {}",
135 e
136 ),
137 }
138 })?;
139 receipt_to_data.insert(receipt, data);
140 }
141 Ok(Self::new(receipt_to_data))
142 }
143}
144
145impl TryFrom<SealedResponse> for GetSharesResult {
146 type Error = Error;
147
148 fn try_from(response: SealedResponse) -> Result<Self> {
149 response.result()?.clone().try_into()
150 }
151}
152
153impl std::fmt::Display for GetSharesResult {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 let abbrevable: HashMap<Receipt, ByteString> = self
156 .receipt_to_data()
157 .iter()
158 .map(|(k, v)| (k.clone(), v.clone()))
159 .collect();
160 f.write_fmt(format_args!(
161 "{} OK {}",
162 "getShares".flanked_function(),
163 abbrevable.abbrev()
164 ))
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use bc_components::XID;
171 use indoc::indoc;
172
173 use super::*;
174
175 fn user_id() -> XID {
176 XID::from_data_ref(hex_literal::hex!(
177 "8712dfac3d0ebfa910736b2a9ee39d4b68f64222a77bcc0074f3f5f1c9216d30"
178 ))
179 .unwrap()
180 }
181
182 fn data_1() -> ByteString { b"data_1".to_vec().into() }
183
184 fn receipt_1() -> Receipt { Receipt::new(user_id(), data_1()) }
185
186 fn data_2() -> ByteString { b"data_2".to_vec().into() }
187
188 fn receipt_2() -> Receipt { Receipt::new(user_id(), data_2()) }
189
190 #[test]
191 fn test_request() {
192 bc_envelope::register_tags();
193
194 let receipts = vec![receipt_1(), receipt_2()];
195
196 let request = GetShares::new(receipts);
197 let expression: Expression = request.clone().into();
198 let request_envelope = expression.to_envelope();
199 #[rustfmt::skip]
201 assert_eq!(request_envelope.format(), indoc! {r#"
202 «"getShares"» [
203 ❰"receipt"❱: Bytes(32) [
204 'isA': "Receipt"
205 ]
206 ❰"receipt"❱: Bytes(32) [
207 'isA': "Receipt"
208 ]
209 ]
210 "#}.trim());
211 let decoded_expression =
212 Expression::try_from(request_envelope).unwrap();
213 let decoded = GetShares::try_from(decoded_expression).unwrap();
214 assert_eq!(request, decoded);
215 }
216
217 #[test]
218 fn test_response() {
219 bc_envelope::register_tags();
220
221 let receipts_to_data =
222 vec![(receipt_1(), data_1()), (receipt_2(), data_2())]
223 .into_iter()
224 .collect();
225 let response = GetSharesResult::new(receipts_to_data);
226 let response_envelope = response.to_envelope();
227 #[rustfmt::skip]
229 assert_eq!(response_envelope.format(), indoc! {r#"
230 'OK' [
231 Bytes(32) [
232 'isA': "Receipt"
233 ]
234 : Bytes(6)
235 Bytes(32) [
236 'isA': "Receipt"
237 ]
238 : Bytes(6)
239 ]
240 "#}.trim());
241 let decoded = GetSharesResult::try_from(response_envelope).unwrap();
242 assert_eq!(response, decoded);
243 }
244}