canic_core/access/
rule.rs

1use crate::{
2    Error, ThisError, access::AccessError, ids::BuildNetwork, ops::ic::network::NetworkOps,
3};
4
5///
6/// RuleAccessError
7///
8
9#[derive(Debug, ThisError)]
10pub enum RuleAccessError {
11    #[error("this endpoint requires a build-time network (DFX_NETWORK) of either 'ic' or 'local'")]
12    BuildNetworkUnknown,
13
14    #[error(
15        "this endpoint is only available when built for '{expected}' (DFX_NETWORK), but was built for '{actual}'"
16    )]
17    BuildNetworkMismatch {
18        expected: BuildNetwork,
19        actual: BuildNetwork,
20    },
21}
22
23impl From<RuleAccessError> for Error {
24    fn from(err: RuleAccessError) -> Self {
25        AccessError::Rule(err).into()
26    }
27}
28
29///
30/// Rules
31///
32
33/// build_network_ic
34/// Permits access only when `DFX_NETWORK=ic` was set at build time.
35#[allow(clippy::unused_async)]
36pub async fn build_network_ic() -> Result<(), AccessError> {
37    check_build_network(BuildNetwork::Ic).map_err(AccessError::from)
38}
39
40/// build_network_local
41/// Permits access only when `DFX_NETWORK=local` was set at build time.
42#[allow(clippy::unused_async)]
43pub async fn build_network_local() -> Result<(), AccessError> {
44    check_build_network(BuildNetwork::Local).map_err(AccessError::from)
45}
46
47///
48/// Helpers
49///
50
51pub fn check_build_network(expected: BuildNetwork) -> Result<(), RuleAccessError> {
52    let actual = NetworkOps::build_network();
53
54    match actual {
55        Some(actual) if actual == expected => Ok(()),
56        Some(actual) => Err(RuleAccessError::BuildNetworkMismatch { expected, actual }),
57        None => Err(RuleAccessError::BuildNetworkUnknown),
58    }
59}
60
61///
62/// TESTS
63///
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    fn check(expected: BuildNetwork, actual: Option<BuildNetwork>) -> Result<(), RuleAccessError> {
70        // Inline the same logic but with injected `actual`
71        match actual {
72            Some(actual) if actual == expected => Ok(()),
73            Some(actual) => Err(RuleAccessError::BuildNetworkMismatch { expected, actual }),
74            None => Err(RuleAccessError::BuildNetworkUnknown),
75        }
76    }
77
78    #[test]
79    fn build_network_matches_expected() {
80        assert!(check(BuildNetwork::Ic, Some(BuildNetwork::Ic)).is_ok());
81        assert!(check(BuildNetwork::Local, Some(BuildNetwork::Local)).is_ok());
82    }
83
84    #[test]
85    fn build_network_mismatch_errors() {
86        let err = check(BuildNetwork::Ic, Some(BuildNetwork::Local)).unwrap_err();
87
88        match err {
89            RuleAccessError::BuildNetworkMismatch { expected, actual } => {
90                assert_eq!(expected, BuildNetwork::Ic);
91                assert_eq!(actual, BuildNetwork::Local);
92            }
93            RuleAccessError::BuildNetworkUnknown => panic!("expected BuildNetworkMismatch"),
94        }
95    }
96
97    #[test]
98    fn build_network_unknown_errors() {
99        let err = check(BuildNetwork::Ic, None).unwrap_err();
100        assert!(matches!(err, RuleAccessError::BuildNetworkUnknown));
101    }
102}