Skip to main content

made_client/
ceremonies.rs

1use made_proto::v1::{
2    CeremonyInstanceState, GetCeremonyInstanceRequest, ListCeremonyInstancesRequest,
3    SearchCeremonyInstancesRequest,
4};
5use std::collections::{BTreeSet, VecDeque};
6
7use crate::{CeremonySearchPage, CeremonyTree, MadeClient, MadeClientError};
8
9impl MadeClient {
10    pub async fn get_ceremony(
11        &self,
12        ceremony_id: impl Into<String>,
13    ) -> Result<CeremonyInstanceState, MadeClientError> {
14        let mut rpc = self.rpc();
15        let response = rpc
16            .get_ceremony_instance(Self::request(
17                &self.context(),
18                "/underpass.made.v1.MadeService/GetCeremonyInstance",
19                GetCeremonyInstanceRequest {
20                    ceremony_id: ceremony_id.into(),
21                },
22            ))
23            .await
24            .map_err(MadeClientError::from_status)?
25            .into_inner();
26        response.instance.ok_or_else(|| {
27            MadeClientError::ProtocolViolation("get ceremony response has no instance".to_owned())
28        })
29    }
30
31    /// Existing unpaged server listing kept for compatibility.
32    pub async fn list_ceremonies(&self) -> Result<Vec<CeremonyInstanceState>, MadeClientError> {
33        let mut rpc = self.rpc();
34        let response = rpc
35            .list_ceremony_instances(Self::request(
36                &self.context(),
37                "/underpass.made.v1.MadeService/ListCeremonyInstances",
38                ListCeremonyInstancesRequest {},
39            ))
40            .await
41            .map_err(MadeClientError::from_status)?
42            .into_inner();
43        Ok(response.instances)
44    }
45
46    /// Search one bounded page. The returned cursor is opaque and bound to
47    /// the request's filters; callers must send it back unchanged.
48    pub async fn search_ceremonies(
49        &self,
50        query: SearchCeremonyInstancesRequest,
51    ) -> Result<CeremonySearchPage, MadeClientError> {
52        let mut rpc = self.rpc();
53        let response = rpc
54            .search_ceremony_instances(Self::request(
55                &self.context(),
56                "/underpass.made.v1.MadeService/SearchCeremonyInstances",
57                query,
58            ))
59            .await
60            .map_err(MadeClientError::from_status)?
61            .into_inner();
62        Ok(CeremonySearchPage::new(
63            response.instances,
64            (!response.next_cursor.is_empty()).then_some(response.next_cursor),
65        ))
66    }
67
68    pub async fn ceremony_tree(
69        &self,
70        root_id: impl Into<String>,
71        max_nodes: usize,
72    ) -> Result<CeremonyTree, MadeClientError> {
73        let root_id = root_id.into();
74        let mut pending = VecDeque::from([root_id]);
75        let mut seen = BTreeSet::new();
76        let mut instances = Vec::new();
77        while let Some(ceremony_id) = pending.pop_front() {
78            if !seen.insert(ceremony_id.clone()) {
79                continue;
80            }
81            if seen.len() > max_nodes {
82                return Err(MadeClientError::ProtocolViolation(format!(
83                    "ceremony tree exceeds the requested {max_nodes} node limit"
84                )));
85            }
86            let instance = self.get_ceremony(ceremony_id).await?;
87            for child in instance
88                .child_groups
89                .iter()
90                .flat_map(|group| group.children.iter())
91            {
92                pending.push_back(child.child_id.clone());
93            }
94            instances.push(instance);
95        }
96        CeremonyTree::from_instances(instances)
97    }
98}