depo_api/request/
get_shares.rs

1use 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//
13// Request
14//
15
16#[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//
77// Response
78//
79
80#[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        // println!("{}", request_envelope.format());
200        #[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        // println!("{}", response_envelope.format());
228        #[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}