Skip to main content

alux_sdk/
conversion.rs

1use alux_ext::ext;
2
3/// Helper extensions implemented for any type related to [Into] interface.
4///
5/// NOTE: It also serves as an example of extension method pattern in Rust.
6#[ext(name = IntoExt)]
7pub impl<This> This {
8    /// A version of [`Into::into`] function that can be used to explicitly specify the target
9    /// conversion type.
10    /// ```
11    /// use alux_sdk::*;
12    /// // `Into` requires context to infer the type,
13    /// let _: i64 = Into::into(42_i8);
14    /// // The opposite `From` requires context to infer the type,
15    /// let _: i64 = From::from(42_i8);
16    /// // `.to()` can be used in the same way as a postfix function (the same as `.into()`),
17    /// let _: i64 = 42_i8.to();
18    /// // but also with the type specified explicitly.
19    /// let _ = 42_i8.to::<i64>();
20    /// // Especially useful to chain multiple conversions inline.
21    /// let _: i64 = 42_i8.to::<i16>().to::<i32>().into();
22    /// ```
23    fn to<R>(self) -> R
24    where
25        This: Into<R>,
26    {
27        self.into()
28    }
29}
30
31/// These tests also serve as an example of two ways functions can be tested.
32///
33/// 1. With automatically created Mock versions of functions and checking if it's
34///    called with the expected arguments.
35///    Traits are just collection of functions so they are tested in a similar way.
36///
37/// 2. With generative testing (quick-check based) by using Arbitrary values as
38///    function input and checking for expected result.
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use std::fmt::Debug;
43    use std::sync::Arc;
44
45    // Testing with mock functions (mockall lib)
46
47    use mockall::mock;
48    use mockall::predicate::*;
49
50    // Creates mock structure and its implementation for the `From` trait. It has
51    //  generic parameter because testing extension is also generic.
52    mock! {
53        FromSt<T: 'static> {}
54
55        impl<T: 'static> From<T> for FromSt<T> {
56            fn from(t: T) -> Self;
57        }
58    }
59
60    /// Proves that [`IntoExt::to`] delegates to [`From::from`].
61    fn proof_to_calls_from<T: Debug + Clone + PartialEq + Send + 'static>(t: T) {
62        // Mock of `From<T>::from` trait function with expectations
63        let ctx = MockFromSt::<T>::from_context();
64        ctx.expect()
65            // From should be called once ...
66            .times(1)
67            // ... for any input value
68            .with(eq(t.clone()))
69            // Result of `from` is not tested here so return default
70            .returning(|_| MockFromSt::default());
71
72        // Call to testing extension function
73        t.to::<MockFromSt<T>>();
74    }
75
76    #[test]
77    fn run_proof_to_calls_from() {
78        // Running mocking tests with different types even though running
79        //  with only one type passes the test.
80        proof_to_calls_from(42);
81        proof_to_calls_from("Hello");
82        proof_to_calls_from(());
83        proof_to_calls_from(Arc::new(42));
84    }
85
86    // Testing with generative tests (proptest lib)
87
88    use proptest::prelude::*;
89
90    /// Testing structure with [From<T>] implementation
91    #[derive(Debug, PartialEq)]
92    struct Wrap<T>(T);
93
94    impl<T> From<T> for Wrap<T> {
95        fn from(t: T) -> Self {
96            Wrap(t)
97        }
98    }
99
100    fn from_and_to_fun_equal<T: PartialEq + Debug + Clone>(t: T) {
101        // Creates wrapped value created with `From`
102        let wrapped_expected: Wrap<T> = From::from(t.clone());
103
104        // Creates wrapped value created with `.to()`
105        let wrapped_actual = t.to::<Wrap<T>>();
106
107        assert_eq!(wrapped_expected, wrapped_actual);
108    }
109
110    // Running generative tests with different types even though running
111    //  with only one type passes the test.
112    proptest! {
113        #[test]
114        fn from_and_to_fun_equal_int(v: i128) {
115            from_and_to_fun_equal(v);
116        }
117
118        #[test]
119        fn from_and_to_fun_equal_str(v: String) {
120            from_and_to_fun_equal(v);
121        }
122    }
123}