checked_rs/
view.rs

1use anyhow::Result;
2
3use crate::guard::Guard;
4
5pub trait Validator: 'static + Copy {
6    type Item;
7    type Error;
8    fn validate(item: &Self::Item) -> Result<(), Self::Error>;
9}
10
11#[derive(
12    Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
13)]
14#[repr(transparent)]
15pub struct View<T: 'static, E, U: Validator<Item = T, Error = E>>(T, std::marker::PhantomData<U>);
16
17impl<T: std::fmt::Debug, E, U: Validator<Item = T, Error = E>> std::fmt::Debug for View<T, E, U> {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        f.debug_tuple("View").field(&self.0).finish()
20    }
21}
22
23impl<T, E, U: Validator<Item = T, Error = E>> std::ops::Deref for View<T, E, U> {
24    type Target = T;
25
26    #[inline(always)]
27    fn deref(&self) -> &Self::Target {
28        self.as_ref()
29    }
30}
31
32impl<T, E, U: Validator<Item = T, Error = E>> AsRef<T> for View<T, E, U> {
33    #[inline(always)]
34    fn as_ref(&self) -> &T {
35        &self.0
36    }
37}
38
39impl<T, E, U: Validator<Item = T, Error = E>> View<T, E, U> {
40    #[inline(always)]
41    pub fn new(item: T) -> Self {
42        Self(item, std::marker::PhantomData)
43    }
44
45    #[inline(always)]
46    pub fn with_validator(item: T, _: U) -> Self {
47        Self(item, std::marker::PhantomData)
48    }
49
50    #[inline(always)]
51    pub fn into_inner(self) -> T {
52        self.0
53    }
54
55    #[inline(always)]
56    #[must_use]
57    pub fn modify<'a>(&'a mut self) -> Guard<'a, T, E, U> {
58        Guard::new(&mut self.0)
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_view() -> Result<()> {
68        #[derive(Clone, Copy)]
69        struct TestValidator;
70
71        impl Validator for TestValidator {
72            type Item = i32;
73            type Error = anyhow::Error;
74
75            fn validate(item: &Self::Item) -> Result<()> {
76                if *item < 0 {
77                    Err(anyhow::anyhow!("Value must be positive"))
78                } else if *item % 2 == 0 && *item != 0 && *item <= 10 {
79                    Err(anyhow::anyhow!(
80                        "Value must be odd, or zero, or greater than 10"
81                    ))
82                } else if *item == 7 {
83                    Err(anyhow::anyhow!("Value must not be 7"))
84                } else {
85                    Ok(())
86                }
87            }
88        }
89
90        let mut item = View::with_validator(0, TestValidator);
91        let mut g = item.modify();
92
93        *g = 1;
94        assert_eq!(*g, 1);
95        assert!(g.check().is_ok());
96
97        *g = -1;
98        assert_eq!(*g, -1);
99        assert!(g.check().is_err());
100
101        *g = 12;
102        assert_eq!(*g, 12);
103        assert!(g.check().is_ok());
104
105        *g = 7;
106        assert_eq!(*g, 7);
107
108        let mut g = match g.commit() {
109            Ok(_) => panic!("Expected error"),
110            Err(g) => g,
111        };
112
113        // The guard's value should be unchanged if commit fails
114        assert_eq!(*g, 7);
115
116        *g = 100;
117        assert!(g.commit().is_ok());
118
119        // the guard is consumed by commit, so we can't check it again
120        // the `View`'s value should be updated
121        assert_eq!(&*item, &100);
122
123        Ok(())
124    }
125}