Skip to main content

rustrails_record/
strict_loading.rs

1/// Error raised when an association is lazily loaded in strict-loading mode.
2#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
3#[error("`{association}` was lazily loaded on `{model}` which has strict_loading enabled")]
4pub struct StrictLoadingViolation {
5    /// The model type that rejected the lazy-loaded association.
6    pub model: String,
7    /// The association name that would have been loaded lazily.
8    pub association: String,
9}
10
11/// Trait for models that support strict-loading mode.
12pub trait StrictLoading {
13    /// Returns whether strict loading is enabled for this instance.
14    fn strict_loading(&self) -> bool;
15
16    /// Enables strict loading on this instance.
17    fn set_strict_loading(&mut self, enabled: bool);
18
19    /// Checks whether lazily loading `association` is allowed.
20    fn check_strict_loading(&self, association: &str) -> Result<(), StrictLoadingViolation> {
21        if self.strict_loading() {
22            let model = std::any::type_name::<Self>()
23                .rsplit("::")
24                .next()
25                .map_or_else(|| "unknown".to_owned(), str::to_owned);
26
27            return Err(StrictLoadingViolation {
28                model,
29                association: association.to_owned(),
30            });
31        }
32
33        Ok(())
34    }
35}
36
37/// Stores per-record strict-loading state.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub struct StrictLoadingState {
40    enabled: bool,
41}
42
43impl StrictLoadingState {
44    /// Creates strict-loading state with lazy loading allowed.
45    #[must_use]
46    pub fn new() -> Self {
47        Self::default()
48    }
49
50    /// Creates strict-loading state with lazy loading rejected.
51    #[must_use]
52    pub fn enabled() -> Self {
53        Self { enabled: true }
54    }
55
56    /// Returns whether strict loading is enabled.
57    #[must_use]
58    pub fn is_enabled(&self) -> bool {
59        self.enabled
60    }
61
62    /// Updates the strict-loading flag.
63    pub fn set(&mut self, enabled: bool) {
64        self.enabled = enabled;
65    }
66}
67
68impl StrictLoading for StrictLoadingState {
69    fn strict_loading(&self) -> bool {
70        self.is_enabled()
71    }
72
73    fn set_strict_loading(&mut self, enabled: bool) {
74        self.set(enabled);
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::{StrictLoading, StrictLoadingState, StrictLoadingViolation};
81
82    #[test]
83    fn strict_loading_state_new_is_disabled_by_default() {
84        let state = StrictLoadingState::new();
85
86        assert!(!state.is_enabled());
87        assert!(!state.strict_loading());
88    }
89
90    #[test]
91    fn strict_loading_state_enabled_is_enabled() {
92        let state = StrictLoadingState::enabled();
93
94        assert!(state.is_enabled());
95        assert!(state.strict_loading());
96    }
97
98    #[test]
99    fn check_strict_loading_on_enabled_state_returns_violation() {
100        let state = StrictLoadingState::enabled();
101
102        let error = state
103            .check_strict_loading("comments")
104            .expect_err("enabled strict loading should reject lazy loads");
105
106        assert_eq!(
107            error,
108            StrictLoadingViolation {
109                model: "StrictLoadingState".to_owned(),
110                association: "comments".to_owned(),
111            }
112        );
113    }
114
115    #[test]
116    fn check_strict_loading_on_disabled_state_returns_ok() {
117        let state = StrictLoadingState::new();
118
119        assert!(state.check_strict_loading("comments").is_ok());
120    }
121}