canic_core/access/
rule.rs

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