Skip to main content

ic_query/subnet_catalog/resolver/
prefix.rs

1use super::{ResolveAs, ResolvedSubnet};
2use crate::subnet_catalog::{CatalogError, SubnetCatalog, canonical_principal_text};
3use std::collections::BTreeSet;
4
5impl SubnetCatalog {
6    /// Resolve an exact principal or a unique cached subnet principal prefix.
7    pub fn resolve_principal_or_prefix(
8        &self,
9        input: &str,
10        forced: Option<ResolveAs>,
11    ) -> Result<ResolvedSubnet, CatalogError> {
12        if canonical_principal_text(input).is_ok() {
13            return self.resolve_principal(input, forced);
14        }
15        self.resolve_principal_prefix(input, forced)
16    }
17
18    /// Resolve a unique prefix of a cached subnet principal.
19    pub fn resolve_principal_prefix(
20        &self,
21        input: &str,
22        forced: Option<ResolveAs>,
23    ) -> Result<ResolvedSubnet, CatalogError> {
24        let prefix = input.trim().to_ascii_lowercase();
25        if prefix.is_empty() {
26            return Err(CatalogError::PrincipalPrefixNotFound { prefix });
27        }
28
29        let matches = self.subnet_principal_prefix_matches(&prefix, forced);
30        let mut iter = matches.iter();
31        let Some(first) = iter.next() else {
32            return Err(CatalogError::PrincipalPrefixNotFound { prefix });
33        };
34        if iter.next().is_some() {
35            return Err(CatalogError::AmbiguousPrincipalPrefix {
36                prefix,
37                matches: matches
38                    .iter()
39                    .map(|subnet| format!("subnet:{subnet}"))
40                    .collect::<Vec<_>>(),
41            });
42        }
43
44        let mut resolved = self.resolve_known_subnet(first)?;
45        resolved.input_principal = input.to_string();
46        resolved.resolved_from = "subnet_principal_prefix".to_string();
47        Ok(resolved)
48    }
49
50    fn subnet_principal_prefix_matches(
51        &self,
52        prefix: &str,
53        forced: Option<ResolveAs>,
54    ) -> BTreeSet<String> {
55        let mut matches = BTreeSet::new();
56        if forced != Some(ResolveAs::Canister) {
57            for subnet in &self.subnets {
58                if subnet.subnet_principal.starts_with(prefix) {
59                    matches.insert(subnet.subnet_principal.clone());
60                }
61            }
62        }
63        matches
64    }
65}