1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
//! # Preprocess
//!
//! A crate to help you preprocess your structs and enums.
//! Can be used to validate data, or to transform it.
//!
//! There are two kinds of preprocessors:
//!
//! - **Validators**: They check if the given field is valid and don't modify
//! the value. For example: a validator could check if a string is a valid
//! email address.
//! - **Preprocessors**: These allow you to modify the value (and possibly type)
//! of a field. For example: a preprocessor could trim a string, or convert it
//! to uppercase.
//!
//! ## Example usage
//!
//! ```rust
//! use preprocess::prelude::*;
//!
//! #[preprocess::sync]
//! #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
//! pub struct UserSignUpRequest {
//! // First trims the email, then converts it to lowercase, then validates it as an email address.
//! #[preprocess(trim, lowercase, email)]
//! pub email: String,
//! // First trims the password, then checks if it's at least 8 characters long.
//! #[preprocess(trim, length(min = 8))]
//! pub password: String,
//! }
//!
//! let processed_value = raw_value.preprocess()?;
//! ```
//!
//! ## Inheriting derive attributes
//!
//! Since the crate uses an attribute macro, it must always be the first
//! attribute on the struct or enum. A new struct / enum will be generated with
//! the name `{original_name}Processed`. The derive macro will inherit all the
//! derive attributes from the original struct / enum. For example:
//!
//! ```rust
//! use preprocess::prelude::*;
//!
//! #[preprocess::sync]
//! #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct UserSignUpRequest {
//! #[preprocess(trim, lowercase, email)]
//! #[serde(default)]
//! pub email: String,
//! #[serde(alias = "pass")]
//! #[preprocess(trim, length(min = 8))]
//! pub password: String,
//! }
//! ```
//!
//! The above code will generate:
//!
//! ```rust
//! #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct UserSignUpRequestProcessed {
//! #[serde(default)]
//! pub email: String,
//! #[serde(alias = "pass")]
//! pub password: String,
//! }
//! ```
//!
//! This way, any custom derive attributes you use (like Serde) will be
//! inherited by the generated struct / enum. This also ensures that you can
//! preprocess your struct / enum and send the preprocessed version to the
//! client, without having to write any extra code.
//!
//! ## List of allowed preprocessors
//!
//! | Preprocessor | Description |
//! | ---------------------------------------------------------- | --------------------------------------------------- |
//! | [`email`](`crate::validators#email`) | Validates a string to be a valid email address. |
//! | [`domain`](`crate::validators#domain`) | Validates a string to be a valid domain name. |
//! | [`ip`](`crate::validators#ip`) | Validates a string to be a valid IP Address. |
//! | [`url`](`crate::validators#url`) | Validates a string to be a valid URL. |
//! | [`length`](`crate::validators#length`) | Validates the length of a string. |
//! | [`range`](`crate::validators#range`) | Validates the range of a number. |
//! | [`contains`](`crate::validators#contains`) | Validates if a string contains a substring. |
//! | [`does_not_contain`](`crate::validators#does_not_contain`) | Validates if a string does not contain a substring. |
//! | [`regex`](`crate::validators#regex`) | Validates a string using a regex. |
//! | [`type`](#enforcing-the-type-of-a-value) | Enforces the type of a value using `TryFrom`. |
//! | [`trim`](`crate::preprocessors#trim`) | Trims a string. |
//! | [`lowercase`](`crate::preprocessors#lowercase`) | Converts a string to lowercase. |
//! | [`uppercase`](`crate::preprocessors#uppercase`) | Converts a string to uppercase. |
//! | [`custom`](#custom-preprocessors) | Validates a string using a custom function. |
//!
//! More details about each preprocessor can be found in the respective module
//! documentation of [preprocessors](crate::preprocessors) and
//! [validators](crate::validators).
//!
//! ### Custom preprocessors
//!
//! You can use a custom function as a preprocessor. The function must have the
//! following signature:
//!
//! ```rust
//! fn custom_preprocessor<T>(value: T) -> Result<T, Error>;
//! ```
//!
//! The function must return a `Result` with the same type as the input. If the
//! function returns an `Err`, the error will be returned as the error of the
//! preprocessor. If the function returns an `Ok`, the value will be returned as
//! the output of the preprocessor.
//!
//! ```rust
//! pub fn custom_preprocessor(value: String) -> Result<String, Error> {
//! if value.len() < 8 {
//! return Err(Error::new(
//! "Password must be at least 8 characters long",
//! ));
//! }
//! Ok(value)
//! }
//!
//! #[preprocess::sync]
//! #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
//! pub struct UserSignUpRequest {
//! #[preprocess(custom = "custom_preprocessor")]
//! pub password: String,
//! }
//! ```
//!
//! ### Enforcing the type of a value
//!
//! You can use the `type` preprocessor to enforce the type of a value. This is
//! useful when you want to convert a value to a different type. For example,
//! you might want to convert a string to an integer. You can use the `type`
//! preprocessor to do this. The `type` preprocessor uses [`TryFrom`] to convert
//! the value to the desired type. If the conversion fails, the preprocessor
//! will return an error.
//!
//! ```rust
//! #[preprocess::sync]
//! #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
//! pub struct UserSignUpRequest {
//! #[preprocess(type = "i32")]
//! pub age: i16,
//! }
//! ```
//!
//! ## Usage
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! preprocess = "<version>"
//! ```
//!
//! Then, you can import the crate and use it like this:
//!
//! ```rust
//! use preprocess::prelude::*;
//!
//! #[preprocess::sync]
//! #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
//! pub struct UserSignUpRequest {
//! // First trims the email, then converts it to lowercase, then validates it as an email address.
//! #[preprocess(trim, lowercase, email)]
//! pub email: String,
//! // First trims the password, then checks if it's at least 8 characters long.
//! #[preprocess(trim, length(min = 8))]
//! pub password: String,
//! }
//! ```
//!
//! ## MSRV
//!
//! There is no MSRV as such, and to be honest, I don't see the point of an
//! MSRV, with how easy rust is to upgrade. I just use the latest version of
//! rust on my machine. That being said, I don't think I've used any new rust
//! features. So it should work on older versions of rust as well. Please open
//! an [issue](https://github.com/patr-cloud/preprocess/issues) if you're facing
//! any, well, issues.
/// An attribute macro for preprocessing structs
pub use sync;
/// Error type for the library. This type is used to return errors from the
/// preprocessors and validators.
pub use crate Error;
/// List of all the preprocessors that mutates the given field, including
/// changing the type if required.
/// Utility module for the library.
/// List of all the validators that validates the given field without mutating
/// it. The type of the field may still be changed. For example, the
/// [`ip`](crate::validators::validate_ip) validator will change the type
/// of the field to [`IpAddr`](std::net::IpAddr).
/// Prelude module for the library. This module re-exports all the important
/// types and traits from the library. This module is useful when you want to
/// use the library without importing the individual modules.
///
/// # Example
/// ```rust
/// use preprocess::prelude::*;
///
/// #[preprocess::sync]
/// #[derive(Debug, Deserialize, Serialize)]
/// pub struct LoginRequest {
/// #[preprocess(email)]
/// pub email: String,
/// #[preprocess(custom = "validate_password")]
/// pub password: String,
/// }
/// ```
/// A list of all the types that are re-exported from supporting crates. Used by
/// the preprocessor to set the types for a field if required.
/// A trait that can be implemented by any type to allow it to be preprocessed.
/// This trait is automatically implemented for all types that use the
/// `#[preprocess::sync]` macro.