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