state_validation/
lib.rs

1//! ## State Validation
2//! `state-validation` lets you validate an input for a given state. Then, run an action using the validated output.
3//!
4//! Ex. You want to remove an admin from `UserStorage`, given a `UserID`, you want to retrieve the `User` who maps onto the `UserID` and validate they are an existing user whose privilege level is admin.
5//! The state is `UserStorage`, the input is a `UserID`, and the valid output is an `AdminUser`.
6//!
7//! Here is our input `UserID`:
8//! ```
9//! # use std::collections::{HashSet, HashMap};
10//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
11//! #[derive(Hash, PartialEq, Eq, Clone, Copy)]
12//! struct UserID(usize);
13//! ```
14//! Our `User` which holds its `UserID` and `username`:
15//! ```
16//! # use std::collections::{HashSet, HashMap};
17//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
18//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
19//! # struct UserID(usize);
20//! #[derive(Clone)]
21//! struct User {
22//!     id: UserID,
23//!     username: String,
24//! }
25//! ```
26//! Our state will be `UserStorage`:
27//! ```
28//! # use std::collections::{HashSet, HashMap};
29//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
30//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
31//! # struct UserID(usize);
32//! # #[derive(Clone)]
33//! # struct User {
34//! #     id: UserID,
35//! #     username: String,
36//! # }
37//! #[derive(Default)]
38//! struct UserStorage {
39//!     maps: HashMap<UserID, User>,
40//! }
41//! ```
42//! We will create a newtype `AdminUser`, which only users with admin privilege will be wrapped in.
43//! This will also be the output of our filter which checks if a user is admin:
44//! ```
45//! # use std::collections::{HashSet, HashMap};
46//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
47//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
48//! # struct UserID(usize);
49//! # #[derive(Clone)]
50//! # struct User {
51//! #     id: UserID,
52//! #     username: String,
53//! # }
54//! # #[derive(Default)]
55//! # struct UserStorage {
56//! #     maps: HashMap<UserID, User>,
57//! # }
58//! struct AdminUser(User);
59//! ```
60//! Our first filter will check if a `User` exists given a `UserID`:
61//! ```
62//! # use std::collections::{HashSet, HashMap};
63//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
64//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
65//! # struct UserID(usize);
66//! # #[derive(Clone)]
67//! # struct User {
68//! #     id: UserID,
69//! #     username: String,
70//! # }
71//! # #[derive(Default)]
72//! # struct UserStorage {
73//! #     maps: HashMap<UserID, User>,
74//! # }
75//! # struct AdminUser(User);
76//! struct UserExists;
77//! # #[derive(Debug)]
78//! # struct UserDoesNotExistError;
79//! # impl std::error::Error for UserDoesNotExistError {}
80//! # impl std::fmt::Display for UserDoesNotExistError {
81//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82//! #        write!(f, "user does not exist")
83//! #     }
84//! # }
85//! impl StateFilter<UserStorage, UserID> for UserExists {
86//!     type ValidOutput = User;
87//!     type Error = UserDoesNotExistError;
88//!     fn filter(state: &UserStorage, user_id: UserID) -> Result<Self::ValidOutput, Self::Error> {
89//!         if let Some(user) = state.maps.get(&user_id) {
90//!             Ok(user.clone())
91//!         } else {
92//!             Err(UserDoesNotExistError)
93//!         }
94//!     }
95//! }
96//! ```
97//! Our second filter will check if a `User` is admin:
98//! ```
99//! # use std::collections::{HashSet, HashMap};
100//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
101//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
102//! # struct UserID(usize);
103//! # #[derive(Clone)]
104//! # struct User {
105//! #     id: UserID,
106//! #     username: String,
107//! # }
108//! # #[derive(Default)]
109//! # struct UserStorage {
110//! #     maps: HashMap<UserID, User>,
111//! # }
112//! # struct AdminUser(User);
113//! #
114//! struct UserIsAdmin;
115//! # #[derive(Debug)]
116//! # struct UserIsNotAdminError;
117//! # impl std::error::Error for UserIsNotAdminError {}
118//! # impl std::fmt::Display for UserIsNotAdminError {
119//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
120//! #        write!(f, "user is not an admin")
121//! #     }
122//! # }
123//! impl<State> StateFilter<State, User> for UserIsAdmin {
124//!     type ValidOutput = AdminUser;
125//!     type Error = UserIsNotAdminError;
126//!     fn filter(state: &State, user: User) -> Result<Self::ValidOutput, Self::Error> {
127//!         if user.username == "ADMIN" {
128//!             Ok(AdminUser(user))
129//!         } else {
130//!             Err(UserIsNotAdminError)
131//!         }
132//!     }
133//! }
134//! ```
135//! Note: in the above code, we don't care about the `state` so it is a generic.
136//!
137//! Now, we can finally implement an action that removes the admin from user storage:
138//! ```
139//! # use std::collections::{HashSet, HashMap};
140//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
141//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
142//! # struct UserID(usize);
143//! # #[derive(Clone)]
144//! # struct User {
145//! #     id: UserID,
146//! #     username: String,
147//! # }
148//! # #[derive(Default)]
149//! # struct UserStorage {
150//! #     maps: HashMap<UserID, User>,
151//! # }
152//! # struct AdminUser(User);
153//! # struct UserExists;
154//! # #[derive(Debug)]
155//! # struct UserDoesNotExistError;
156//! # impl std::error::Error for UserDoesNotExistError {}
157//! # impl std::fmt::Display for UserDoesNotExistError {
158//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
159//! #        write!(f, "user does not exist")
160//! #     }
161//! # }
162//! # impl StateFilter<UserStorage, UserID> for UserExists {
163//! #     type ValidOutput = User;
164//! #     type Error = UserDoesNotExistError;
165//! #     fn filter(state: &UserStorage, user_id: UserID) -> Result<Self::ValidOutput, Self::Error> {
166//! #         if let Some(user) = state.maps.get(&user_id) {
167//! #             Ok(user.clone())
168//! #         } else {
169//! #             Err(UserDoesNotExistError)
170//! #         }
171//! #     }
172//! # }
173//! # struct UserIsAdmin;
174//! # #[derive(Debug)]
175//! # struct UserIsNotAdminError;
176//! # impl std::error::Error for UserIsNotAdminError {}
177//! # impl std::fmt::Display for UserIsNotAdminError {
178//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
179//! #        write!(f, "user is not an admin")
180//! #     }
181//! # }
182//! # impl<State> StateFilter<State, User> for UserIsAdmin {
183//! #     type ValidOutput = AdminUser;
184//! #     type Error = UserIsNotAdminError;
185//! #     fn filter(state: &State, user: User) -> Result<Self::ValidOutput, Self::Error> {
186//! #         if user.username == "ADMIN" {
187//! #             Ok(AdminUser(user))
188//! #         } else {
189//! #             Err(UserIsNotAdminError)
190//! #         }
191//! #     }
192//! # }
193//! #
194//! struct RemoveAdmin;
195//! impl ValidAction<UserStorage, UserID> for RemoveAdmin {
196//!     // To chain filters, use `Condition`.
197//!     type Filter = (
198//!         //       <Input,  Filter>
199//!         Condition<UserID, UserExists>,
200//!         // Previous filter outputs a `User`,
201//!         // so next filter can take `User` as input.
202//!         Condition<User, UserIsAdmin>,
203//!     );
204//!     type Output = UserStorage;
205//!     fn with_valid_input(
206//!         self,
207//!         mut state: UserStorage,
208//!         // The final output from the filters is an `AdminUser`.
209//!         admin_user: <Self::Filter as StateFilter<UserStorage, UserID>>::ValidOutput,
210//!     ) -> Self::Output {
211//!         let _ = state.maps.remove(&admin_user.0.id).unwrap();
212//!         state
213//!     }
214//! }
215//! ```
216//! Now, let's put it all together. We create the state `UserStorage`,
217//! and then use [`Validator::try_new`] to run our filters. An error is returned if any of the filters fail,
218//! otherwise we get a validator that we can run an action on:
219//! ```
220//! # use std::collections::{HashSet, HashMap};
221//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
222//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
223//! # struct UserID(usize);
224//! # #[derive(Clone)]
225//! # struct User {
226//! #     id: UserID,
227//! #     username: String,
228//! # }
229//! # #[derive(Default)]
230//! # struct UserStorage {
231//! #     maps: HashMap<UserID, User>,
232//! # }
233//! # struct AdminUser(User);
234//! # struct UserExists;
235//! # #[derive(Debug)]
236//! # struct UserDoesNotExistError;
237//! # impl std::error::Error for UserDoesNotExistError {}
238//! # impl std::fmt::Display for UserDoesNotExistError {
239//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
240//! #        write!(f, "user does not exist")
241//! #     }
242//! # }
243//! # impl StateFilter<UserStorage, UserID> for UserExists {
244//! #     type ValidOutput = User;
245//! #     type Error = UserDoesNotExistError;
246//! #     fn filter(state: &UserStorage, user_id: UserID) -> Result<Self::ValidOutput, Self::Error> {
247//! #         if let Some(user) = state.maps.get(&user_id) {
248//! #             Ok(user.clone())
249//! #         } else {
250//! #             Err(UserDoesNotExistError)
251//! #         }
252//! #     }
253//! # }
254//! # struct UserIsAdmin;
255//! # #[derive(Debug)]
256//! # struct UserIsNotAdminError;
257//! # impl std::error::Error for UserIsNotAdminError {}
258//! # impl std::fmt::Display for UserIsNotAdminError {
259//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
260//! #        write!(f, "user is not an admin")
261//! #     }
262//! # }
263//! # impl<State> StateFilter<State, User> for UserIsAdmin {
264//! #     type ValidOutput = AdminUser;
265//! #     type Error = UserIsNotAdminError;
266//! #     fn filter(state: &State, user: User) -> Result<Self::ValidOutput, Self::Error> {
267//! #         if user.username == "ADMIN" {
268//! #             Ok(AdminUser(user))
269//! #         } else {
270//! #             Err(UserIsNotAdminError)
271//! #         }
272//! #     }
273//! # }
274//! #
275//! # struct RemoveAdmin;
276//! # impl ValidAction<UserStorage, UserID> for RemoveAdmin {
277//! #     type Filter = (
278//! #         Condition<UserID, UserExists>,
279//! #         Condition<User, UserIsAdmin>,
280//! #     );
281//! #     type Output = UserStorage;
282//! #     fn with_valid_input(
283//! #         self,
284//! #         mut state: UserStorage,
285//! #         admin_user: <Self::Filter as StateFilter<UserStorage, UserID>>::ValidOutput,
286//! #     ) -> Self::Output {
287//! #         let _ = state.maps.remove(&admin_user.0.id).unwrap();
288//! #         state
289//! #     }
290//! # }
291//! #
292//! // State setup
293//! let mut user_storage = UserStorage::default();
294//! user_storage.maps.insert(UserID(0), User {
295//!     id: UserID(0),
296//!     username: "ADMIN".to_string(),
297//! });
298//!
299//! // Create validator which will validate the input.
300//! // No error is returned if validation succeeds.
301//! let validator = Validator::try_new(user_storage, UserID(0)).expect("admin user did not exist");
302//!
303//! // Execute an action which requires the state and input above.
304//! let user_storage = validator.execute(RemoveAdmin);
305//!
306//! assert!(user_storage.maps.is_empty());
307//! ```
308//! ---
309//! Another example using `UserExists` filter, and `UserIsAdmin` filter in the body of the [`ValidAction`]:
310//! ```
311//! # use std::collections::{HashSet, HashMap};
312//! # use state_validation::{Validator, ValidAction, StateFilter, Condition};
313//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
314//! # struct UserID(usize);
315//! #
316//! # #[derive(Default)]
317//! # struct UserStorage {
318//! #     maps: HashMap<UserID, User>,
319//! # }
320//! # #[derive(Clone)]
321//! # struct User {
322//! #     id: UserID,
323//! #     username: String,
324//! # }
325//! #
326//! # struct AdminUser(User);
327//! #
328//! # struct UserExists;
329//! # #[derive(Debug)]
330//! # struct UserDoesNotExistError;
331//! # impl std::error::Error for UserDoesNotExistError {}
332//! # impl std::fmt::Display for UserDoesNotExistError {
333//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
334//! #        write!(f, "user does not exist")
335//! #     }
336//! # }
337//! # impl StateFilter<UserStorage, UserID> for UserExists {
338//! #     type ValidOutput = User;
339//! #     type Error = UserDoesNotExistError;
340//! #     fn filter(state: &UserStorage, user_id: UserID) -> Result<Self::ValidOutput, Self::Error> {
341//! #         if let Some(user) = state.maps.get(&user_id) {
342//! #             Ok(user.clone())
343//! #         } else {
344//! #             Err(UserDoesNotExistError)
345//! #         }
346//! #     }
347//! # }
348//! #
349//! # struct UserIsAdmin;
350//! # #[derive(Debug)]
351//! # struct UserIsNotAdminError;
352//! # impl std::error::Error for UserIsNotAdminError {}
353//! # impl std::fmt::Display for UserIsNotAdminError {
354//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
355//! #        write!(f, "user is not an admin")
356//! #     }
357//! # }
358//! # impl StateFilter<UserStorage, User> for UserIsAdmin {
359//! #     type ValidOutput = AdminUser;
360//! #     type Error = UserIsNotAdminError;
361//! #     fn filter(state: &UserStorage, user: User) -> Result<Self::ValidOutput, Self::Error> {
362//! #         if user.username == "ADMIN" {
363//! #             Ok(AdminUser(user))
364//! #         } else {
365//! #             Err(UserIsNotAdminError)
366//! #         }
367//! #     }
368//! # }
369//! #
370//! enum Privilege {
371//!     None,
372//!     Admin,
373//! }
374//!
375//! struct UpdateUserPrivilege(Privilege);
376//! impl ValidAction<UserStorage, UserID> for UpdateUserPrivilege {
377//!     type Filter = UserExists;
378//!     type Output = UserStorage;
379//!     fn with_valid_input(
380//!         self,
381//!         mut state: UserStorage,
382//!         mut user: <Self::Filter as StateFilter<UserStorage, UserID>>::ValidOutput,
383//!     ) -> Self::Output {
384//!         match self.0 {
385//!             Privilege::None => {
386//!                 if let Ok(AdminUser(mut user)) = UserIsAdmin::filter(&state, user) {
387//!                     user.username = "NOT_ADMIN".to_string();
388//!                     let _ = state.maps.insert(user.id, user);
389//!                 }
390//!             }
391//!             Privilege::Admin => {
392//!                 user.username = "ADMIN".to_string();
393//!                 let _ = state.maps.insert(user.id, user);
394//!             }
395//!         }
396//!         state
397//!     }
398//! }
399//!
400//! # let mut user_storage = UserStorage::default();
401//! # user_storage.maps.insert(UserID(0), User {
402//! #     id: UserID(0),
403//! #     username: "ADMIN".to_string(),
404//! # });
405//! # assert_eq!(user_storage.maps.len(), 1);
406//! # let user = user_storage.maps.get(&UserID(0));
407//! # assert!(user.is_some());
408//! # assert_eq!(user.unwrap().username, "ADMIN");
409//!
410//! // This `Validator` has its generic `Filter` parameter implicitly changed
411//! // based on what action we call. On a type level, this is a different type
412//! // than the validator in the previous example even though we write the same code
413//! // due to its `Filter` generic parameter being different.
414//! let validator = Validator::try_new(user_storage, UserID(0)).expect("user did not exist");
415//!
416//! let user_storage = validator.execute(UpdateUserPrivilege(Privilege::None));
417//!
418//! # assert_eq!(user_storage.maps.len(), 1);
419//! let user = user_storage.maps.get(&UserID(0));
420//! # assert!(user.is_some());
421//! assert_eq!(user.unwrap().username, "NOT_ADMIN");
422//! ```
423//!
424//! ## [`StateFilterInputConversion`] & [`StateFilterInputCombination`]
425//! Automatic implementations of [`StateFilterInputConversion`] and [`StateFilterInputCombination`]
426//! are generated with the [`StateFilterConversion`] derive macro.
427//! Example:
428//! ```
429//! # use state_validation::StateFilterConversion;
430//! # struct UserID(usize);
431//! # struct User;
432//! #[derive(StateFilterConversion)]
433//! struct UserWithUserName {
434//!     #[conversion(User)]
435//!     user_id: UserID,
436//!     username: String,
437//! }
438//! ```
439//! Now, `UserWithUsername` can be broken down into `User`, `UserID`, and `String`.
440//! Take advantage of the newtype pattern to breakdown the input further.
441//! For example, instead of having username as a `String`, use:
442//! ```
443//! struct Username(String);
444//! ```
445//! This way, the compiler can differentiate between a `String` and a `Username`.
446//!
447//!
448//! The [`StateFilterInputConversion`] and [`StateFilterInputCombination`] traits work together
449//! to allow splitting the input down into its parts and then back together.
450//!
451//! The usefulness of this is, if a [`StateFilter`] only requires a part of the input,
452//! [`StateFilterInputConversion`] can split it down to just that part, and leave the rest in [`StateFilterInputConversion::Remainder`]
453//! which will be combined with the output of the filter. And, each consecutive filter can split
454//! whatever input they desire and combine their output with the remainder they did not touch.
455//!
456//! Here is an example with manual implementations: Assume we wanted to change the username of a user,
457//! if its `UserID` and current username matched that of the input.
458//! First, let's create its input:
459//! ```
460//! # struct UserID(usize);
461//! struct UserWithUsername {
462//!     user_id: UserID,
463//!     username: String,
464//! }
465//! ```
466//!
467//! We want to check if a user with `UserID` exists, and their current username is `username`, and then update their username.
468//! We already have a filter that will check if a user exists. It is called: `UserExists`.
469//! But, `UserExists` does not take `UserWithUsername` as an input. It only takes `UserID` as input.
470//! `UserWithUsername` does contain a `UserID`. So, we should be able to retrieve the `UserID`,
471//! pass it into `UserExists`, then reconstruct the output of `UserExists` with the leftover `username`.
472//! The first part of solving this issue is input conversion into `UserID`, so we implement [`StateFilterInputConversion`]:
473//! ```
474//! # use state_validation::StateFilterInputConversion;
475//! # struct UserID(usize);
476//! # struct UserWithUsername {
477//! #     user_id: UserID,
478//! #     username: String,
479//! # }
480//! struct UsernameForUserID(String);
481//! impl StateFilterInputConversion<UserID> for UserWithUsername {
482//!     type Remainder = UsernameForUserID;
483//!     fn split_take(self) -> (UserID, Self::Remainder) {
484//!         (self.user_id, UsernameForUserID(self.username))
485//!     }
486//! }
487//! ```
488//! Notice in the above code, within `split_take`, the first element of the tuple is the input our `UserExists` filter cares about.
489//! However, for the `Remainder`, we have a newtype which stores the leftover to combine later with the output of `UserExists`.
490//! ```
491//! # use state_validation::StateFilterInputCombination;
492//! # struct User;
493//! # struct UserID(usize);
494//! # struct UsernameForUserID(String);
495//! struct UsernameForUser {
496//!     user: User,
497//!     username: String,
498//! }
499//! impl StateFilterInputCombination<User> for UsernameForUserID {
500//!     type Combined = UsernameForUser;
501//!     fn combine(self, user: User) -> Self::Combined {
502//!         UsernameForUser {
503//!             user,
504//!             username: self.0,
505//!         }
506//!     }
507//! }
508//! ```
509//! Now, the combination of `UserExists`'s output (which is a `User`) and the leftover `username`,
510//! results in a new struct called `UsernameForUser`.
511//!
512//! Now, we need a new filter that checks if the username of the `User` is equal to `username`.
513//! ```
514//! # use state_validation::StateFilter;
515//! # struct User {
516//! #     username: String,
517//! # }
518//! # struct UsernameForUser {
519//! #     user: User,
520//! #     username: String,
521//! # }
522//! struct UsernameEquals;
523//! # #[derive(Debug)]
524//! # struct UsernameIsNotEqual;
525//! # impl std::error::Error for UsernameIsNotEqual {}
526//! # impl std::fmt::Display for UsernameIsNotEqual {
527//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
528//! #        write!(f, "username is not equal")
529//! #     }
530//! # }
531//! impl<State> StateFilter<State, UsernameForUser> for UsernameEquals {
532//!     type ValidOutput = User;
533//!     type Error = UsernameIsNotEqual;
534//!     fn filter(state: &State, value: UsernameForUser) -> Result<Self::ValidOutput, Self::Error> {
535//!         if value.user.username == value.username {
536//!             Ok(value.user)
537//!         } else {
538//!             Err(UsernameIsNotEqual)
539//!         }
540//!     }
541//! }
542//! ```
543//!
544//! Finally, we can run our action to update the user's username:
545//! ```
546//! # use std::collections::HashMap;
547//! # use state_validation::{ValidAction, Condition, StateFilter, StateFilterInputConversion, StateFilterInputCombination};
548//! # #[derive(Hash, PartialEq, Eq, Clone, Copy)]
549//! # struct UserID(usize);
550//! # #[derive(Clone)]
551//! # struct User {
552//! #     id: UserID,
553//! #     username: String,
554//! # }
555//! # #[derive(Default)]
556//! # struct UserStorage {
557//! #     maps: HashMap<UserID, User>,
558//! # }
559//! # struct UserExists;
560//! # #[derive(Debug)]
561//! # struct UserDoesNotExistError;
562//! # impl std::error::Error for UserDoesNotExistError {}
563//! # impl std::fmt::Display for UserDoesNotExistError {
564//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
565//! #        write!(f, "user does not exist")
566//! #     }
567//! # }
568//! # impl StateFilter<UserStorage, UserID> for UserExists {
569//! #     type ValidOutput = User;
570//! #     type Error = UserDoesNotExistError;
571//! #     fn filter(state: &UserStorage, user_id: UserID) -> Result<Self::ValidOutput, Self::Error> {
572//! #         if let Some(user) = state.maps.get(&user_id) {
573//! #             Ok(user.clone())
574//! #         } else {
575//! #             Err(UserDoesNotExistError)
576//! #         }
577//! #     }
578//! # }
579//! # struct UserWithUsername {
580//! #     user_id: UserID,
581//! #     username: String,
582//! # }
583//! # struct UsernameForUserID(String);
584//! # impl StateFilterInputConversion<UserID> for UserWithUsername {
585//! #     type Remainder = UsernameForUserID;
586//! #     fn split_take(self) -> (UserID, Self::Remainder) {
587//! #         (self.user_id, UsernameForUserID(self.username))
588//! #     }
589//! # }
590//! # struct UsernameForUser {
591//! #     user: User,
592//! #     username: String,
593//! # }
594//! # impl StateFilterInputCombination<User> for UsernameForUserID {
595//! #     type Combined = UsernameForUser;
596//! #     fn combine(self, user: User) -> Self::Combined {
597//! #         UsernameForUser {
598//! #             user,
599//! #             username: self.0,
600//! #         }
601//! #     }
602//! # }
603//! # struct UsernameEquals;
604//! # #[derive(Debug)]
605//! # struct UsernameIsNotEqual;
606//! # impl std::error::Error for UsernameIsNotEqual {}
607//! # impl std::fmt::Display for UsernameIsNotEqual {
608//! #    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
609//! #        write!(f, "username is not equal")
610//! #     }
611//! # }
612//! # impl<State> StateFilter<State, UsernameForUser> for UsernameEquals {
613//! #     type ValidOutput = User;
614//! #     type Error = UsernameIsNotEqual;
615//! #     fn filter(state: &State, value: UsernameForUser) -> Result<Self::ValidOutput, Self::Error> {
616//! #         if value.user.username == value.username {
617//! #             Ok(value.user)
618//! #         } else {
619//! #             Err(UsernameIsNotEqual)
620//! #         }
621//! #     }
622//! # }
623//! struct UpdateUsername {
624//!     new_username: String,
625//! }
626//! impl ValidAction<UserStorage, UserWithUsername> for UpdateUsername {
627//!     type Filter = (
628//!         Condition<UserID, UserExists>,
629//!         Condition<UsernameForUser, UsernameEquals>,
630//!     );
631//!     type Output = UserStorage;
632//!     fn with_valid_input(
633//!         self,
634//!         mut state: UserStorage,
635//!         mut user: <Self::Filter as StateFilter<UserStorage, UserWithUsername>>::ValidOutput,
636//!     ) -> Self::Output {
637//!         user.username = self.new_username;
638//!         let _ = state.maps.insert(user.id, user);
639//!         state
640//!     }
641//! }
642//! ```
643//!
644//! ## Soundness Rules
645//! [`Validator::try_new`] takes ownership of the `state` to disallow consecutive
646//! [`Validator::execute`] calls because an action is assumed to mutate the `state`.
647//! Since an action is assumed to mutate the `state`, any validators using the same `state`
648//! cannot be created.
649//!
650//! It is up to you to make sure the filters properly validate what they promise.
651//!
652//! ## Limitations
653//! Currently, the amount of filters that can be chained is eight.
654//! The reason for this is because of variadics not being supported as of Rust 2024.
655//! Having no more than eight implementations is arbitrary because having more than eight filters is unlikely.
656//! There is no reason not to implement more in the future, if more than eight filters are required.
657
658mod action;
659mod condition;
660#[cfg(feature = "input_collector")]
661mod input_collector;
662mod state_filter;
663pub use action::*;
664pub use condition::*;
665#[cfg(feature = "input_collector")]
666pub use input_collector::*;
667pub use state_filter::*;
668#[cfg(feature = "derive")]
669pub use state_validation_derive::*;
670
671pub struct Validator<State, Input, Filter: StateFilter<State, Input>> {
672    state: State,
673    value: Filter::ValidOutput,
674    _p: std::marker::PhantomData<(Input, Filter)>,
675}
676
677impl<State, Input, Filter: StateFilter<State, Input>> Validator<State, Input, Filter> {
678    pub fn try_new(state: State, input: Input) -> Result<Self, Filter::Error> {
679        let value = Filter::filter(&state, input)?;
680        Ok(Validator {
681            state,
682            value,
683            _p: std::marker::PhantomData::default(),
684        })
685    }
686    pub fn execute<Action: ValidAction<State, Input, Filter = Filter>>(
687        self,
688        valid_action: Action,
689    ) -> Action::Output {
690        valid_action.with_valid_input(self.state, self.value)
691    }
692}