ic_query/subnet_catalog/resolver/
prefix.rs1use super::{ResolveAs, ResolvedSubnet};
2use crate::subnet_catalog::{CatalogError, SubnetCatalog, canonical_principal_text};
3use std::collections::BTreeSet;
4
5impl SubnetCatalog {
6 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 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}