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) .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}