canic_core/access/
rule.rs

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