kitten/
lib.rs

1pub struct Kitten;
2pub struct GivenAnd<T> {
3    data: T,
4}
5
6pub struct When<T> {
7    data: T,
8}
9
10pub struct Then<T> {
11    data: T,
12}
13
14use std::future::Future;
15
16fn get_current_thread() -> tokio::runtime::Runtime {
17    tokio::runtime::Builder::new_current_thread()
18        .enable_all()
19        .build()
20        .unwrap()
21}
22
23impl Kitten {
24    pub fn given<Result>(function: impl Fn() -> Result) -> GivenAnd<Result> {
25        let result = function();
26        GivenAnd { data: result }
27    }
28
29    pub fn given_async<F, Fut, Result>(f: F) -> GivenAnd<Result>
30    where
31        F: FnOnce() -> Fut,
32        Fut: Future<Output = Result>,
33    {
34        GivenAnd {
35            data: get_current_thread().block_on(f()),
36        }
37    }
38}
39
40impl<Input> GivenAnd<Input> {
41    pub fn and<Result>(self, function: impl Fn(Input) -> Result) -> GivenAnd<Result> {
42        GivenAnd {
43            data: function(self.data),
44        }
45    }
46
47    pub fn when<Result>(self, function: impl Fn(Input) -> Result) -> When<Result> {
48        When {
49            data: function(self.data),
50        }
51    }
52
53    pub fn and_async<F, Fut, Result>(self, f: F) -> GivenAnd<Result>
54    where
55        F: FnOnce(Input) -> Fut,
56        Fut: Future<Output = Result>,
57    {
58        GivenAnd {
59            data: get_current_thread().block_on(f(self.data)),
60        }
61    }
62
63    pub fn when_async<F, Fut, Result>(self, f: F) -> When<Result>
64    where
65        F: FnOnce(Input) -> Fut,
66        Fut: Future<Output = Result>,
67    {
68        When {
69            data: get_current_thread().block_on(f(self.data)),
70        }
71    }
72}
73
74impl<Input> When<Input> {
75    pub fn and<Result>(self, function: impl Fn(Input) -> Result) -> When<Result> {
76        When {
77            data: function(self.data),
78        }
79    }
80    pub fn and_async<F, Fut, Result>(self, f: F) -> When<Result>
81    where
82        F: FnOnce(Input) -> Fut,
83        Fut: Future<Output = Result>,
84    {
85        When {
86            data: get_current_thread().block_on(f(self.data)),
87        }
88    }
89
90    pub fn then<Result>(self, function: impl Fn(Input) -> Result) -> Then<Result> {
91        Then {
92            data: function(self.data),
93        }
94    }
95    pub fn then_async<F, Fut, Result>(self, f: F) -> Then<Result>
96    where
97        F: FnOnce(Input) -> Fut,
98        Fut: Future<Output = Result>,
99    {
100        Then {
101            data: get_current_thread().block_on(f(self.data)),
102        }
103    }
104}
105
106impl<Input> Then<Input> {
107    pub fn and<Result>(self, function: impl Fn(Input) -> Result) -> Then<Result> {
108        Then {
109            data: function(self.data),
110        }
111    }
112    pub fn and_async<F, Fut, Result>(self, f: F) -> Then<Result>
113    where
114        F: FnOnce(Input) -> Fut,
115        Fut: Future<Output = Result>,
116    {
117        Then {
118            data: get_current_thread().block_on(f(self.data)),
119        }
120    }
121}
122#[cfg(test)]
123mod tests {
124    mod calculator;
125    use crate::Kitten;
126    use calculator::Calculator;
127
128    use self::calculator::Calc;
129
130    #[test]
131    fn kitten_works_with_closures() {
132        Kitten::given(|| 10)
133            .and(|result| {
134                assert_eq!(result, 10);
135                String::from("another type")
136            })
137            .when(|result| {
138                assert_eq!(result, String::from("another type"));
139                1
140            })
141            .then(|result| {
142                assert_eq!(result, 1);
143                vec!["this", "is", "a", "list"]
144            })
145            .and(|result| assert_eq!(result, vec!["this", "is", "a", "list"]));
146    }
147
148    #[test]
149    fn kitten_works_with_named_functions_and_looks_clean() {
150        Kitten::given(a_calculator)
151            .when(adding_1_and_2_via_a_function)
152            .then(the_answer_is_3_checked_via_a_function);
153    }
154
155    #[test]
156    fn kitten_works_with_async_functions() {
157        Kitten::given_async(a_calculator_async)
158            .when_async(adding_1_and_2_via_a_function_async)
159            .then_async(the_answer_is_3_checked_via_a_function_async);
160    }
161
162    #[test]
163    fn kitten_works_with_mixing_sync_and_async_functions() {
164        Kitten::given_async(a_calculator_async)
165            .and(|thus_calculator| thus_calculator)
166            .and_async(|thus_calculator| async { thus_calculator })
167            .when_async(adding_1_and_2_via_a_function_async)
168            .and(the_answer_is_3_checked_via_a_function)
169            .then(|_| a_calculator())
170            .and_async(adding_1_and_2_via_a_function_async)
171            .and(the_answer_is_3_checked_via_a_function);
172    }
173
174    #[test]
175    fn kitten_works_with_mixing_other_sync_and_async_functions() {
176        Kitten::given(a_calculator)
177            .and_async(|thus_calculator| async { thus_calculator })
178            .when(adding_1_and_2_via_a_function)
179            .and_async(the_answer_is_3_checked_via_a_function_async)
180            .then(|_| a_calculator())
181            .and(adding_1_and_2_via_a_function)
182            .and_async(the_answer_is_3_checked_via_a_function_async);
183    }
184
185    #[test]
186    fn it_works_with_named_functions_closures_and_closures_that_call_functions() {
187        Kitten::given(a_calculator) // last return in given chain passed to when
188            .when(adding_1_and_2_via_a_function)
189            .then(|answer| {
190                assert_eq!(answer, 3);
191                answer
192            })
193            .and(the_answer_is_3_checked_via_a_function);
194    }
195
196    #[test]
197    fn it_works_with_closures_that_capture_variables() {
198        let example = "example";
199
200        Kitten::given(|| 10)
201            .and(|result| {
202                assert_eq!(result, 10);
203                String::from("another type")
204            })
205            .when(|result| {
206                assert_eq!(result, String::from("another type"));
207                format!("test-{}", example)
208            })
209            .then(|result| {
210                assert_eq!(result, "test-example".to_string());
211                vec!["this", "is", "a", "list"]
212            })
213            .and(|result| assert_eq!(result, vec!["this", "is", "a", "list"]));
214    }
215
216    fn adding_1_and_2_via_a_function(c: Calculator) -> i32 {
217        c.add(1, 2)
218    }
219
220    async fn adding_1_and_2_via_a_function_async(c: Calculator) -> i32 {
221        async { c.add(1, 2) }.await
222    }
223
224    fn the_answer_is_3_checked_via_a_function(the_answer: i32) {
225        assert_eq!(the_answer.clone(), 3);
226    }
227
228    async fn the_answer_is_3_checked_via_a_function_async(the_answer: i32) {
229        assert_eq!(the_answer.clone(), 3);
230        println!("the answer is {}", the_answer);
231    }
232
233    fn a_calculator() -> Calculator {
234        Calculator {}
235    }
236
237    async fn a_calculator_async() -> Calculator {
238        Calculator {}
239    }
240}