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}