├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml ├── src ├── base32 │ └── mod.rs ├── base64 │ └── mod.rs ├── base64_url │ └── mod.rs ├── boolean │ └── mod.rs ├── domain │ └── mod.rs ├── email │ └── mod.rs ├── host │ └── mod.rs ├── http_ftp_url │ └── mod.rs ├── http_url │ └── mod.rs ├── integer │ └── mod.rs ├── ipv4 │ └── mod.rs ├── ipv6 │ └── mod.rs ├── json │ └── mod.rs ├── json_array │ └── mod.rs ├── json_object │ └── mod.rs ├── lib.rs ├── line │ └── mod.rs ├── mac_address │ └── mod.rs ├── macro_validated_customized_hash_set.rs ├── macro_validated_customized_number.rs ├── macro_validated_customized_phone_number.rs ├── macro_validated_customized_string.rs ├── macro_validated_customized_vec.rs ├── number │ └── mod.rs ├── short_crypt_qr_code_alphanumeric │ └── mod.rs ├── short_crypt_url_component │ └── mod.rs ├── text │ └── mod.rs ├── uri │ └── mod.rs ├── uuid │ └── mod.rs ├── validator_option.rs └── version │ └── mod.rs └── tests ├── macros.rs ├── rocket.rs └── serde.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij+all 2 | 3 | ### Intellij+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | ### Intellij+all Patch ### 66 | # Ignores the whole .idea folder and all .iml files 67 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 68 | 69 | .idea/ 70 | 71 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 72 | 73 | *.iml 74 | modules.xml 75 | .idea/misc.xml 76 | *.ipr 77 | 78 | 79 | # End of https://www.gitignore.io/api/intellij+all 80 | 81 | 82 | ### Rust ### 83 | # Generated by Cargo 84 | # will have compiled files and executables 85 | /target/ 86 | 87 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 88 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 89 | Cargo.lock 90 | 91 | # These are backup files generated by rustfmt 92 | **/*.rs.bk 93 | 94 | 95 | # End of https://www.gitignore.io/api/rust 96 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | os: 9 | - linux 10 | - osx 11 | - windows 12 | 13 | matrix: 14 | include: 15 | - rust: stable 16 | os: linux 17 | env: TARGET=x86_64-unknown-linux-musl 18 | install: rustup target add $TARGET 19 | script: cargo test --target $TARGET 20 | - rust: beta 21 | os: linux 22 | env: TARGET=x86_64-unknown-linux-musl 23 | install: rustup target add $TARGET 24 | script: cargo test --target $TARGET 25 | - rust: nightly 26 | os: linux 27 | env: TARGET=x86_64-unknown-linux-musl 28 | install: rustup target add $TARGET 29 | script: cargo test --target $TARGET 30 | - rust: stable 31 | os: windows 32 | env: TARGET=x86_64-pc-windows-gnu 33 | install: 34 | - rustup set default-host $TARGET 35 | - rustup default $TRAVIS_RUST_VERSION 36 | - rustup target add $TARGET 37 | - mkdir -p ~/.cargo 38 | - printf '\n[target.'$TARGET']\nlinker = "x86_64-w64-mingw32-gcc"\nar = "x86_64-w64-mingw32-ar"\n' >> ~/.cargo/config 39 | script: cargo test --target $TARGET 40 | - rust: beta 41 | os: windows 42 | env: TARGET=x86_64-pc-windows-gnu 43 | install: 44 | - rustup set default-host $TARGET 45 | - rustup default $TRAVIS_RUST_VERSION 46 | - rustup target add $TARGET 47 | - mkdir -p ~/.cargo 48 | - printf '\n[target.'$TARGET']\nlinker = "x86_64-w64-mingw32-gcc"\nar = "x86_64-w64-mingw32-ar"\n' >> ~/.cargo/config 49 | script: cargo test --target $TARGET 50 | - rust: nightly 51 | os: windows 52 | env: TARGET=x86_64-pc-windows-gnu 53 | install: 54 | - rustup set default-host $TARGET 55 | - rustup default $TRAVIS_RUST_VERSION 56 | - rustup target add $TARGET 57 | - mkdir -p ~/.cargo 58 | - printf '\n[target.'$TARGET']\nlinker = "x86_64-w64-mingw32-gcc"\nar = "x86_64-w64-mingw32-ar"\n' >> ~/.cargo/config 59 | script: cargo test --target $TARGET -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "validators" 3 | version = "0.20.19" 4 | authors = ["Magic Len "] 5 | edition = "2018" 6 | repository = "https://github.com/magiclen/validators" 7 | homepage = "https://magiclen.org/validators" 8 | keywords = ["validator", "validation", "user", "rocket", "serde"] 9 | categories = ["value-formatting"] 10 | description= "A library for validating user input." 11 | readme = "README.md" 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml"] 14 | 15 | [badges.travis-ci] 16 | repository = "magiclen/validators" 17 | branch = "master" 18 | 19 | [dependencies] 20 | regex = "1.0" 21 | lazy_static = "1.1" 22 | debug-helper = "0.3" 23 | rocket = { version = "0.4.2", optional = true } 24 | serde = { version = "1.0", optional = true } 25 | serde_json = { version = "1.0", optional = true } 26 | phonenumber = { version = "0.2.4", optional = true } 27 | 28 | [dev-dependencies] 29 | assert_approx_eq = "1.1" 30 | 31 | [dependencies.num-traits] 32 | version = "0.2" 33 | features = ["i128"] 34 | 35 | [features] 36 | nightly = [] 37 | rocketly = ["nightly", "rocket"] 38 | rocketly-test = ["rocketly"] 39 | serdely = ["serde", "serde_json"] 40 | serdely-test = ["serdely"] 41 | phone-number = ["phonenumber"] 42 | 43 | [package.metadata.docs.rs] 44 | all-features = true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Validators 2 | ==================== 3 | 4 | [![Build Status](https://travis-ci.org/magiclen/validators.svg?branch=master)](https://travis-ci.org/magiclen/validators) 5 | 6 | This crate provides many validators for validating data from users and modeling them to structs without much extra effort. 7 | 8 | All validators are separated into different modules and unified for two main types: **XXX** and **XXXValidator** where **XXX** is a type that you want to validate. 9 | 10 | The former is a struct or a enum, and the latter is a struct which can be considered as a generator of the former. 11 | 12 | A **XXXValidator** struct usually contains some values of `ValidatorOption` in order to use different rules to check data. 13 | 14 | For example, the mod `domain` has `Domain` and `DomainValidator` structs. If we want to create a `Domain` instance, we need to create a `DomainValidator` instance first. 15 | 16 | When initialing a `DomainValidator`, we can choose to make this `DomainValidator` **allow** or **not allow** the input to have or **must** have a port number. 17 | 18 | ```rust 19 | extern crate validators; 20 | 21 | use validators::ValidatorOption; 22 | use validators::domain::{Domain, DomainValidator}; 23 | 24 | let domain = "tool.magiclen.org:8080".to_string(); 25 | 26 | let dv = DomainValidator { 27 | port: ValidatorOption::Allow, 28 | localhost: ValidatorOption::NotAllow, 29 | }; 30 | 31 | let domain = dv.parse_string(domain).unwrap(); 32 | 33 | assert_eq!("tool.magiclen.org:8080", domain.get_full_domain()); 34 | assert_eq!("tool.magiclen.org", domain.get_full_domain_without_port()); 35 | assert_eq!("org", domain.get_top_level_domain().unwrap()); 36 | assert_eq!("tool", domain.get_sub_domain().unwrap()); 37 | assert_eq!("magiclen", domain.get_domain()); 38 | assert_eq!(8080, domain.get_port().unwrap()); 39 | ``` 40 | 41 | If you want the **XXX** model to be stricter, you can use its wrapper type which is something like **XXXWithPort** or **XXXWithoutPort**. 42 | 43 | For instance, `Domain` has some wrappers, such as **DomainLocalhostableWithPort**, **DomainLocalhostableAllowPort** and **DomainLocalhostableWithoutPort**. 44 | 45 | ```rust 46 | extern crate validators; 47 | 48 | use validators::domain::{DomainLocalhostableWithPort}; 49 | 50 | let domain = "tool.magiclen.org:8080".to_string(); 51 | 52 | let domain = DomainLocalhostableWithPort::from_string(domain).unwrap(); 53 | 54 | assert_eq!("tool.magiclen.org:8080", domain.get_full_domain()); 55 | assert_eq!("tool.magiclen.org", domain.get_full_domain_without_port()); 56 | assert_eq!("org", domain.get_top_level_domain().unwrap()); 57 | assert_eq!("tool", domain.get_sub_domain().unwrap()); 58 | assert_eq!("magiclen", domain.get_domain()); 59 | assert_eq!(8080, domain.get_port()); // This function does not use `Option` as its return value, because the struct `DomainLocalhostableWithPort` has already made sure the input must have a port number! 60 | ``` 61 | 62 | This crate aims to use the simplest and slackest way (normally only use regular expressions) to validate data, in order to minimize the overhead. 63 | 64 | Therefore, it may not be competent in some critical situations. Use it carefully. Check out the documentation to see more useful validators and wrapper types. 65 | 66 | ## Customization 67 | 68 | This crate also provides macros to create customized validated structs for any strings, numbers and Vecs. 69 | 70 | For example, to create a struct which only allows **"Hi"** or **"Hello"** restricted by a regular expression, 71 | 72 | ```rust 73 | #[macro_use] extern crate validators; 74 | 75 | validated_customized_regex_string!(Greet, "^(Hi|Hello)$"); 76 | 77 | let s = Greet::from_str("Hi").unwrap(); 78 | ``` 79 | 80 | While a regex needs to be compiled before it operates, if you want to reuse a compiled regex, you can add a **ref** keyword, and pass a static Regex instance to the macro, 81 | 82 | ```rust 83 | #[macro_use] extern crate validators; 84 | #[macro_use] extern crate lazy_static; 85 | extern crate regex; 86 | 87 | use regex::Regex; 88 | 89 | lazy_static! { 90 | static ref RE_GREET: Regex = { 91 | Regex::new("^(Hi|Hello)$").unwrap() 92 | }; 93 | } 94 | 95 | validated_customized_regex_string!(Greet, ref RE_GREET); 96 | 97 | let s = Greet::from_str("Hi").unwrap(); 98 | ``` 99 | 100 | You can also make your struct public by adding a **pub** keyword, 101 | 102 | ```rust 103 | #[macro_use] extern crate validators; 104 | 105 | validated_customized_regex_string!(pub Greet, "^(Hi|Hello)$"); 106 | 107 | let s = Greet::from_str("Hi").unwrap(); 108 | ``` 109 | 110 | For numbers limited in a range, 111 | 112 | ```rust 113 | #[macro_use] extern crate validators; 114 | 115 | validated_customized_ranged_number!(Score, u8, 0, 100); 116 | 117 | let score = Score::from_str("80").unwrap(); 118 | ``` 119 | 120 | For a Vec whose length is limited in a range, 121 | 122 | ```rust 123 | #[macro_use] extern crate validators; 124 | 125 | validated_customized_regex_string!(Name, "^[A-Z][a-zA-Z]*( [A-Z][a-zA-Z]*)*$"); 126 | validated_customized_ranged_length_vec!(Names, 1, 5); 127 | 128 | let mut names = Vec::new(); 129 | 130 | names.push(Name::from_str("Ron").unwrap()); 131 | names.push(Name::from_str("Magic Len").unwrap()); 132 | 133 | let names = Names::from_vec(names).unwrap(); 134 | ``` 135 | 136 | For a HashSet whose length is limited in a range, 137 | 138 | ```rust 139 | #[macro_use] extern crate validators; 140 | 141 | use std::collections::HashSet; 142 | 143 | validated_customized_regex_string!(Name, "^[A-Z][a-zA-Z]*( [A-Z][a-zA-Z]*)*$"); 144 | validated_customized_ranged_length_hash_set!(Names, 1, 5); 145 | 146 | let mut names = HashSet::new(); 147 | 148 | names.insert(Name::from_str("Ron").unwrap()); 149 | names.insert(Name::from_str("Magic Len").unwrap()); 150 | 151 | let names = Names::from_hash_set(names).unwrap(); 152 | ``` 153 | 154 | All validated wrapper types and validated customized structs implement the `ValidatedWrapper` trait. 155 | 156 | Read the documentation to know more helpful customized macros. 157 | 158 | ## Phone Number Support 159 | 160 | This crate supports [phonenumber](https://crates.io/crates/phonenumber) crate. You can create validators for phone numbers by using the `validated_customized_phone_number` macro. 161 | 162 | To use it, you have to enable **phone-number** feature for this crate. 163 | 164 | ```toml 165 | [dependencies.validators] 166 | version = "*" 167 | features = ["phone-number"] 168 | ``` 169 | 170 | For example, 171 | 172 | ```rust 173 | #[macro_use] extern crate validators; 174 | 175 | use validators::PhoneNumberCountry; 176 | 177 | validated_customized_phone_number!(P1, PhoneNumberCountry::TW); 178 | validated_customized_phone_number!(pub P2, PhoneNumberCountry::CN, PhoneNumberCountry::US); 179 | 180 | let phone_number = P1::from_str("0912345678").unwrap(); 181 | assert_eq!("0912345678", phone_number.get_full_phone_number()); 182 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::TW)); 183 | 184 | let phone_number = P2::from_str("626-555-1212").unwrap(); 185 | assert_eq!("626-555-1212", phone_number.get_full_phone_number()); 186 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::US)); 187 | ``` 188 | 189 | ## Rocket Support 190 | 191 | This crate supports [Rocket](https://rocket.rs/) framework. All validated wrapper types and validated customized structs implement the `FromFormValue` and `FromParam` traits. 192 | 193 | To use with Rocket support, you have to enable **rocketly** feature for this crate. 194 | 195 | ```toml 196 | [dependencies.validators] 197 | version = "*" 198 | features = ["rocketly"] 199 | ``` 200 | 201 | For example, 202 | 203 | ```rust 204 | #![feature(plugin)] 205 | #![feature(custom_derive)] 206 | #![plugin(rocket_codegen)] 207 | 208 | #[macro_use] extern crate validators; 209 | 210 | extern crate rocket; 211 | 212 | use rocket::request::Form; 213 | 214 | use validators::http_url::HttpUrlUnlocalableWithProtocol; 215 | use validators::email::Email; 216 | 217 | validated_customized_ranged_number!(PersonID, u8, 0, 100); 218 | validated_customized_regex_string!(Name, r"^[\S ]{1,80}$"); 219 | validated_customized_ranged_number!(PersonAge, u8, 0, 130); 220 | 221 | #[derive(Debug, FromForm)] 222 | struct ContactModel { 223 | name: Name, 224 | age: Option, 225 | email: Email, 226 | url: Option 227 | } 228 | 229 | #[post("/contact/", data = "")] 230 | fn contact(id: PersonID, model: Form) -> &'static str { 231 | println!("{}", id); 232 | println!("{:?}", model.get()); 233 | "do something..." 234 | } 235 | ``` 236 | 237 | ## Serde Support 238 | 239 | Serde is a framework for serializing and deserializing Rust data structures efficiently and generically. And again, this crate supports [Serde](https://crates.io/crates/serde) framework. 240 | 241 | All validated wrapper types and validated customized structs implement the `Serialize` and `Deserialize` traits. 242 | 243 | To use with Serde support, you have to enable **serdely** feature for this crate. 244 | 245 | ```toml 246 | [dependencies.validators] 247 | version = "*" 248 | features = ["serdely"] 249 | ``` 250 | 251 | For example, 252 | 253 | ```rust 254 | #[macro_use] extern crate validators; 255 | #[macro_use] extern crate serde_json; 256 | 257 | validated_customized_regex_string!(Name, "^[A-Z][a-zA-Z]*( [A-Z][a-zA-Z]*)*$"); 258 | validated_customized_ranged_length_vec!(Names, 1, 5); 259 | 260 | let mut names = Vec::new(); 261 | 262 | names.push(Name::from_str("Ron").unwrap()); 263 | names.push(Name::from_str("Magic Len").unwrap()); 264 | 265 | let names = Names::from_vec(names).unwrap(); 266 | 267 | assert_eq!("[\"Ron\",\"Magic Len\"]", json!(names).to_string()); 268 | ``` 269 | 270 | Also, the `json`, `json_array` and `json_object` modules are available. 271 | 272 | ## Crates.io 273 | 274 | https://crates.io/crates/validators 275 | 276 | ## Documentation 277 | 278 | https://docs.rs/validators 279 | 280 | ## License 281 | 282 | [MIT](LICENSE) -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | brace_style = "PreferSameLine" 2 | enum_discrim_align_threshold = 100 3 | force_explicit_abi = false 4 | force_multiline_blocks = true 5 | format_code_in_doc_comments = true 6 | format_macro_matchers = true 7 | max_width = 100 8 | newline_style = "Unix" 9 | normalize_doc_attributes = true 10 | overflow_delimited_expr = true 11 | reorder_impl_items = true 12 | struct_lit_single_line = false 13 | use_field_init_shorthand = true 14 | use_small_heuristics = "Off" 15 | use_try_shorthand = true -------------------------------------------------------------------------------- /src/base32/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::FromStr; 10 | 11 | lazy_static! { 12 | static ref BASE32_RE: Regex = { 13 | Regex::new("^([A-Z2-7]{8})*(([A-Z2-7]{8})|([A-Z2-7]{7}=)|([A-Z2-7]{5}===)|([A-Z2-7]{4}====)|([A-Z2-7]{2}======))$").unwrap() 14 | }; 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone)] 18 | pub enum Base32Error { 19 | IncorrectFormat, 20 | } 21 | 22 | impl Display for Base32Error { 23 | #[inline] 24 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 25 | Debug::fmt(self, f) 26 | } 27 | } 28 | 29 | impl Error for Base32Error {} 30 | 31 | pub type Base32Result = Result; 32 | 33 | #[derive(Debug, PartialEq)] 34 | pub struct Base32Validator {} 35 | 36 | #[derive(Clone, PartialEq, Eq, Hash)] 37 | pub struct Base32 { 38 | base32: String, 39 | } 40 | 41 | impl Base32 { 42 | #[inline] 43 | pub fn get_base32(&self) -> &str { 44 | &self.base32 45 | } 46 | 47 | #[inline] 48 | pub fn into_string(self) -> String { 49 | self.base32 50 | } 51 | 52 | #[allow(clippy::missing_safety_doc)] 53 | #[inline] 54 | pub unsafe fn from_string_unchecked(base32: String) -> Base32 { 55 | Base32 { 56 | base32, 57 | } 58 | } 59 | } 60 | 61 | impl Deref for Base32 { 62 | type Target = str; 63 | 64 | #[inline] 65 | fn deref(&self) -> &Self::Target { 66 | &self.base32 67 | } 68 | } 69 | 70 | impl Validated for Base32 {} 71 | 72 | impl Debug for Base32 { 73 | #[inline] 74 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 75 | impl_debug_for_tuple_struct!(Base32, f, self, let .0 = self.base32); 76 | } 77 | } 78 | 79 | impl Display for Base32 { 80 | #[inline] 81 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 82 | f.write_str(&self.base32)?; 83 | Ok(()) 84 | } 85 | } 86 | 87 | impl Base32Validator { 88 | #[inline] 89 | pub fn is_base32(&self, base32: &str) -> bool { 90 | self.parse_inner(base32).is_ok() 91 | } 92 | 93 | pub fn parse_string(&self, base32: String) -> Base32Result { 94 | let mut base32_inner = self.parse_inner(&base32)?; 95 | 96 | base32_inner.base32 = base32; 97 | 98 | Ok(base32_inner) 99 | } 100 | 101 | pub fn parse_str(&self, base32: &str) -> Base32Result { 102 | let mut base32_inner = self.parse_inner(base32)?; 103 | 104 | base32_inner.base32.push_str(base32); 105 | 106 | Ok(base32_inner) 107 | } 108 | 109 | #[inline] 110 | fn parse_inner(&self, base32: &str) -> Base32Result { 111 | if BASE32_RE.is_match(base32) { 112 | Ok(Base32 { 113 | base32: String::new(), 114 | }) 115 | } else { 116 | Err(Base32Error::IncorrectFormat) 117 | } 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod tests { 123 | use super::*; 124 | 125 | #[test] 126 | fn test_base32_methods() { 127 | let base32 = "EB2GK43UEBWWK43TMFTWKCQK".to_string(); 128 | 129 | let bv = Base32Validator {}; 130 | 131 | let base32 = bv.parse_string(base32).unwrap(); 132 | 133 | assert_eq!("EB2GK43UEBWWK43TMFTWKCQK", base32.get_base32()); 134 | } 135 | 136 | #[test] 137 | fn test_base32_lv1() { 138 | let base32 = "EB2GK43UEBWWK43TMFTWKCQK".to_string(); 139 | 140 | let bv = Base32Validator {}; 141 | 142 | bv.parse_string(base32).unwrap(); 143 | } 144 | } 145 | 146 | // Base32's wrapper struct is itself 147 | impl ValidatedWrapper for Base32 { 148 | type Error = Base32Error; 149 | 150 | #[inline] 151 | fn from_string(base32: String) -> Result { 152 | Base32::from_string(base32) 153 | } 154 | 155 | #[inline] 156 | fn from_str(base32: &str) -> Result { 157 | Base32::from_str(base32) 158 | } 159 | } 160 | 161 | impl Base32 { 162 | #[inline] 163 | pub fn from_string(base32: String) -> Result { 164 | Base32::create_validator().parse_string(base32) 165 | } 166 | 167 | #[inline] 168 | #[allow(clippy::should_implement_trait)] 169 | pub fn from_str(base32: &str) -> Result { 170 | Base32::create_validator().parse_str(base32) 171 | } 172 | 173 | #[inline] 174 | fn create_validator() -> Base32Validator { 175 | Base32Validator {} 176 | } 177 | } 178 | 179 | impl FromStr for Base32 { 180 | type Err = Base32Error; 181 | 182 | #[inline] 183 | fn from_str(s: &str) -> Result { 184 | Base32::from_str(s) 185 | } 186 | } 187 | 188 | #[cfg(feature = "rocketly")] 189 | impl<'a> ::rocket::request::FromParam<'a> for Base32 { 190 | type Error = Base32Error; 191 | 192 | #[inline] 193 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 194 | Base32::from_str(param) 195 | } 196 | } 197 | 198 | #[cfg(feature = "rocketly")] 199 | impl<'a> ::rocket::request::FromFormValue<'a> for Base32 { 200 | type Error = Base32Error; 201 | 202 | #[inline] 203 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 204 | Base32::from_str(form_value) 205 | } 206 | } 207 | 208 | #[cfg(feature = "serdely")] 209 | struct StringVisitor; 210 | 211 | #[cfg(feature = "serdely")] 212 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 213 | type Value = Base32; 214 | 215 | #[inline] 216 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 217 | formatter.write_str("a Base32 string") 218 | } 219 | 220 | #[inline] 221 | fn visit_str(self, v: &str) -> Result 222 | where 223 | E: ::serde::de::Error, { 224 | Base32::from_str(v).map_err(|err| E::custom(err.to_string())) 225 | } 226 | 227 | #[inline] 228 | fn visit_string(self, v: String) -> Result 229 | where 230 | E: ::serde::de::Error, { 231 | Base32::from_string(v).map_err(|err| E::custom(err.to_string())) 232 | } 233 | } 234 | 235 | #[cfg(feature = "serdely")] 236 | impl<'de> ::serde::Deserialize<'de> for Base32 { 237 | #[inline] 238 | fn deserialize(deserializer: D) -> Result 239 | where 240 | D: ::serde::Deserializer<'de>, { 241 | deserializer.deserialize_str(StringVisitor) 242 | } 243 | } 244 | 245 | #[cfg(feature = "serdely")] 246 | impl ::serde::Serialize for Base32 { 247 | #[inline] 248 | fn serialize(&self, serializer: S) -> Result 249 | where 250 | S: ::serde::Serializer, { 251 | serializer.serialize_str(&self.base32) 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/base64/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::{FromStr, Utf8Error}; 10 | 11 | lazy_static! { 12 | static ref BASE64_RE: Regex = { 13 | Regex::new( 14 | "^([A-Za-z0-9+/]{4})*(([A-Za-z0-9+/]{4})|([A-Za-z0-9+/]{3}=)|([A-Za-z0-9+/]{2}==))$", 15 | ) 16 | .unwrap() 17 | }; 18 | } 19 | 20 | #[derive(Debug, PartialEq, Clone)] 21 | pub enum Base64Error { 22 | IncorrectFormat, 23 | UTF8Error(Utf8Error), 24 | } 25 | 26 | impl Display for Base64Error { 27 | #[inline] 28 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 29 | Debug::fmt(self, f) 30 | } 31 | } 32 | 33 | impl Error for Base64Error {} 34 | 35 | impl From for Base64Error { 36 | #[inline] 37 | fn from(err: Utf8Error) -> Self { 38 | Base64Error::UTF8Error(err) 39 | } 40 | } 41 | 42 | pub type Base64Result = Result; 43 | 44 | #[derive(Debug, PartialEq)] 45 | pub struct Base64Validator {} 46 | 47 | #[derive(Clone, PartialEq, Eq, Hash)] 48 | pub struct Base64 { 49 | base64: String, 50 | } 51 | 52 | impl Base64 { 53 | #[inline] 54 | pub fn get_base64(&self) -> &str { 55 | &self.base64 56 | } 57 | 58 | #[inline] 59 | pub fn into_string(self) -> String { 60 | self.base64 61 | } 62 | 63 | #[allow(clippy::missing_safety_doc)] 64 | #[inline] 65 | pub unsafe fn from_string_unchecked(base64: String) -> Base64 { 66 | Base64 { 67 | base64, 68 | } 69 | } 70 | } 71 | 72 | impl Deref for Base64 { 73 | type Target = str; 74 | 75 | #[inline] 76 | fn deref(&self) -> &Self::Target { 77 | &self.base64 78 | } 79 | } 80 | 81 | impl Validated for Base64 {} 82 | 83 | impl Debug for Base64 { 84 | #[inline] 85 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 86 | impl_debug_for_tuple_struct!(Base64, f, self, let .0 = self.base64); 87 | } 88 | } 89 | 90 | impl Display for Base64 { 91 | #[inline] 92 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 93 | f.write_str(&self.base64)?; 94 | Ok(()) 95 | } 96 | } 97 | 98 | impl Base64Validator { 99 | #[inline] 100 | pub fn is_base64(&self, base64: &str) -> bool { 101 | self.parse_inner(base64).is_ok() 102 | } 103 | 104 | #[inline] 105 | pub fn parse_string(&self, base64: String) -> Base64Result { 106 | let mut base64_inner = self.parse_inner(&base64)?; 107 | 108 | base64_inner.base64 = base64; 109 | 110 | Ok(base64_inner) 111 | } 112 | 113 | #[inline] 114 | pub fn parse_str(&self, base64: &str) -> Base64Result { 115 | let mut base64_inner = self.parse_inner(base64)?; 116 | 117 | base64_inner.base64.push_str(base64); 118 | 119 | Ok(base64_inner) 120 | } 121 | 122 | #[inline] 123 | fn parse_inner(&self, base64: &str) -> Base64Result { 124 | if BASE64_RE.is_match(base64) { 125 | Ok(Base64 { 126 | base64: String::new(), 127 | }) 128 | } else { 129 | Err(Base64Error::IncorrectFormat) 130 | } 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | mod tests { 136 | use super::*; 137 | 138 | #[test] 139 | fn test_base64_methods() { 140 | let base64 = "IHRlc3QgbWVzc2FnZQoK".to_string(); 141 | 142 | let bv = Base64Validator {}; 143 | 144 | let base64 = bv.parse_string(base64).unwrap(); 145 | 146 | assert_eq!("IHRlc3QgbWVzc2FnZQoK", base64.get_base64()); 147 | } 148 | 149 | #[test] 150 | fn test_base64_lv1() { 151 | let base64 = "IHRlc3QgbWVzc2FnZQoK".to_string(); 152 | 153 | let bv = Base64Validator {}; 154 | 155 | bv.parse_string(base64).unwrap(); 156 | } 157 | } 158 | 159 | // Base64's wrapper struct is itself 160 | impl ValidatedWrapper for Base64 { 161 | type Error = Base64Error; 162 | 163 | #[inline] 164 | fn from_string(base64: String) -> Result { 165 | Base64::from_string(base64) 166 | } 167 | 168 | #[inline] 169 | fn from_str(base64: &str) -> Result { 170 | Base64::from_str(base64) 171 | } 172 | } 173 | 174 | impl Base64 { 175 | #[inline] 176 | pub fn from_string(base64: String) -> Result { 177 | Base64::create_validator().parse_string(base64) 178 | } 179 | 180 | #[inline] 181 | #[allow(clippy::should_implement_trait)] 182 | pub fn from_str(base64: &str) -> Result { 183 | Base64::create_validator().parse_str(base64) 184 | } 185 | 186 | fn create_validator() -> Base64Validator { 187 | Base64Validator {} 188 | } 189 | } 190 | 191 | impl FromStr for Base64 { 192 | type Err = Base64Error; 193 | 194 | #[inline] 195 | fn from_str(s: &str) -> Result { 196 | Base64::from_str(s) 197 | } 198 | } 199 | 200 | #[cfg(feature = "rocketly")] 201 | impl<'a> ::rocket::request::FromFormValue<'a> for Base64 { 202 | type Error = Base64Error; 203 | 204 | #[inline] 205 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 206 | Base64::from_string(form_value.url_decode()?) 207 | } 208 | } 209 | 210 | #[cfg(feature = "rocketly")] 211 | impl<'a> ::rocket::request::FromParam<'a> for Base64 { 212 | type Error = Base64Error; 213 | 214 | #[inline] 215 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 216 | Base64::from_string(param.url_decode()?) 217 | } 218 | } 219 | 220 | #[cfg(feature = "serdely")] 221 | struct StringVisitor; 222 | 223 | #[cfg(feature = "serdely")] 224 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 225 | type Value = Base64; 226 | 227 | #[inline] 228 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 229 | formatter.write_str("a Base64 string") 230 | } 231 | 232 | #[inline] 233 | fn visit_str(self, v: &str) -> Result 234 | where 235 | E: ::serde::de::Error, { 236 | Base64::from_str(v).map_err(|err| E::custom(err.to_string())) 237 | } 238 | 239 | #[inline] 240 | fn visit_string(self, v: String) -> Result 241 | where 242 | E: ::serde::de::Error, { 243 | Base64::from_string(v).map_err(|err| E::custom(err.to_string())) 244 | } 245 | } 246 | 247 | #[cfg(feature = "serdely")] 248 | impl<'de> ::serde::Deserialize<'de> for Base64 { 249 | #[inline] 250 | fn deserialize(deserializer: D) -> Result 251 | where 252 | D: ::serde::Deserializer<'de>, { 253 | deserializer.deserialize_string(StringVisitor) 254 | } 255 | } 256 | 257 | #[cfg(feature = "serdely")] 258 | impl ::serde::Serialize for Base64 { 259 | #[inline] 260 | fn serialize(&self, serializer: S) -> Result 261 | where 262 | S: ::serde::Serializer, { 263 | serializer.serialize_str(&self.base64) 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/base64_url/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::FromStr; 10 | 11 | lazy_static! { 12 | static ref BASE64_URL_RE: Regex = 13 | Regex::new(r"^([A-Za-z0-9\-_]{4})*[A-Za-z0-9\-_]{2,4}$").unwrap(); 14 | } 15 | 16 | #[derive(Debug, PartialEq, Clone)] 17 | pub enum Base64UrlError { 18 | IncorrectFormat, 19 | } 20 | 21 | impl Display for Base64UrlError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for Base64UrlError {} 29 | 30 | pub type Base64UrlResult = Result; 31 | 32 | #[derive(Debug, PartialEq)] 33 | pub struct Base64UrlValidator {} 34 | 35 | #[derive(Clone, PartialEq, Eq, Hash)] 36 | pub struct Base64Url { 37 | base64_url: String, 38 | } 39 | 40 | impl Base64Url { 41 | #[inline] 42 | pub fn get_base64_url(&self) -> &str { 43 | &self.base64_url 44 | } 45 | 46 | #[inline] 47 | pub fn into_string(self) -> String { 48 | self.base64_url 49 | } 50 | 51 | #[allow(clippy::missing_safety_doc)] 52 | #[inline] 53 | pub unsafe fn from_string_unchecked(base64_url: String) -> Base64Url { 54 | Base64Url { 55 | base64_url, 56 | } 57 | } 58 | } 59 | 60 | impl Deref for Base64Url { 61 | type Target = str; 62 | 63 | #[inline] 64 | fn deref(&self) -> &Self::Target { 65 | &self.base64_url 66 | } 67 | } 68 | 69 | impl Validated for Base64Url {} 70 | 71 | impl Debug for Base64Url { 72 | #[inline] 73 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 74 | impl_debug_for_tuple_struct!(Base64Url, f, self, let .0 = self.base64_url); 75 | } 76 | } 77 | 78 | impl Display for Base64Url { 79 | #[inline] 80 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 81 | f.write_str(&self.base64_url)?; 82 | Ok(()) 83 | } 84 | } 85 | 86 | impl Base64UrlValidator { 87 | #[inline] 88 | pub fn is_base64_url(&self, base64_url: &str) -> bool { 89 | self.parse_inner(base64_url).is_ok() 90 | } 91 | 92 | #[inline] 93 | pub fn parse_string(&self, base64_url: String) -> Base64UrlResult { 94 | let mut base64_url_inner = self.parse_inner(&base64_url)?; 95 | 96 | base64_url_inner.base64_url = base64_url; 97 | 98 | Ok(base64_url_inner) 99 | } 100 | 101 | #[inline] 102 | pub fn parse_str(&self, base64_url: &str) -> Base64UrlResult { 103 | let mut base64_url_inner = self.parse_inner(base64_url)?; 104 | 105 | base64_url_inner.base64_url.push_str(base64_url); 106 | 107 | Ok(base64_url_inner) 108 | } 109 | 110 | #[inline] 111 | fn parse_inner(&self, base64_url: &str) -> Base64UrlResult { 112 | if BASE64_URL_RE.is_match(base64_url) { 113 | Ok(Base64Url { 114 | base64_url: String::new(), 115 | }) 116 | } else { 117 | Err(Base64UrlError::IncorrectFormat) 118 | } 119 | } 120 | } 121 | 122 | #[cfg(test)] 123 | mod tests { 124 | use super::*; 125 | 126 | #[test] 127 | fn test_base64_url_methods() { 128 | let base64_url = "YXJ0aWNsZXM".to_string(); 129 | 130 | let bv = Base64UrlValidator {}; 131 | 132 | let base64_url = bv.parse_string(base64_url).unwrap(); 133 | 134 | assert_eq!("YXJ0aWNsZXM", base64_url.get_base64_url()); 135 | } 136 | 137 | #[test] 138 | fn test_base64_url_lv1() { 139 | let base64_url = "YXJ0aWNsZXM".to_string(); 140 | 141 | let bv = Base64UrlValidator {}; 142 | 143 | bv.parse_string(base64_url).unwrap(); 144 | } 145 | } 146 | 147 | // Base64Url's wrapper struct is itself 148 | impl ValidatedWrapper for Base64Url { 149 | type Error = Base64UrlError; 150 | 151 | #[inline] 152 | fn from_string(base64_url: String) -> Result { 153 | Base64Url::from_string(base64_url) 154 | } 155 | 156 | #[inline] 157 | fn from_str(base64_url: &str) -> Result { 158 | Base64Url::from_str(base64_url) 159 | } 160 | } 161 | 162 | impl Base64Url { 163 | #[inline] 164 | pub fn from_string(base64_url: String) -> Result { 165 | Base64Url::create_validator().parse_string(base64_url) 166 | } 167 | 168 | #[inline] 169 | #[allow(clippy::should_implement_trait)] 170 | pub fn from_str(base64_url: &str) -> Result { 171 | Base64Url::create_validator().parse_str(base64_url) 172 | } 173 | 174 | #[inline] 175 | fn create_validator() -> Base64UrlValidator { 176 | Base64UrlValidator {} 177 | } 178 | } 179 | 180 | impl FromStr for Base64Url { 181 | type Err = Base64UrlError; 182 | 183 | #[inline] 184 | fn from_str(s: &str) -> Result { 185 | Base64Url::from_str(s) 186 | } 187 | } 188 | 189 | #[cfg(feature = "rocketly")] 190 | impl<'a> ::rocket::request::FromFormValue<'a> for Base64Url { 191 | type Error = Base64UrlError; 192 | 193 | #[inline] 194 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 195 | Base64Url::from_str(form_value) 196 | } 197 | } 198 | 199 | #[cfg(feature = "rocketly")] 200 | impl<'a> ::rocket::request::FromParam<'a> for Base64Url { 201 | type Error = Base64UrlError; 202 | 203 | #[inline] 204 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 205 | Base64Url::from_str(param) 206 | } 207 | } 208 | 209 | #[cfg(feature = "serdely")] 210 | struct StringVisitor; 211 | 212 | #[cfg(feature = "serdely")] 213 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 214 | type Value = Base64Url; 215 | 216 | #[inline] 217 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 218 | formatter.write_str("a Base64-URL string") 219 | } 220 | 221 | #[inline] 222 | fn visit_str(self, v: &str) -> Result 223 | where 224 | E: ::serde::de::Error, { 225 | Base64Url::from_str(v).map_err(|err| E::custom(err.to_string())) 226 | } 227 | 228 | #[inline] 229 | fn visit_string(self, v: String) -> Result 230 | where 231 | E: ::serde::de::Error, { 232 | Base64Url::from_string(v).map_err(|err| E::custom(err.to_string())) 233 | } 234 | } 235 | 236 | #[cfg(feature = "serdely")] 237 | impl<'de> ::serde::Deserialize<'de> for Base64Url { 238 | #[inline] 239 | fn deserialize(deserializer: D) -> Result 240 | where 241 | D: ::serde::Deserializer<'de>, { 242 | deserializer.deserialize_string(StringVisitor) 243 | } 244 | } 245 | 246 | #[cfg(feature = "serdely")] 247 | impl ::serde::Serialize for Base64Url { 248 | #[inline] 249 | fn serialize(&self, serializer: S) -> Result 250 | where 251 | S: ::serde::Serializer, { 252 | serializer.serialize_str(&self.base64_url) 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/boolean/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::FromStr; 10 | 11 | lazy_static! { 12 | pub(crate) static ref TRUE_RE: Regex = Regex::new(r"^(?i)true|yes|on|y|t|1$").unwrap(); 13 | pub(crate) static ref FALSE_RE: Regex = Regex::new(r"^(?i)false|no|off|n|f|0$").unwrap(); 14 | } 15 | 16 | #[derive(Debug, PartialEq, Clone)] 17 | pub enum BooleanError { 18 | IncorrectFormat, 19 | } 20 | 21 | impl Display for BooleanError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for BooleanError {} 29 | 30 | pub type BooleanResult = Result; 31 | 32 | #[derive(Debug, PartialEq)] 33 | pub struct BooleanValidator {} 34 | 35 | #[derive(Clone, PartialEq, Eq, Hash)] 36 | pub struct Boolean { 37 | boolean: bool, 38 | } 39 | 40 | impl Boolean { 41 | pub fn get_bool(&self) -> bool { 42 | self.boolean 43 | } 44 | 45 | #[inline] 46 | pub fn from_bool(boolean: bool) -> Boolean { 47 | Boolean { 48 | boolean, 49 | } 50 | } 51 | } 52 | 53 | impl Deref for Boolean { 54 | type Target = bool; 55 | 56 | #[inline] 57 | fn deref(&self) -> &Self::Target { 58 | &self.boolean 59 | } 60 | } 61 | 62 | impl Validated for Boolean {} 63 | 64 | impl Debug for Boolean { 65 | #[inline] 66 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 67 | impl_debug_for_tuple_struct!(Boolean, f, self, let .0 = self.boolean); 68 | } 69 | } 70 | 71 | impl Display for Boolean { 72 | #[inline] 73 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 74 | if self.boolean { 75 | f.write_str("true")?; 76 | } else { 77 | f.write_str("false")?; 78 | } 79 | Ok(()) 80 | } 81 | } 82 | 83 | impl BooleanValidator { 84 | #[inline] 85 | pub fn is_boolean(&self, boolean: &str) -> bool { 86 | self.parse_inner(boolean).is_ok() 87 | } 88 | 89 | #[inline] 90 | pub fn parse_string(&self, boolean: String) -> BooleanResult { 91 | self.parse_inner(&boolean) 92 | } 93 | 94 | #[inline] 95 | pub fn parse_str(&self, boolean: &str) -> BooleanResult { 96 | self.parse_inner(boolean) 97 | } 98 | 99 | #[inline] 100 | fn parse_inner(&self, boolean: &str) -> BooleanResult { 101 | if TRUE_RE.is_match(boolean) { 102 | Ok(Boolean { 103 | boolean: true, 104 | }) 105 | } else if FALSE_RE.is_match(boolean) { 106 | Ok(Boolean { 107 | boolean: false, 108 | }) 109 | } else { 110 | Err(BooleanError::IncorrectFormat) 111 | } 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod tests { 117 | use super::*; 118 | 119 | #[test] 120 | fn test_boolean_methods() { 121 | let boolean = Boolean::from_str("TRUE").unwrap(); 122 | 123 | assert_eq!(true, boolean.get_bool()); 124 | 125 | let boolean = Boolean::from_str("FALSE").unwrap(); 126 | 127 | assert_eq!(false, boolean.get_bool()); 128 | } 129 | 130 | #[test] 131 | fn test_boolean_lv1() { 132 | Boolean::from_str("TRUE").unwrap(); 133 | Boolean::from_str("FALSE").unwrap(); 134 | Boolean::from_str("ON").unwrap(); 135 | Boolean::from_str("OFF").unwrap(); 136 | Boolean::from_str("YES").unwrap(); 137 | Boolean::from_str("NO").unwrap(); 138 | Boolean::from_str("Y").unwrap(); 139 | Boolean::from_str("N").unwrap(); 140 | Boolean::from_str("T").unwrap(); 141 | Boolean::from_str("F").unwrap(); 142 | Boolean::from_str("0").unwrap(); 143 | Boolean::from_str("1").unwrap(); 144 | } 145 | } 146 | 147 | // Boolean's wrapper struct is itself 148 | impl ValidatedWrapper for Boolean { 149 | type Error = BooleanError; 150 | 151 | #[inline] 152 | fn from_string(boolean: String) -> Result { 153 | Boolean::from_string(boolean) 154 | } 155 | 156 | #[inline] 157 | fn from_str(boolean: &str) -> Result { 158 | Boolean::from_str(boolean) 159 | } 160 | } 161 | 162 | impl Boolean { 163 | #[inline] 164 | pub fn from_string(boolean: String) -> Result { 165 | Boolean::create_validator().parse_string(boolean) 166 | } 167 | 168 | #[inline] 169 | #[allow(clippy::should_implement_trait)] 170 | pub fn from_str(boolean: &str) -> Result { 171 | Boolean::create_validator().parse_str(boolean) 172 | } 173 | 174 | #[inline] 175 | fn create_validator() -> BooleanValidator { 176 | BooleanValidator {} 177 | } 178 | } 179 | 180 | impl FromStr for Boolean { 181 | type Err = BooleanError; 182 | 183 | #[inline] 184 | fn from_str(s: &str) -> Result { 185 | Boolean::from_str(s) 186 | } 187 | } 188 | 189 | #[cfg(feature = "rocketly")] 190 | impl<'a> ::rocket::request::FromParam<'a> for Boolean { 191 | type Error = BooleanError; 192 | 193 | #[inline] 194 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 195 | Boolean::from_string(param.to_string()) 196 | } 197 | } 198 | 199 | #[cfg(feature = "rocketly")] 200 | impl<'a> ::rocket::request::FromFormValue<'a> for Boolean { 201 | type Error = BooleanError; 202 | 203 | #[inline] 204 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 205 | Boolean::from_str(form_value.as_str()) 206 | } 207 | } 208 | 209 | #[cfg(feature = "serdely")] 210 | struct StringBooleanVisitor; 211 | 212 | #[cfg(feature = "serdely")] 213 | impl<'de> ::serde::de::Visitor<'de> for StringBooleanVisitor { 214 | type Value = Boolean; 215 | 216 | #[inline] 217 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 218 | formatter.write_str("a Boolean string or a bool value") 219 | } 220 | 221 | #[inline] 222 | fn visit_str(self, v: &str) -> Result 223 | where 224 | E: ::serde::de::Error, { 225 | Boolean::from_str(v).map_err(|err| E::custom(err.to_string())) 226 | } 227 | 228 | #[inline] 229 | fn visit_string(self, v: String) -> Result 230 | where 231 | E: ::serde::de::Error, { 232 | Boolean::from_string(v).map_err(|err| E::custom(err.to_string())) 233 | } 234 | 235 | #[inline] 236 | fn visit_bool(self, v: bool) -> Result 237 | where 238 | E: ::serde::de::Error, { 239 | Ok(Boolean::from_bool(v)) 240 | } 241 | } 242 | 243 | #[cfg(feature = "serdely")] 244 | impl<'de> ::serde::Deserialize<'de> for Boolean { 245 | #[inline] 246 | fn deserialize(deserializer: D) -> Result 247 | where 248 | D: ::serde::Deserializer<'de>, { 249 | deserializer.deserialize_any(StringBooleanVisitor) 250 | } 251 | } 252 | 253 | #[cfg(feature = "serdely")] 254 | impl ::serde::Serialize for Boolean { 255 | #[inline] 256 | fn serialize(&self, serializer: S) -> Result 257 | where 258 | S: ::serde::Serializer, { 259 | serializer.serialize_bool(self.boolean) 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/email/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::str::{FromStr, Utf8Error}; 11 | 12 | use super::domain::{Domain, DomainError, DomainUnlocalhostableWithoutPort}; 13 | 14 | lazy_static! { 15 | static ref EMAIL_RE: Regex = { 16 | Regex::new("^(([0-9A-Za-z!#$%&'*+-/=?^_`{|}~&&[^@]]+)|(\"([0-9A-Za-z!#$%&'*+-/=?^_`{|}~ \"(),:;<>@\\[\\\\\\]]+)\"))@").unwrap() 17 | }; 18 | } 19 | 20 | #[derive(Debug, PartialEq, Clone)] 21 | pub enum EmailError { 22 | IncorrectLocalPart, 23 | IncorrectDomainPart(DomainError), 24 | UTF8Error(Utf8Error), 25 | } 26 | 27 | impl Display for EmailError { 28 | #[inline] 29 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 30 | Debug::fmt(self, f) 31 | } 32 | } 33 | 34 | impl Error for EmailError {} 35 | 36 | impl From for EmailError { 37 | #[inline] 38 | fn from(err: DomainError) -> Self { 39 | EmailError::IncorrectDomainPart(err) 40 | } 41 | } 42 | 43 | impl From for EmailError { 44 | #[inline] 45 | fn from(err: Utf8Error) -> Self { 46 | EmailError::UTF8Error(err) 47 | } 48 | } 49 | 50 | pub type EmailResult = Result; 51 | 52 | #[derive(Debug, PartialEq)] 53 | pub struct EmailValidator {} 54 | 55 | #[derive(Clone)] 56 | pub struct Email { 57 | domain: Domain, 58 | domain_index: usize, 59 | full_email: String, 60 | } 61 | 62 | impl Email { 63 | #[inline] 64 | pub fn get_local(&self) -> &str { 65 | &self.full_email[..(self.domain_index - 1)] 66 | } 67 | 68 | #[inline] 69 | pub fn get_domain(&self) -> &Domain { 70 | &self.domain 71 | } 72 | 73 | #[inline] 74 | pub fn get_full_email(&self) -> &str { 75 | &self.full_email 76 | } 77 | 78 | #[inline] 79 | pub fn into_string(self) -> String { 80 | self.full_email 81 | } 82 | } 83 | 84 | impl Deref for Email { 85 | type Target = str; 86 | 87 | #[inline] 88 | fn deref(&self) -> &Self::Target { 89 | &self.full_email 90 | } 91 | } 92 | 93 | impl Validated for Email {} 94 | 95 | impl Debug for Email { 96 | #[inline] 97 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 98 | impl_debug_for_tuple_struct!(Email, f, self, let .0 = self.full_email); 99 | } 100 | } 101 | 102 | impl Display for Email { 103 | #[inline] 104 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 105 | f.write_str(&self.full_email)?; 106 | Ok(()) 107 | } 108 | } 109 | 110 | impl PartialEq for Email { 111 | #[inline] 112 | fn eq(&self, other: &Self) -> bool { 113 | self.full_email.eq(&other.full_email) 114 | } 115 | } 116 | 117 | impl Eq for Email {} 118 | 119 | impl Hash for Email { 120 | #[inline] 121 | fn hash(&self, state: &mut H) { 122 | self.full_email.hash(state); 123 | } 124 | } 125 | 126 | impl EmailValidator { 127 | #[inline] 128 | pub fn is_email(&self, full_email: &str) -> bool { 129 | self.parse_inner(full_email).is_ok() 130 | } 131 | 132 | #[inline] 133 | pub fn parse_string(&self, full_email: String) -> EmailResult { 134 | let mut email_inner = self.parse_inner(&full_email)?; 135 | 136 | email_inner.full_email = full_email; 137 | 138 | Ok(email_inner) 139 | } 140 | 141 | #[inline] 142 | pub fn parse_str(&self, full_email: &str) -> EmailResult { 143 | let mut email_inner = self.parse_inner(full_email)?; 144 | 145 | email_inner.full_email.push_str(full_email); 146 | 147 | Ok(email_inner) 148 | } 149 | 150 | fn parse_inner(&self, full_email: &str) -> EmailResult { 151 | let c = match EMAIL_RE.captures(&full_email) { 152 | Some(c) => c, 153 | None => return Err(EmailError::IncorrectLocalPart), 154 | }; 155 | 156 | let domain_index; 157 | 158 | match c.get(1) { 159 | Some(m) => { 160 | domain_index = m.end() + 1; 161 | 162 | m.start() 163 | } 164 | None => { 165 | unreachable!(); 166 | } 167 | }; 168 | 169 | let duwnp = match DomainUnlocalhostableWithoutPort::from_str(&full_email[domain_index..]) { 170 | Ok(d) => d, 171 | Err(err) => return Err(EmailError::IncorrectDomainPart(err)), 172 | }; 173 | 174 | let domain = duwnp.into_domain(); 175 | 176 | Ok(Email { 177 | domain, 178 | domain_index, 179 | full_email: String::new(), 180 | }) 181 | } 182 | } 183 | 184 | #[cfg(test)] 185 | mod tests { 186 | use super::*; 187 | 188 | #[test] 189 | fn test_email_methods() { 190 | let email = "len@magiclen.org".to_string(); 191 | 192 | let ev = EmailValidator {}; 193 | 194 | let email = ev.parse_string(email).unwrap(); 195 | 196 | assert_eq!("len@magiclen.org", email.get_full_email()); 197 | assert_eq!("len", email.get_local()); 198 | assert_eq!("magiclen.org", email.get_domain().get_full_domain()); 199 | } 200 | 201 | #[test] 202 | fn test_email_lv1() { 203 | let email = "len@magiclen.org".to_string(); 204 | 205 | let ev = EmailValidator {}; 206 | 207 | ev.parse_string(email).unwrap(); 208 | } 209 | } 210 | 211 | // Email's wrapper struct is itself 212 | impl ValidatedWrapper for Email { 213 | type Error = EmailError; 214 | 215 | #[inline] 216 | fn from_string(email: String) -> Result { 217 | Email::from_string(email) 218 | } 219 | 220 | #[inline] 221 | fn from_str(email: &str) -> Result { 222 | Email::from_str(email) 223 | } 224 | } 225 | 226 | impl Email { 227 | #[inline] 228 | pub fn from_string(full_email: String) -> Result { 229 | Email::create_validator().parse_string(full_email) 230 | } 231 | 232 | #[inline] 233 | #[allow(clippy::should_implement_trait)] 234 | pub fn from_str(full_email: &str) -> Result { 235 | Email::create_validator().parse_str(full_email) 236 | } 237 | 238 | #[inline] 239 | fn create_validator() -> EmailValidator { 240 | EmailValidator {} 241 | } 242 | } 243 | 244 | impl FromStr for Email { 245 | type Err = EmailError; 246 | 247 | #[inline] 248 | fn from_str(s: &str) -> Result { 249 | Email::from_str(s) 250 | } 251 | } 252 | 253 | #[cfg(feature = "rocketly")] 254 | impl<'a> ::rocket::request::FromFormValue<'a> for Email { 255 | type Error = EmailError; 256 | 257 | #[inline] 258 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 259 | Email::from_string(form_value.url_decode()?) 260 | } 261 | } 262 | 263 | #[cfg(feature = "rocketly")] 264 | impl<'a> ::rocket::request::FromParam<'a> for Email { 265 | type Error = EmailError; 266 | 267 | #[inline] 268 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 269 | Email::from_string(param.url_decode()?) 270 | } 271 | } 272 | 273 | #[cfg(feature = "serdely")] 274 | struct StringVisitor; 275 | 276 | #[cfg(feature = "serdely")] 277 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 278 | type Value = Email; 279 | 280 | #[inline] 281 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 282 | formatter.write_str("an email string") 283 | } 284 | 285 | #[inline] 286 | fn visit_str(self, v: &str) -> Result 287 | where 288 | E: ::serde::de::Error, { 289 | Email::from_str(v).map_err(|err| E::custom(err.to_string())) 290 | } 291 | 292 | #[inline] 293 | fn visit_string(self, v: String) -> Result 294 | where 295 | E: ::serde::de::Error, { 296 | Email::from_string(v).map_err(|err| E::custom(err.to_string())) 297 | } 298 | } 299 | 300 | #[cfg(feature = "serdely")] 301 | impl<'de> ::serde::Deserialize<'de> for Email { 302 | #[inline] 303 | fn deserialize(deserializer: D) -> Result 304 | where 305 | D: ::serde::Deserializer<'de>, { 306 | deserializer.deserialize_string(StringVisitor) 307 | } 308 | } 309 | 310 | #[cfg(feature = "serdely")] 311 | impl ::serde::Serialize for Email { 312 | #[inline] 313 | fn serialize(&self, serializer: S) -> Result 314 | where 315 | S: ::serde::Serializer, { 316 | serializer.serialize_str(&self.full_email) 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/host/mod.rs: -------------------------------------------------------------------------------- 1 | use super::{Validated, ValidatedWrapper, ValidatorOption}; 2 | 3 | use std::error::Error; 4 | use std::fmt::{self, Debug, Display, Formatter}; 5 | use std::ops::Deref; 6 | use std::str::Utf8Error; 7 | 8 | use super::domain::{Domain, DomainError, DomainValidator}; 9 | use super::ipv4::{IPv4, IPv4Error, IPv4Validator}; 10 | use super::ipv6::{IPv6, IPv6Error, IPv6Validator}; 11 | 12 | #[derive(Debug, PartialEq, Clone)] 13 | pub enum HostError { 14 | Domain(DomainError), 15 | IPv4(IPv4Error), 16 | IPv6(IPv6Error), 17 | NoValidator, 18 | UTF8Error(Utf8Error), 19 | } 20 | 21 | impl Display for HostError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for HostError {} 29 | 30 | impl From for HostError { 31 | #[inline] 32 | fn from(err: DomainError) -> Self { 33 | HostError::Domain(err) 34 | } 35 | } 36 | 37 | impl From for HostError { 38 | #[inline] 39 | fn from(err: IPv4Error) -> Self { 40 | HostError::IPv4(err) 41 | } 42 | } 43 | 44 | impl From for HostError { 45 | #[inline] 46 | fn from(err: IPv6Error) -> Self { 47 | HostError::IPv6(err) 48 | } 49 | } 50 | 51 | impl From for HostError { 52 | #[inline] 53 | fn from(err: Utf8Error) -> Self { 54 | HostError::UTF8Error(err) 55 | } 56 | } 57 | 58 | pub type HostResult = Result; 59 | 60 | #[derive(Debug, PartialEq)] 61 | pub struct HostValidator { 62 | pub domain: Option, 63 | pub ipv4: Option, 64 | pub ipv6: Option, 65 | } 66 | 67 | #[derive(Clone, PartialEq, Eq, Hash)] 68 | pub enum Host { 69 | Domain(Domain), 70 | IPv4(IPv4), 71 | IPv6(IPv6), 72 | } 73 | 74 | impl Host { 75 | #[inline] 76 | pub fn get_full_host(&self) -> &str { 77 | match self { 78 | Host::Domain(d) => d.get_full_domain(), 79 | Host::IPv4(d) => d.get_full_ipv4(), 80 | Host::IPv6(d) => d.get_full_ipv6(), 81 | } 82 | } 83 | 84 | #[inline] 85 | pub fn get_full_host_without_port(&self) -> &str { 86 | match self { 87 | Host::Domain(d) => d.get_full_domain_without_port(), 88 | Host::IPv4(d) => d.get_full_ipv4_without_port(), 89 | Host::IPv6(d) => d.get_full_ipv6_without_port(), 90 | } 91 | } 92 | 93 | #[inline] 94 | pub fn get_port(&self) -> Option { 95 | match self { 96 | Host::Domain(d) => d.get_port(), 97 | Host::IPv4(d) => d.get_port(), 98 | Host::IPv6(d) => d.get_port(), 99 | } 100 | } 101 | 102 | #[inline] 103 | pub fn is_local(&self) -> bool { 104 | match self { 105 | Host::Domain(d) => d.is_localhost(), 106 | Host::IPv4(d) => d.is_local(), 107 | Host::IPv6(d) => d.is_local(), 108 | } 109 | } 110 | 111 | #[inline] 112 | pub fn into_string(self) -> String { 113 | match self { 114 | Host::Domain(d) => d.into_string(), 115 | Host::IPv4(d) => d.into_string(), 116 | Host::IPv6(d) => d.into_string(), 117 | } 118 | } 119 | } 120 | 121 | impl Deref for Host { 122 | type Target = str; 123 | 124 | #[inline] 125 | fn deref(&self) -> &Self::Target { 126 | match self { 127 | Host::Domain(d) => d.get_full_domain(), 128 | Host::IPv4(d) => d.get_full_ipv4(), 129 | Host::IPv6(d) => d.get_full_ipv6(), 130 | } 131 | } 132 | } 133 | 134 | impl Validated for Host {} 135 | 136 | impl Debug for Host { 137 | #[inline] 138 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 139 | impl_debug_for_enum!(Host::{(Domain(v): (let .v = v)), (IPv4(v): (let .v = v)), (IPv6(v): (let .v = v))}, f, self); 140 | } 141 | } 142 | 143 | impl Display for Host { 144 | #[inline] 145 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 146 | match self { 147 | Host::Domain(d) => f.write_str(d.get_domain()), 148 | Host::IPv4(d) => f.write_str(d.get_full_ipv4()), 149 | Host::IPv6(d) => f.write_str(d.get_full_ipv6()), 150 | } 151 | } 152 | } 153 | 154 | impl HostValidator { 155 | #[inline] 156 | pub fn is_host(&self, full_host: &str) -> bool { 157 | self.parse_inner(full_host).is_ok() 158 | } 159 | 160 | #[inline] 161 | pub fn parse_string(&self, full_host: String) -> HostResult { 162 | self.parse_inner(&full_host) 163 | } 164 | 165 | #[inline] 166 | pub fn parse_str(&self, full_host: &str) -> HostResult { 167 | self.parse_inner(full_host) 168 | } 169 | 170 | fn parse_inner(&self, full_host: &str) -> HostResult { 171 | let mut err = Err(HostError::NoValidator); 172 | 173 | if let Some(ref v) = self.ipv4 { 174 | match v.parse_str(full_host) { 175 | Ok(r) => return Ok(Host::IPv4(r)), 176 | Err(e) => { 177 | err = Err(HostError::IPv4(e)); 178 | } 179 | } 180 | } 181 | if let Some(ref v) = self.ipv6 { 182 | match v.parse_str(full_host) { 183 | Ok(r) => return Ok(Host::IPv6(r)), 184 | Err(e) => { 185 | err = Err(HostError::IPv6(e)); 186 | } 187 | } 188 | } 189 | if let Some(ref v) = self.domain { 190 | match v.parse_str(full_host) { 191 | Ok(r) => return Ok(Host::Domain(r)), 192 | Err(e) => { 193 | err = Err(HostError::Domain(e)); 194 | } 195 | } 196 | } 197 | 198 | err 199 | } 200 | } 201 | 202 | #[cfg(test)] 203 | mod tests { 204 | use super::super::ValidatorOption; 205 | use super::*; 206 | 207 | #[test] 208 | fn test_host_methods() { 209 | let domain = "168.17.212.1:8080".to_string(); 210 | 211 | let iv = IPv4Validator { 212 | port: ValidatorOption::Allow, 213 | local: ValidatorOption::NotAllow, 214 | ipv6: ValidatorOption::NotAllow, 215 | }; 216 | 217 | let hv = HostValidator { 218 | domain: None, 219 | ipv4: Some(iv), 220 | ipv6: None, 221 | }; 222 | 223 | let host = hv.parse_string(domain).unwrap(); 224 | 225 | assert_eq!("168.17.212.1:8080", host.get_full_host()); 226 | assert_eq!("168.17.212.1", host.get_full_host_without_port()); 227 | assert_eq!(false, host.is_local()); 228 | } 229 | 230 | #[test] 231 | fn test_host_lv1() { 232 | let domain = "168.17.212.1".to_string(); 233 | 234 | let iv = IPv4Validator { 235 | port: ValidatorOption::NotAllow, 236 | local: ValidatorOption::NotAllow, 237 | ipv6: ValidatorOption::NotAllow, 238 | }; 239 | 240 | let hv = HostValidator { 241 | domain: None, 242 | ipv4: Some(iv), 243 | ipv6: None, 244 | }; 245 | 246 | hv.parse_string(domain).unwrap(); 247 | } 248 | } 249 | 250 | // TODO ---------- 251 | 252 | macro_rules! extend { 253 | ($name:ident, $local:expr) => { 254 | #[derive(Clone, PartialEq, Eq, Hash)] 255 | pub struct $name(Host); 256 | 257 | impl From<$name> for Host { 258 | #[inline] 259 | fn from(d: $name) -> Self { 260 | d.0 261 | } 262 | } 263 | 264 | impl Deref for $name { 265 | type Target = str; 266 | 267 | #[inline] 268 | fn deref(&self) -> &Self::Target { 269 | match &self.0 { 270 | Host::Domain(d) => d.get_full_domain(), 271 | Host::IPv4(d) => d.get_full_ipv4(), 272 | Host::IPv6(d) => d.get_full_ipv6(), 273 | } 274 | } 275 | } 276 | 277 | impl Validated for $name {} 278 | 279 | impl ValidatedWrapper for $name { 280 | type Error = HostError; 281 | 282 | #[inline] 283 | fn from_string(host: String) -> Result { 284 | $name::from_string(host) 285 | } 286 | 287 | #[inline] 288 | fn from_str(host: &str) -> Result { 289 | $name::from_str(host) 290 | } 291 | } 292 | 293 | impl Debug for $name { 294 | #[inline] 295 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 296 | f.write_fmt(format_args!("{}({})", stringify!($name), self.0))?; 297 | Ok(()) 298 | } 299 | } 300 | 301 | impl Display for $name { 302 | #[inline] 303 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 304 | Display::fmt(&self.0, f) 305 | } 306 | } 307 | 308 | impl $name { 309 | #[inline] 310 | pub fn from_string(host: String) -> Result<$name, HostError> { 311 | Ok($name($name::create_validator().parse_string(host)?)) 312 | } 313 | 314 | #[inline] 315 | #[allow(clippy::should_implement_trait)] 316 | pub fn from_str(host: &str) -> Result<$name, HostError> { 317 | Ok($name($name::create_validator().parse_str(host)?)) 318 | } 319 | 320 | pub fn from_host(host: Host) -> Result<$name, HostError> { 321 | { 322 | match &host { 323 | Host::Domain(h) => { 324 | match $local { 325 | ValidatorOption::Must => { 326 | if !h.is_localhost() { 327 | return Err(HostError::Domain( 328 | DomainError::LocalhostNotFound, 329 | )); 330 | } 331 | } 332 | ValidatorOption::NotAllow => { 333 | if h.is_localhost() { 334 | return Err(HostError::Domain( 335 | DomainError::LocalhostNotAllow, 336 | )); 337 | } 338 | } 339 | _ => (), 340 | } 341 | } 342 | Host::IPv4(h) => { 343 | match $local { 344 | ValidatorOption::Must => { 345 | if !h.is_local() { 346 | return Err(HostError::IPv4(IPv4Error::LocalNotFound)); 347 | } 348 | } 349 | ValidatorOption::NotAllow => { 350 | if h.is_local() { 351 | return Err(HostError::IPv4(IPv4Error::LocalNotAllow)); 352 | } 353 | } 354 | _ => (), 355 | } 356 | } 357 | Host::IPv6(h) => { 358 | match $local { 359 | ValidatorOption::Must => { 360 | if !h.is_local() { 361 | return Err(HostError::IPv6(IPv6Error::LocalNotFound)); 362 | } 363 | } 364 | ValidatorOption::NotAllow => { 365 | if h.is_local() { 366 | return Err(HostError::IPv6(IPv6Error::LocalNotAllow)); 367 | } 368 | } 369 | _ => (), 370 | } 371 | } 372 | } 373 | } 374 | 375 | Ok($name(host)) 376 | } 377 | 378 | #[inline] 379 | pub fn into_host(self) -> Host { 380 | self.0 381 | } 382 | 383 | #[inline] 384 | pub fn as_host(&self) -> &Host { 385 | &self.0 386 | } 387 | 388 | #[inline] 389 | fn create_validator() -> HostValidator { 390 | let dv = DomainValidator { 391 | port: ValidatorOption::Allow, 392 | localhost: $local, 393 | }; 394 | 395 | let iv4 = IPv4Validator { 396 | port: ValidatorOption::Allow, 397 | local: $local, 398 | ipv6: ValidatorOption::NotAllow, 399 | }; 400 | 401 | let iv6 = IPv6Validator { 402 | port: ValidatorOption::Allow, 403 | local: $local, 404 | ipv4: ValidatorOption::NotAllow, 405 | }; 406 | 407 | HostValidator { 408 | domain: Some(dv), 409 | ipv4: Some(iv4), 410 | ipv6: Some(iv6), 411 | } 412 | } 413 | } 414 | 415 | impl $name { 416 | #[inline] 417 | pub fn get_full_host(&self) -> &str { 418 | match &self.0 { 419 | Host::Domain(d) => d.get_full_domain(), 420 | Host::IPv4(d) => d.get_full_ipv4(), 421 | Host::IPv6(d) => d.get_full_ipv6(), 422 | } 423 | } 424 | 425 | #[inline] 426 | pub fn get_full_host_without_port(&self) -> &str { 427 | match &self.0 { 428 | Host::Domain(d) => d.get_full_domain_without_port(), 429 | Host::IPv4(d) => d.get_full_ipv4_without_port(), 430 | Host::IPv6(d) => d.get_full_ipv6_without_port(), 431 | } 432 | } 433 | } 434 | 435 | impl std::str::FromStr for $name { 436 | type Err = HostError; 437 | 438 | #[inline] 439 | fn from_str(s: &str) -> Result<$name, HostError> { 440 | $name::from_str(s) 441 | } 442 | } 443 | 444 | #[cfg(feature = "rocketly")] 445 | impl<'a> ::rocket::request::FromFormValue<'a> for $name { 446 | type Error = HostError; 447 | 448 | #[inline] 449 | fn from_form_value( 450 | form_value: &'a ::rocket::http::RawStr, 451 | ) -> Result { 452 | $name::from_string(form_value.url_decode()?) 453 | } 454 | } 455 | 456 | #[cfg(feature = "rocketly")] 457 | impl<'a> ::rocket::request::FromParam<'a> for $name { 458 | type Error = HostError; 459 | 460 | #[inline] 461 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 462 | $name::from_string(param.url_decode()?) 463 | } 464 | } 465 | 466 | #[cfg(feature = "serdely")] 467 | impl<'de> ::serde::Deserialize<'de> for $name { 468 | #[inline] 469 | fn deserialize(deserializer: D) -> Result 470 | where 471 | D: ::serde::Deserializer<'de>, { 472 | struct StringVisitor; 473 | 474 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 475 | type Value = $name; 476 | 477 | #[inline] 478 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 479 | formatter.write_fmt(format_args!( 480 | "a host({:?}) string", 481 | $name::create_validator() 482 | )) 483 | } 484 | 485 | #[inline] 486 | fn visit_str(self, v: &str) -> Result 487 | where 488 | E: ::serde::de::Error, { 489 | $name::from_str(v).map_err(|err| E::custom(err.to_string())) 490 | } 491 | 492 | #[inline] 493 | fn visit_string(self, v: String) -> Result 494 | where 495 | E: ::serde::de::Error, { 496 | $name::from_string(v).map_err(|err| E::custom(err.to_string())) 497 | } 498 | } 499 | 500 | deserializer.deserialize_string(StringVisitor) 501 | } 502 | } 503 | 504 | #[cfg(feature = "serdely")] 505 | impl ::serde::Serialize for $name { 506 | #[inline] 507 | fn serialize(&self, serializer: S) -> Result 508 | where 509 | S: ::serde::Serializer, { 510 | serializer.serialize_str(self.get_full_host()) 511 | } 512 | } 513 | }; 514 | } 515 | 516 | extend!(HostLocalable, ValidatorOption::Allow); 517 | 518 | impl HostLocalable { 519 | #[inline] 520 | pub fn is_local(&self) -> bool { 521 | match &self.0 { 522 | Host::Domain(d) => d.is_localhost(), 523 | Host::IPv4(d) => d.is_local(), 524 | Host::IPv6(d) => d.is_local(), 525 | } 526 | } 527 | 528 | #[inline] 529 | pub fn get_port(&self) -> Option { 530 | match &self.0 { 531 | Host::Domain(d) => d.get_port(), 532 | Host::IPv4(d) => d.get_port(), 533 | Host::IPv6(d) => d.get_port(), 534 | } 535 | } 536 | } 537 | 538 | extend!(HostUnlocalable, ValidatorOption::NotAllow); 539 | 540 | impl HostUnlocalable {} 541 | -------------------------------------------------------------------------------- /src/json/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "serdely")] 2 | extern crate serde_json; 3 | 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::ops::DerefMut; 11 | use std::str::{FromStr, Utf8Error}; 12 | 13 | use self::serde_json::Value; 14 | 15 | #[derive(Debug, PartialEq, Clone)] 16 | pub enum JSONError { 17 | IncorrectJSON, 18 | UTF8Error(Utf8Error), 19 | } 20 | 21 | impl Display for JSONError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for JSONError {} 29 | 30 | impl From for JSONError { 31 | #[inline] 32 | fn from(err: Utf8Error) -> Self { 33 | JSONError::UTF8Error(err) 34 | } 35 | } 36 | 37 | pub type JSONResult = Result; 38 | 39 | #[derive(Debug, PartialEq)] 40 | pub struct JSONValidator {} 41 | 42 | #[derive(Clone)] 43 | pub struct JSON { 44 | value: Value, 45 | full_json: String, 46 | } 47 | 48 | impl JSON { 49 | #[inline] 50 | pub fn get_json_value(&self) -> &Value { 51 | &self.value 52 | } 53 | 54 | #[inline] 55 | pub fn get_full_json(&self) -> &str { 56 | &self.full_json 57 | } 58 | 59 | #[inline] 60 | pub fn into_value(self) -> Value { 61 | self.value 62 | } 63 | 64 | #[inline] 65 | pub fn into_string(self) -> String { 66 | self.full_json 67 | } 68 | } 69 | 70 | impl Deref for JSON { 71 | type Target = Value; 72 | 73 | #[inline] 74 | fn deref(&self) -> &Self::Target { 75 | &self.value 76 | } 77 | } 78 | 79 | impl DerefMut for JSON { 80 | #[inline] 81 | fn deref_mut(&mut self) -> &mut Self::Target { 82 | &mut self.value 83 | } 84 | } 85 | 86 | impl Validated for JSON {} 87 | 88 | impl Debug for JSON { 89 | #[inline] 90 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 91 | impl_debug_for_tuple_struct!(JSON, f, self, let .0 = self.value); 92 | } 93 | } 94 | 95 | impl Display for JSON { 96 | #[inline] 97 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 98 | Display::fmt(&self.value, f) 99 | } 100 | } 101 | 102 | impl PartialEq for JSON { 103 | #[inline] 104 | fn eq(&self, other: &Self) -> bool { 105 | self.full_json.eq(&other.full_json) 106 | } 107 | } 108 | 109 | impl Eq for JSON {} 110 | 111 | impl Hash for JSON { 112 | #[inline] 113 | fn hash(&self, state: &mut H) { 114 | self.full_json.hash(state); 115 | } 116 | } 117 | 118 | impl JSONValidator { 119 | #[inline] 120 | pub fn is_json(&self, full_json: &str) -> bool { 121 | self.parse_inner(full_json).is_ok() 122 | } 123 | 124 | #[inline] 125 | pub fn parse_string(&self, full_json: String) -> JSONResult { 126 | let mut json_inner = self.parse_inner(&full_json)?; 127 | 128 | json_inner.full_json = full_json; 129 | 130 | Ok(json_inner) 131 | } 132 | 133 | #[inline] 134 | pub fn parse_str(&self, full_json: &str) -> JSONResult { 135 | let mut json_inner = self.parse_inner(full_json)?; 136 | 137 | json_inner.full_json.push_str(full_json); 138 | 139 | Ok(json_inner) 140 | } 141 | 142 | #[inline] 143 | fn parse_inner(&self, full_json: &str) -> JSONResult { 144 | let value: Value = match serde_json::from_str(full_json) { 145 | Ok(json) => json, 146 | Err(_) => return Err(JSONError::IncorrectJSON), 147 | }; 148 | 149 | Ok(JSON { 150 | value, 151 | full_json: String::new(), 152 | }) 153 | } 154 | } 155 | 156 | #[cfg(test)] 157 | mod tests { 158 | use super::*; 159 | 160 | #[test] 161 | fn test_json_lv1() { 162 | let json = "123".to_string(); 163 | 164 | let jo = JSONValidator {}; 165 | 166 | jo.parse_string(json).unwrap(); 167 | } 168 | 169 | #[test] 170 | fn test_json_lv2() { 171 | let json = "\"123\"".to_string(); 172 | 173 | let jo = JSONValidator {}; 174 | 175 | jo.parse_string(json).unwrap(); 176 | } 177 | 178 | #[test] 179 | fn test_json_lv3() { 180 | let json = "{\"id\": 1, \"name\": \"Magic Len\"}".to_string(); 181 | 182 | let jo = JSONValidator {}; 183 | 184 | jo.parse_string(json).unwrap(); 185 | } 186 | 187 | #[test] 188 | fn test_json_lv4() { 189 | let json = "[1, \"Magic Len\"]".to_string(); 190 | 191 | let jo = JSONValidator {}; 192 | 193 | jo.parse_string(json).unwrap(); 194 | } 195 | } 196 | 197 | // JSON's wrapper struct is itself 198 | impl ValidatedWrapper for JSON { 199 | type Error = JSONError; 200 | 201 | #[inline] 202 | fn from_string(json: String) -> Result { 203 | JSON::from_string(json) 204 | } 205 | 206 | #[inline] 207 | fn from_str(json: &str) -> Result { 208 | JSON::from_str(json) 209 | } 210 | } 211 | 212 | impl JSON { 213 | #[inline] 214 | pub fn from_string(full_json: String) -> Result { 215 | JSON::create_validator().parse_string(full_json) 216 | } 217 | 218 | #[inline] 219 | #[allow(clippy::should_implement_trait)] 220 | pub fn from_str(full_json: &str) -> Result { 221 | JSON::create_validator().parse_str(full_json) 222 | } 223 | 224 | #[inline] 225 | fn create_validator() -> JSONValidator { 226 | JSONValidator {} 227 | } 228 | } 229 | 230 | impl FromStr for JSON { 231 | type Err = JSONError; 232 | 233 | #[inline] 234 | fn from_str(s: &str) -> Result { 235 | JSON::from_str(s) 236 | } 237 | } 238 | 239 | #[cfg(feature = "rocketly")] 240 | impl<'a> ::rocket::request::FromFormValue<'a> for JSON { 241 | type Error = JSONError; 242 | 243 | #[inline] 244 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 245 | JSON::from_string(form_value.url_decode()?) 246 | } 247 | } 248 | 249 | #[cfg(feature = "rocketly")] 250 | impl<'a> ::rocket::request::FromParam<'a> for JSON { 251 | type Error = JSONError; 252 | 253 | #[inline] 254 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 255 | JSON::from_string(param.url_decode()?) 256 | } 257 | } 258 | 259 | struct StringVisitor; 260 | 261 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 262 | type Value = JSON; 263 | 264 | #[inline] 265 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 266 | formatter.write_str("an JSON string") 267 | } 268 | 269 | #[inline] 270 | fn visit_str(self, v: &str) -> Result 271 | where 272 | E: ::serde::de::Error, { 273 | JSON::from_str(v).map_err(|err| E::custom(err.to_string())) 274 | } 275 | 276 | #[inline] 277 | fn visit_string(self, v: String) -> Result 278 | where 279 | E: ::serde::de::Error, { 280 | JSON::from_string(v).map_err(|err| E::custom(err.to_string())) 281 | } 282 | } 283 | 284 | impl<'de> ::serde::Deserialize<'de> for JSON { 285 | #[inline] 286 | fn deserialize(deserializer: D) -> Result 287 | where 288 | D: ::serde::Deserializer<'de>, { 289 | deserializer.deserialize_string(StringVisitor) 290 | } 291 | } 292 | 293 | impl ::serde::Serialize for JSON { 294 | #[inline] 295 | fn serialize(&self, serializer: S) -> Result 296 | where 297 | S: ::serde::Serializer, { 298 | self.value.serialize(serializer) 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/json_array/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "serdely")] 2 | extern crate serde_json; 3 | 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::ops::DerefMut; 11 | use std::str::{FromStr, Utf8Error}; 12 | 13 | use self::serde_json::Value; 14 | 15 | #[derive(Debug, PartialEq, Clone)] 16 | pub enum JSONArrayError { 17 | IncorrectJSONArray, 18 | UTF8Error(Utf8Error), 19 | } 20 | 21 | impl Display for JSONArrayError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for JSONArrayError {} 29 | 30 | impl From for JSONArrayError { 31 | #[inline] 32 | fn from(err: Utf8Error) -> Self { 33 | JSONArrayError::UTF8Error(err) 34 | } 35 | } 36 | 37 | pub type JSONArrayResult = Result; 38 | 39 | #[derive(Debug, PartialEq)] 40 | pub struct JSONArrayValidator {} 41 | 42 | #[derive(Clone)] 43 | pub struct JSONArray { 44 | value: Value, 45 | full_json_array: String, 46 | } 47 | 48 | impl JSONArray { 49 | #[inline] 50 | pub fn get_json_value(&self) -> &Value { 51 | &self.value 52 | } 53 | 54 | #[inline] 55 | pub fn get_full_json_array(&self) -> &str { 56 | &self.full_json_array 57 | } 58 | 59 | #[inline] 60 | pub fn into_vec(self) -> Vec { 61 | match self.value { 62 | Value::Array(array) => array, 63 | _ => unreachable!(), 64 | } 65 | } 66 | 67 | #[inline] 68 | pub fn into_value(self) -> Value { 69 | self.value 70 | } 71 | 72 | #[inline] 73 | pub fn into_string(self) -> String { 74 | self.full_json_array 75 | } 76 | } 77 | 78 | impl Deref for JSONArray { 79 | type Target = Vec; 80 | 81 | #[inline] 82 | fn deref(&self) -> &Self::Target { 83 | self.value.as_array().unwrap() 84 | } 85 | } 86 | 87 | impl DerefMut for JSONArray { 88 | #[inline] 89 | fn deref_mut(&mut self) -> &mut Self::Target { 90 | self.value.as_array_mut().unwrap() 91 | } 92 | } 93 | 94 | impl Validated for JSONArray {} 95 | 96 | impl Debug for JSONArray { 97 | #[inline] 98 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 99 | impl_debug_for_tuple_struct!(JSONArray, f, self, let .0 = self.value); 100 | } 101 | } 102 | 103 | impl Display for JSONArray { 104 | #[inline] 105 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 106 | Display::fmt(&self.value, f) 107 | } 108 | } 109 | 110 | impl PartialEq for JSONArray { 111 | #[inline] 112 | fn eq(&self, other: &Self) -> bool { 113 | self.full_json_array.eq(&other.full_json_array) 114 | } 115 | } 116 | 117 | impl Eq for JSONArray {} 118 | 119 | impl Hash for JSONArray { 120 | #[inline] 121 | fn hash(&self, state: &mut H) { 122 | self.full_json_array.hash(state) 123 | } 124 | } 125 | 126 | impl JSONArrayValidator { 127 | #[inline] 128 | pub fn is_json_array(&self, full_json_array: &str) -> bool { 129 | self.parse_inner(full_json_array).is_ok() 130 | } 131 | 132 | #[inline] 133 | pub fn parse_string(&self, full_json_array: String) -> JSONArrayResult { 134 | let mut json_array_inner = self.parse_inner(&full_json_array)?; 135 | 136 | json_array_inner.full_json_array = full_json_array; 137 | 138 | Ok(json_array_inner) 139 | } 140 | 141 | #[inline] 142 | pub fn parse_str(&self, full_json_array: &str) -> JSONArrayResult { 143 | let mut json_array_inner = self.parse_inner(full_json_array)?; 144 | 145 | json_array_inner.full_json_array.push_str(full_json_array); 146 | 147 | Ok(json_array_inner) 148 | } 149 | 150 | #[inline] 151 | fn parse_inner(&self, full_json_array: &str) -> JSONArrayResult { 152 | let json_array: Vec = match serde_json::from_str(full_json_array) { 153 | Ok(json_array) => json_array, 154 | Err(_) => return Err(JSONArrayError::IncorrectJSONArray), 155 | }; 156 | 157 | let value = Value::Array(json_array); 158 | 159 | Ok(JSONArray { 160 | value, 161 | full_json_array: String::new(), 162 | }) 163 | } 164 | } 165 | 166 | #[cfg(test)] 167 | mod tests { 168 | use super::*; 169 | 170 | #[test] 171 | fn test_json_array_lv1() { 172 | let json_array = "[1, \"Magic Len\"]".to_string(); 173 | 174 | let jo = JSONArrayValidator {}; 175 | 176 | jo.parse_string(json_array).unwrap(); 177 | } 178 | } 179 | 180 | // JSONArray's wrapper struct is itself 181 | impl ValidatedWrapper for JSONArray { 182 | type Error = JSONArrayError; 183 | 184 | #[inline] 185 | fn from_string(json_array: String) -> Result { 186 | JSONArray::from_string(json_array) 187 | } 188 | 189 | #[inline] 190 | fn from_str(json_array: &str) -> Result { 191 | JSONArray::from_str(json_array) 192 | } 193 | } 194 | 195 | impl JSONArray { 196 | #[inline] 197 | pub fn from_string(full_json_array: String) -> Result { 198 | JSONArray::create_validator().parse_string(full_json_array) 199 | } 200 | 201 | #[inline] 202 | #[allow(clippy::should_implement_trait)] 203 | pub fn from_str(full_json_array: &str) -> Result { 204 | JSONArray::create_validator().parse_str(full_json_array) 205 | } 206 | 207 | fn create_validator() -> JSONArrayValidator { 208 | JSONArrayValidator {} 209 | } 210 | } 211 | 212 | impl FromStr for JSONArray { 213 | type Err = JSONArrayError; 214 | 215 | #[inline] 216 | fn from_str(s: &str) -> Result { 217 | JSONArray::from_str(s) 218 | } 219 | } 220 | 221 | #[cfg(feature = "rocketly")] 222 | impl<'a> ::rocket::request::FromFormValue<'a> for JSONArray { 223 | type Error = JSONArrayError; 224 | 225 | #[inline] 226 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 227 | JSONArray::from_string(form_value.url_decode()?) 228 | } 229 | } 230 | 231 | #[cfg(feature = "rocketly")] 232 | impl<'a> ::rocket::request::FromParam<'a> for JSONArray { 233 | type Error = JSONArrayError; 234 | 235 | #[inline] 236 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 237 | JSONArray::from_string(param.url_decode()?) 238 | } 239 | } 240 | 241 | struct StringVisitor; 242 | 243 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 244 | type Value = JSONArray; 245 | 246 | #[inline] 247 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 248 | formatter.write_str("an JSONArray string") 249 | } 250 | 251 | #[inline] 252 | fn visit_str(self, v: &str) -> Result 253 | where 254 | E: ::serde::de::Error, { 255 | JSONArray::from_str(v).map_err(|err| E::custom(err.to_string())) 256 | } 257 | 258 | #[inline] 259 | fn visit_string(self, v: String) -> Result 260 | where 261 | E: ::serde::de::Error, { 262 | JSONArray::from_string(v).map_err(|err| E::custom(err.to_string())) 263 | } 264 | } 265 | 266 | impl<'de> ::serde::Deserialize<'de> for JSONArray { 267 | #[inline] 268 | fn deserialize(deserializer: D) -> Result 269 | where 270 | D: ::serde::Deserializer<'de>, { 271 | deserializer.deserialize_string(StringVisitor) 272 | } 273 | } 274 | 275 | impl ::serde::Serialize for JSONArray { 276 | #[inline] 277 | fn serialize(&self, serializer: S) -> Result 278 | where 279 | S: ::serde::Serializer, { 280 | self.value.serialize(serializer) 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/json_object/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "serdely")] 2 | extern crate serde_json; 3 | 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::ops::DerefMut; 11 | use std::str::{FromStr, Utf8Error}; 12 | 13 | use self::serde_json::Map; 14 | use self::serde_json::Value; 15 | 16 | #[derive(Debug, PartialEq, Clone)] 17 | pub enum JSONObjectError { 18 | IncorrectJSONObject, 19 | UTF8Error(Utf8Error), 20 | } 21 | 22 | impl Display for JSONObjectError { 23 | #[inline] 24 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 25 | Debug::fmt(self, f) 26 | } 27 | } 28 | 29 | impl Error for JSONObjectError {} 30 | 31 | impl From for JSONObjectError { 32 | #[inline] 33 | fn from(err: Utf8Error) -> Self { 34 | JSONObjectError::UTF8Error(err) 35 | } 36 | } 37 | 38 | pub type JSONObjectResult = Result; 39 | 40 | #[derive(Debug, PartialEq)] 41 | pub struct JSONObjectValidator {} 42 | 43 | #[derive(Clone)] 44 | pub struct JSONObject { 45 | value: Value, 46 | full_json_object: String, 47 | } 48 | 49 | impl JSONObject { 50 | #[inline] 51 | pub fn get_json_value(&self) -> &Value { 52 | &self.value 53 | } 54 | 55 | #[inline] 56 | pub fn get_full_json_object(&self) -> &str { 57 | &self.full_json_object 58 | } 59 | 60 | #[inline] 61 | pub fn into_map(self) -> Map { 62 | match self.value { 63 | Value::Object(map) => map, 64 | _ => unreachable!(), 65 | } 66 | } 67 | 68 | #[inline] 69 | pub fn into_value(self) -> Value { 70 | self.value 71 | } 72 | 73 | #[inline] 74 | pub fn into_string(self) -> String { 75 | self.full_json_object 76 | } 77 | } 78 | 79 | impl Deref for JSONObject { 80 | type Target = Map; 81 | 82 | #[inline] 83 | fn deref(&self) -> &Self::Target { 84 | self.value.as_object().unwrap() 85 | } 86 | } 87 | 88 | impl DerefMut for JSONObject { 89 | #[inline] 90 | fn deref_mut(&mut self) -> &mut Self::Target { 91 | self.value.as_object_mut().unwrap() 92 | } 93 | } 94 | 95 | impl Validated for JSONObject {} 96 | 97 | impl Debug for JSONObject { 98 | #[inline] 99 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 100 | impl_debug_for_tuple_struct!(JSONObject, f, self, let .0 = self.value); 101 | } 102 | } 103 | 104 | impl Display for JSONObject { 105 | #[inline] 106 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 107 | Display::fmt(&self.value, f) 108 | } 109 | } 110 | 111 | impl PartialEq for JSONObject { 112 | #[inline] 113 | fn eq(&self, other: &Self) -> bool { 114 | self.full_json_object.eq(&other.full_json_object) 115 | } 116 | } 117 | 118 | impl Eq for JSONObject {} 119 | 120 | impl Hash for JSONObject { 121 | #[inline] 122 | fn hash(&self, state: &mut H) { 123 | self.full_json_object.hash(state); 124 | } 125 | } 126 | 127 | impl JSONObjectValidator { 128 | #[inline] 129 | pub fn is_json_object(&self, full_json_object: &str) -> bool { 130 | self.parse_inner(full_json_object).is_ok() 131 | } 132 | 133 | #[inline] 134 | pub fn parse_string(&self, full_json_object: String) -> JSONObjectResult { 135 | let mut json_object_inner = self.parse_inner(&full_json_object)?; 136 | 137 | json_object_inner.full_json_object = full_json_object; 138 | 139 | Ok(json_object_inner) 140 | } 141 | 142 | #[inline] 143 | pub fn parse_str(&self, full_json_object: &str) -> JSONObjectResult { 144 | let mut json_object_inner = self.parse_inner(full_json_object)?; 145 | 146 | json_object_inner.full_json_object.push_str(full_json_object); 147 | 148 | Ok(json_object_inner) 149 | } 150 | 151 | #[inline] 152 | fn parse_inner(&self, full_json_object: &str) -> JSONObjectResult { 153 | let json_object: Map = match serde_json::from_str(full_json_object) { 154 | Ok(json_object) => json_object, 155 | Err(_) => return Err(JSONObjectError::IncorrectJSONObject), 156 | }; 157 | 158 | let value = Value::Object(json_object); 159 | 160 | Ok(JSONObject { 161 | value, 162 | full_json_object: String::new(), 163 | }) 164 | } 165 | } 166 | 167 | #[cfg(test)] 168 | mod tests { 169 | use super::*; 170 | 171 | #[test] 172 | fn test_json_object_lv1() { 173 | let json_object = "{\"id\": 1, \"name\": \"Magic Len\"}".to_string(); 174 | 175 | let jo = JSONObjectValidator {}; 176 | 177 | jo.parse_string(json_object).unwrap(); 178 | } 179 | } 180 | 181 | // JSONObject's wrapper struct is itself 182 | impl ValidatedWrapper for JSONObject { 183 | type Error = JSONObjectError; 184 | 185 | #[inline] 186 | fn from_string(json_object: String) -> Result { 187 | JSONObject::from_string(json_object) 188 | } 189 | 190 | #[inline] 191 | fn from_str(json_object: &str) -> Result { 192 | JSONObject::from_str(json_object) 193 | } 194 | } 195 | 196 | impl JSONObject { 197 | #[inline] 198 | pub fn from_string(full_json_object: String) -> Result { 199 | JSONObject::create_validator().parse_string(full_json_object) 200 | } 201 | 202 | #[inline] 203 | #[allow(clippy::should_implement_trait)] 204 | pub fn from_str(full_json_object: &str) -> Result { 205 | JSONObject::create_validator().parse_str(full_json_object) 206 | } 207 | 208 | #[inline] 209 | fn create_validator() -> JSONObjectValidator { 210 | JSONObjectValidator {} 211 | } 212 | } 213 | 214 | impl FromStr for JSONObject { 215 | type Err = JSONObjectError; 216 | 217 | #[inline] 218 | fn from_str(s: &str) -> Result { 219 | JSONObject::from_str(s) 220 | } 221 | } 222 | 223 | #[cfg(feature = "rocketly")] 224 | impl<'a> ::rocket::request::FromFormValue<'a> for JSONObject { 225 | type Error = JSONObjectError; 226 | 227 | #[inline] 228 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 229 | JSONObject::from_string(form_value.url_decode()?) 230 | } 231 | } 232 | 233 | #[cfg(feature = "rocketly")] 234 | impl<'a> ::rocket::request::FromParam<'a> for JSONObject { 235 | type Error = JSONObjectError; 236 | 237 | #[inline] 238 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 239 | JSONObject::from_string(param.url_decode()?) 240 | } 241 | } 242 | 243 | struct StringVisitor; 244 | 245 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 246 | type Value = JSONObject; 247 | 248 | #[inline] 249 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 250 | formatter.write_str("an JSONObject string") 251 | } 252 | 253 | #[inline] 254 | fn visit_str(self, v: &str) -> Result 255 | where 256 | E: ::serde::de::Error, { 257 | JSONObject::from_str(v).map_err(|err| E::custom(err.to_string())) 258 | } 259 | 260 | #[inline] 261 | fn visit_string(self, v: String) -> Result 262 | where 263 | E: ::serde::de::Error, { 264 | JSONObject::from_string(v).map_err(|err| E::custom(err.to_string())) 265 | } 266 | } 267 | 268 | impl<'de> ::serde::Deserialize<'de> for JSONObject { 269 | #[inline] 270 | fn deserialize(deserializer: D) -> Result 271 | where 272 | D: ::serde::Deserializer<'de>, { 273 | deserializer.deserialize_string(StringVisitor) 274 | } 275 | } 276 | 277 | impl ::serde::Serialize for JSONObject { 278 | #[inline] 279 | fn serialize(&self, serializer: S) -> Result 280 | where 281 | S: ::serde::Serializer, { 282 | self.value.serialize(serializer) 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Validators 3 | 4 | This crate provides many validators for validating data from users and modeling them to structs without much extra effort. 5 | 6 | All validators are separated into different modules and unified for two main types: **XXX** and **XXXValidator** where **XXX** is a type that you want to validate. 7 | 8 | The former is a struct or a enum, and the latter is a struct which can be considered as a generator of the former. 9 | 10 | A **XXXValidator** struct usually contains some values of `ValidatorOption` in order to use different rules to check data. 11 | 12 | For example, the mod `domain` has `Domain` and `DomainValidator` structs. If we want to create a `Domain` instance, we need to create a `DomainValidator` instance first. 13 | 14 | When initialing a `DomainValidator`, we can choose to make this `DomainValidator` **allow** or **not allow** the input to have or **must** have a port number. 15 | 16 | ```rust 17 | extern crate validators; 18 | 19 | use validators::ValidatorOption; 20 | use validators::domain::{Domain, DomainValidator}; 21 | 22 | let domain = "tool.magiclen.org:8080".to_string(); 23 | 24 | let dv = DomainValidator { 25 | port: ValidatorOption::Allow, 26 | localhost: ValidatorOption::NotAllow, 27 | }; 28 | 29 | let domain = dv.parse_string(domain).unwrap(); 30 | 31 | assert_eq!("tool.magiclen.org:8080", domain.get_full_domain()); 32 | assert_eq!("tool.magiclen.org", domain.get_full_domain_without_port()); 33 | assert_eq!("org", domain.get_top_level_domain().unwrap()); 34 | assert_eq!("tool", domain.get_sub_domain().unwrap()); 35 | assert_eq!("magiclen", domain.get_domain()); 36 | assert_eq!(8080, domain.get_port().unwrap()); 37 | ``` 38 | 39 | If you want the **XXX** model to be stricter, you can use its wrapper type which is something like **XXXWithPort** or **XXXWithoutPort**. 40 | 41 | For instance, `Domain` has some wrappers, such as **DomainLocalhostableWithPort**, **DomainLocalhostableAllowPort** and **DomainLocalhostableWithoutPort**. 42 | 43 | ```rust 44 | extern crate validators; 45 | 46 | use validators::domain::{DomainLocalhostableWithPort}; 47 | 48 | let domain = "tool.magiclen.org:8080".to_string(); 49 | 50 | let domain = DomainLocalhostableWithPort::from_string(domain).unwrap(); 51 | 52 | assert_eq!("tool.magiclen.org:8080", domain.get_full_domain()); 53 | assert_eq!("tool.magiclen.org", domain.get_full_domain_without_port()); 54 | assert_eq!("org", domain.get_top_level_domain().unwrap()); 55 | assert_eq!("tool", domain.get_sub_domain().unwrap()); 56 | assert_eq!("magiclen", domain.get_domain()); 57 | assert_eq!(8080, domain.get_port()); // This function does not use `Option` as its return value, because the struct `DomainLocalhostableWithPort` has already made sure the input must have a port number! 58 | ``` 59 | 60 | This crate aims to use the simplest and slackest way (normally only use regular expressions) to validate data, in order to minimize the overhead. 61 | 62 | Therefore, it may not be competent in some critical situations. Use it carefully. Check out the documentation to see more useful validators and wrapper types. 63 | 64 | ## Customization 65 | 66 | This crate also provides macros to create customized validated structs for any strings, numbers and Vecs. 67 | 68 | For example, to create a struct which only allows **"Hi"** or **"Hello"** restricted by a regular expression, 69 | 70 | ```rust 71 | #[macro_use] extern crate validators; 72 | 73 | validated_customized_regex_string!(Greet, "^(Hi|Hello)$"); 74 | 75 | let s = Greet::from_str("Hi").unwrap(); 76 | ``` 77 | 78 | While a regex needs to be compiled before it operates, if you want to reuse a compiled regex, you can add a **ref** keyword, and pass a static Regex instance to the macro, 79 | 80 | ```rust 81 | #[macro_use] extern crate validators; 82 | #[macro_use] extern crate lazy_static; 83 | extern crate regex; 84 | 85 | use regex::Regex; 86 | 87 | lazy_static! { 88 | static ref RE_GREET: Regex = { 89 | Regex::new("^(Hi|Hello)$").unwrap() 90 | }; 91 | } 92 | 93 | validated_customized_regex_string!(Greet, ref RE_GREET); 94 | 95 | let s = Greet::from_str("Hi").unwrap(); 96 | ``` 97 | 98 | You can also make your struct public by adding a **pub** keyword, 99 | 100 | ```rust 101 | #[macro_use] extern crate validators; 102 | 103 | validated_customized_regex_string!(pub Greet, "^(Hi|Hello)$"); 104 | 105 | let s = Greet::from_str("Hi").unwrap(); 106 | ``` 107 | 108 | For numbers limited in a range, 109 | 110 | ```rust 111 | #[macro_use] extern crate validators; 112 | 113 | validated_customized_ranged_number!(Score, u8, 0, 100); 114 | 115 | let score = Score::from_str("80").unwrap(); 116 | ``` 117 | 118 | For a Vec whose length is limited in a range, 119 | 120 | ```rust 121 | #[macro_use] extern crate validators; 122 | 123 | validated_customized_regex_string!(Name, "^[A-Z][a-zA-Z]*( [A-Z][a-zA-Z]*)*$"); 124 | validated_customized_ranged_length_vec!(Names, 1, 5); 125 | 126 | let mut names = Vec::new(); 127 | 128 | names.push(Name::from_str("Ron").unwrap()); 129 | names.push(Name::from_str("Magic Len").unwrap()); 130 | 131 | let names = Names::from_vec(names).unwrap(); 132 | ``` 133 | 134 | For a HashSet whose length is limited in a range, 135 | 136 | ```rust 137 | #[macro_use] extern crate validators; 138 | 139 | use std::collections::HashSet; 140 | 141 | validated_customized_regex_string!(Name, "^[A-Z][a-zA-Z]*( [A-Z][a-zA-Z]*)*$"); 142 | validated_customized_ranged_length_hash_set!(Names, 1, 5); 143 | 144 | let mut names = HashSet::new(); 145 | 146 | names.insert(Name::from_str("Ron").unwrap()); 147 | names.insert(Name::from_str("Magic Len").unwrap()); 148 | 149 | let names = Names::from_hash_set(names).unwrap(); 150 | ``` 151 | 152 | All validated wrapper types and validated customized structs implement the `ValidatedWrapper` trait. 153 | 154 | Read the documentation to know more helpful customized macros. 155 | 156 | ## Phone Number Support 157 | 158 | This crate supports [phonenumber](https://crates.io/crates/phonenumber) crate. You can create validators for phone numbers by using the `validated_customized_phone_number` macro. 159 | 160 | To use it, you have to enable **phone-number** feature for this crate. 161 | 162 | ```toml 163 | [dependencies.validators] 164 | version = "*" 165 | features = ["phone-number"] 166 | ``` 167 | 168 | For example, 169 | 170 | ```rust,ignore 171 | #[macro_use] extern crate validators; 172 | 173 | use validators::PhoneNumberCountry; 174 | 175 | validated_customized_phone_number!(P1, PhoneNumberCountry::TW); 176 | validated_customized_phone_number!(pub P2, PhoneNumberCountry::CN, PhoneNumberCountry::US); 177 | 178 | let phone_number = P1::from_str("0912345678").unwrap(); 179 | assert_eq!("0912345678", phone_number.get_full_phone_number()); 180 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::TW)); 181 | 182 | let phone_number = P2::from_str("626-555-1212").unwrap(); 183 | assert_eq!("626-555-1212", phone_number.get_full_phone_number()); 184 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::US)); 185 | ``` 186 | 187 | ## Rocket Support 188 | 189 | This crate supports [Rocket](https://rocket.rs/) framework. All validated wrapper types and validated customized structs implement the `FromFormValue` and `FromParam` traits. 190 | 191 | To use with Rocket support, you have to enable **rocketly** feature for this crate. 192 | 193 | ```toml 194 | [dependencies.validators] 195 | version = "*" 196 | features = ["rocketly"] 197 | ``` 198 | 199 | For example, 200 | 201 | ```rust,ignore 202 | #![feature(plugin)] 203 | #![feature(custom_derive)] 204 | #![plugin(rocket_codegen)] 205 | 206 | #[macro_use] extern crate validators; 207 | 208 | extern crate rocket; 209 | 210 | use rocket::request::Form; 211 | 212 | use validators::http_url::HttpUrlUnlocalableWithProtocol; 213 | use validators::email::Email; 214 | 215 | validated_customized_ranged_number!(PersonID, u8, 0, 100); 216 | validated_customized_regex_string!(Name, r"^[\S ]{1,80}$"); 217 | validated_customized_ranged_number!(PersonAge, u8, 0, 130); 218 | 219 | #[derive(Debug, FromForm)] 220 | struct ContactModel { 221 | name: Name, 222 | age: Option, 223 | email: Email, 224 | url: Option 225 | } 226 | 227 | #[post("/contact/", data = "")] 228 | fn contact(id: PersonID, model: Form) -> &'static str { 229 | println!("{}", id); 230 | println!("{:?}", model.get()); 231 | "do something..." 232 | } 233 | ``` 234 | 235 | ## Serde Support 236 | 237 | Serde is a framework for serializing and deserializing Rust data structures efficiently and generically. And again, this crate supports [Serde](https://crates.io/crates/serde) framework. 238 | 239 | All validated wrapper types and validated customized structs implement the `Serialize` and `Deserialize` traits. 240 | 241 | To use with Serde support, you have to enable **serdely** feature for this crate. 242 | 243 | ```toml 244 | [dependencies.validators] 245 | version = "*" 246 | features = ["serdely"] 247 | ``` 248 | 249 | For example, 250 | 251 | ```rust,ignore 252 | #[macro_use] extern crate validators; 253 | #[macro_use] extern crate serde_json; 254 | 255 | validated_customized_regex_string!(Name, "^[A-Z][a-zA-Z]*( [A-Z][a-zA-Z]*)*$"); 256 | validated_customized_ranged_length_vec!(Names, 1, 5); 257 | 258 | let mut names = Vec::new(); 259 | 260 | names.push(Name::from_str("Ron").unwrap()); 261 | names.push(Name::from_str("Magic Len").unwrap()); 262 | 263 | let names = Names::from_vec(names).unwrap(); 264 | 265 | assert_eq!("[\"Ron\",\"Magic Len\"]", json!(names).to_string()); 266 | ``` 267 | 268 | Also, the `json`, `json_array` and `json_object` modules are available. 269 | */ 270 | 271 | #![cfg_attr(feature = "nightly", feature(ip))] 272 | 273 | #[doc(hidden)] 274 | pub extern crate regex; 275 | 276 | #[macro_use] 277 | pub extern crate lazy_static; 278 | 279 | #[macro_use] 280 | pub extern crate debug_helper; 281 | 282 | #[cfg(feature = "rocketly")] 283 | pub extern crate rocket; 284 | 285 | #[cfg(feature = "serdely")] 286 | #[macro_use] 287 | pub extern crate serde; 288 | 289 | #[cfg(feature = "phone-number")] 290 | pub extern crate phonenumber; 291 | 292 | pub extern crate num_traits; 293 | 294 | #[cfg(test)] 295 | #[macro_use] 296 | extern crate assert_approx_eq; 297 | 298 | mod validator_option; 299 | 300 | pub use validator_option::*; 301 | 302 | use std::fmt::{Debug, Display}; 303 | 304 | pub trait Validated: Display + PartialEq + Clone + Debug {} 305 | 306 | pub trait ValidatedWrapper: Validated { 307 | type Error: Display + PartialEq + Clone + Debug; 308 | 309 | fn from_string(from_string_input: String) -> Result; 310 | 311 | fn from_str(from_str_input: &str) -> Result; 312 | } 313 | 314 | pub mod base32; 315 | pub mod base64; 316 | pub mod base64_url; 317 | pub mod boolean; 318 | pub mod domain; 319 | pub mod email; 320 | pub mod host; 321 | pub mod http_ftp_url; 322 | pub mod http_url; 323 | pub mod integer; 324 | pub mod ipv4; 325 | pub mod ipv6; 326 | #[cfg(feature = "serdely")] 327 | pub mod json; 328 | #[cfg(feature = "serdely")] 329 | pub mod json_array; 330 | #[cfg(feature = "serdely")] 331 | pub mod json_object; 332 | pub mod line; 333 | pub mod mac_address; 334 | pub mod number; 335 | pub mod short_crypt_qr_code_alphanumeric; 336 | pub mod short_crypt_url_component; 337 | pub mod text; 338 | pub mod uri; 339 | pub mod uuid; 340 | pub mod version; 341 | 342 | mod macro_validated_customized_hash_set; 343 | mod macro_validated_customized_number; 344 | #[cfg(feature = "phone-number")] 345 | mod macro_validated_customized_phone_number; 346 | mod macro_validated_customized_string; 347 | mod macro_validated_customized_vec; 348 | 349 | pub use self::macro_validated_customized_hash_set::*; 350 | pub use self::macro_validated_customized_number::*; 351 | #[cfg(feature = "phone-number")] 352 | pub use self::macro_validated_customized_phone_number::*; 353 | pub use self::macro_validated_customized_string::*; 354 | pub use self::macro_validated_customized_vec::*; 355 | -------------------------------------------------------------------------------- /src/line/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::{FromStr, Utf8Error}; 10 | 11 | lazy_static! { 12 | static ref LINE_RE: Regex = Regex::new("^[^\x00-\x08\x0A-\x1F\x7F]*$").unwrap(); 13 | } 14 | 15 | #[derive(Debug, PartialEq, Clone)] 16 | pub enum LineError { 17 | IncorrectFormat, 18 | UTF8Error(Utf8Error), 19 | } 20 | 21 | impl Display for LineError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for LineError {} 29 | 30 | impl From for LineError { 31 | #[inline] 32 | fn from(err: Utf8Error) -> Self { 33 | LineError::UTF8Error(err) 34 | } 35 | } 36 | 37 | pub type LineResult = Result; 38 | 39 | #[derive(Debug, PartialEq)] 40 | pub struct LineValidator {} 41 | 42 | #[derive(Clone, PartialEq, Eq, Hash)] 43 | pub struct Line { 44 | line: String, 45 | } 46 | 47 | impl Line { 48 | #[inline] 49 | pub fn get_line(&self) -> &str { 50 | &self.line 51 | } 52 | 53 | #[inline] 54 | pub fn into_string(self) -> String { 55 | self.line 56 | } 57 | 58 | #[allow(clippy::missing_safety_doc)] 59 | #[inline] 60 | pub unsafe fn from_string_unchecked(line: String) -> Line { 61 | Line { 62 | line, 63 | } 64 | } 65 | } 66 | 67 | impl Deref for Line { 68 | type Target = str; 69 | 70 | #[inline] 71 | fn deref(&self) -> &Self::Target { 72 | &self.line 73 | } 74 | } 75 | 76 | impl Validated for Line {} 77 | 78 | impl Debug for Line { 79 | #[inline] 80 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 81 | impl_debug_for_tuple_struct!(Line, f, self, let .0 = self.line); 82 | } 83 | } 84 | 85 | impl Display for Line { 86 | #[inline] 87 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 88 | f.write_str(&self.line)?; 89 | Ok(()) 90 | } 91 | } 92 | 93 | impl LineValidator { 94 | #[inline] 95 | pub fn is_line(&self, line: &str) -> bool { 96 | self.parse_inner(line).is_ok() 97 | } 98 | 99 | #[inline] 100 | pub fn parse_string(&self, line: String) -> LineResult { 101 | let mut line_inner = self.parse_inner(&line)?; 102 | 103 | line_inner.line = line; 104 | 105 | Ok(line_inner) 106 | } 107 | 108 | #[inline] 109 | pub fn parse_str(&self, line: &str) -> LineResult { 110 | let mut line_inner = self.parse_inner(line)?; 111 | 112 | line_inner.line.push_str(line); 113 | 114 | Ok(line_inner) 115 | } 116 | 117 | #[inline] 118 | fn parse_inner(&self, line: &str) -> LineResult { 119 | if LINE_RE.is_match(line) { 120 | Ok(Line { 121 | line: String::new(), 122 | }) 123 | } else { 124 | Err(LineError::IncorrectFormat) 125 | } 126 | } 127 | } 128 | 129 | #[cfg(test)] 130 | mod tests { 131 | use super::*; 132 | 133 | #[test] 134 | fn test_line_methods() { 135 | let line = "abc123 ABC中文".to_string(); 136 | 137 | let bv = LineValidator {}; 138 | 139 | let line = bv.parse_string(line).unwrap(); 140 | 141 | assert_eq!("abc123 ABC中文", line.get_line()); 142 | } 143 | 144 | #[test] 145 | fn test_line_lv1() { 146 | let line = "abc123 ABC中文".to_string(); 147 | 148 | let bv = LineValidator {}; 149 | 150 | bv.parse_string(line).unwrap(); 151 | } 152 | } 153 | 154 | // Line's wrapper struct is itself 155 | impl ValidatedWrapper for Line { 156 | type Error = LineError; 157 | 158 | #[inline] 159 | fn from_string(line: String) -> Result { 160 | Line::from_string(line) 161 | } 162 | 163 | #[inline] 164 | fn from_str(line: &str) -> Result { 165 | Line::from_str(line) 166 | } 167 | } 168 | 169 | impl Line { 170 | #[inline] 171 | pub fn from_string(line: String) -> Result { 172 | Line::create_validator().parse_string(line) 173 | } 174 | 175 | #[inline] 176 | #[allow(clippy::should_implement_trait)] 177 | pub fn from_str(line: &str) -> Result { 178 | Line::create_validator().parse_str(line) 179 | } 180 | 181 | #[inline] 182 | fn create_validator() -> LineValidator { 183 | LineValidator {} 184 | } 185 | } 186 | 187 | impl FromStr for Line { 188 | type Err = LineError; 189 | 190 | #[inline] 191 | fn from_str(s: &str) -> Result { 192 | Line::from_str(s) 193 | } 194 | } 195 | 196 | #[cfg(feature = "rocketly")] 197 | impl<'a> ::rocket::request::FromParam<'a> for Line { 198 | type Error = LineError; 199 | 200 | #[inline] 201 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 202 | Line::from_string(param.url_decode()?) 203 | } 204 | } 205 | 206 | #[cfg(feature = "rocketly")] 207 | impl<'a> ::rocket::request::FromFormValue<'a> for Line { 208 | type Error = LineError; 209 | 210 | #[inline] 211 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 212 | Line::from_string(form_value.url_decode()?) 213 | } 214 | } 215 | 216 | #[cfg(feature = "serdely")] 217 | struct StringVisitor; 218 | 219 | #[cfg(feature = "serdely")] 220 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 221 | type Value = Line; 222 | 223 | #[inline] 224 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 225 | formatter.write_str("a Line string") 226 | } 227 | 228 | #[inline] 229 | fn visit_str(self, v: &str) -> Result 230 | where 231 | E: ::serde::de::Error, { 232 | Line::from_str(v).map_err(|err| E::custom(err.to_string())) 233 | } 234 | 235 | #[inline] 236 | fn visit_string(self, v: String) -> Result 237 | where 238 | E: ::serde::de::Error, { 239 | Line::from_string(v).map_err(|err| E::custom(err.to_string())) 240 | } 241 | } 242 | 243 | #[cfg(feature = "serdely")] 244 | impl<'de> ::serde::Deserialize<'de> for Line { 245 | #[inline] 246 | fn deserialize(deserializer: D) -> Result 247 | where 248 | D: ::serde::Deserializer<'de>, { 249 | deserializer.deserialize_str(StringVisitor) 250 | } 251 | } 252 | 253 | #[cfg(feature = "serdely")] 254 | impl ::serde::Serialize for Line { 255 | #[inline] 256 | fn serialize(&self, serializer: S) -> Result 257 | where 258 | S: ::serde::Serializer, { 259 | serializer.serialize_str(&self.line) 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/macro_validated_customized_string.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serdely")] 2 | use super::ValidatedWrapper; 3 | 4 | use std::error::Error; 5 | use std::fmt::{self, Debug, Display, Formatter}; 6 | use std::str::Utf8Error; 7 | 8 | #[derive(Debug, PartialEq, Clone)] 9 | pub enum ValidatedCustomizedStringError { 10 | RegexError(regex::Error), 11 | NotMatch, 12 | UTF8Error(Utf8Error), 13 | } 14 | 15 | impl Display for ValidatedCustomizedStringError { 16 | #[inline] 17 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 18 | Debug::fmt(self, f) 19 | } 20 | } 21 | 22 | impl Error for ValidatedCustomizedStringError {} 23 | 24 | #[cfg(feature = "serdely")] 25 | pub struct StringVisitor(pub Vec); 26 | 27 | #[cfg(feature = "serdely")] 28 | impl<'de, V: ValidatedWrapper> serde::de::Visitor<'de> for StringVisitor { 29 | type Value = V; 30 | 31 | #[inline] 32 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 33 | formatter.write_fmt(format_args!("a string({})", stringify!($name))) 34 | } 35 | 36 | #[inline] 37 | fn visit_str(self, v: &str) -> Result 38 | where 39 | E: serde::de::Error, { 40 | V::from_str(v).map_err(|err| E::custom(err.to_string())) 41 | } 42 | 43 | #[inline] 44 | fn visit_string(self, v: String) -> Result 45 | where 46 | E: serde::de::Error, { 47 | V::from_string(v).map_err(|err| E::custom(err.to_string())) 48 | } 49 | } 50 | 51 | #[cfg(feature = "serdely")] 52 | #[doc(hidden)] 53 | #[macro_export] 54 | macro_rules! validated_customized_string_struct_implement_se_de { 55 | ($name:ident) => { 56 | impl<'de> $crate::serde::Deserialize<'de> for $name { 57 | #[inline] 58 | fn deserialize(deserializer: D) -> ::std::result::Result 59 | where 60 | D: $crate::serde::Deserializer<'de>, { 61 | deserializer.deserialize_string($crate::StringVisitor(Vec::<$name>::new())) 62 | } 63 | } 64 | 65 | impl $crate::serde::Serialize for $name { 66 | #[inline] 67 | fn serialize(&self, serializer: S) -> ::std::result::Result 68 | where 69 | S: $crate::serde::Serializer, { 70 | serializer.serialize_str(self.as_str()) 71 | } 72 | } 73 | }; 74 | } 75 | 76 | #[cfg(not(feature = "serdely"))] 77 | #[doc(hidden)] 78 | #[macro_export] 79 | macro_rules! validated_customized_string_struct_implement_se_de { 80 | ($name:ident) => {}; 81 | } 82 | 83 | #[cfg(feature = "rocketly")] 84 | #[doc(hidden)] 85 | #[macro_export] 86 | macro_rules! validated_customized_string_struct_implement_from_form_value { 87 | ($name:ident) => { 88 | impl<'a> $crate::rocket::request::FromFormValue<'a> for $name { 89 | type Error = $crate::ValidatedCustomizedStringError; 90 | 91 | #[inline] 92 | fn from_form_value( 93 | form_value: &'a $crate::rocket::http::RawStr, 94 | ) -> ::std::result::Result { 95 | $name::from_string( 96 | form_value 97 | .url_decode() 98 | .map_err(|err| $crate::ValidatedCustomizedStringError::UTF8Error(err))?, 99 | ) 100 | } 101 | } 102 | 103 | impl<'a> $crate::rocket::request::FromParam<'a> for $name { 104 | type Error = $crate::ValidatedCustomizedStringError; 105 | 106 | #[inline] 107 | fn from_param( 108 | param: &'a $crate::rocket::http::RawStr, 109 | ) -> ::std::result::Result { 110 | $name::from_string( 111 | param 112 | .url_decode() 113 | .map_err(|err| $crate::ValidatedCustomizedStringError::UTF8Error(err))?, 114 | ) 115 | } 116 | } 117 | }; 118 | } 119 | 120 | #[cfg(not(feature = "rocketly"))] 121 | #[doc(hidden)] 122 | #[macro_export] 123 | macro_rules! validated_customized_string_struct_implement_from_form_value { 124 | ($name:ident) => {}; 125 | } 126 | 127 | #[macro_export] 128 | macro_rules! validated_customized_string_struct { 129 | ( $name:ident, $field:ident, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block ) => { 130 | impl ::std::fmt::Debug for $name { 131 | #[inline] 132 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 133 | $crate::debug_helper::impl_debug_for_tuple_struct!($name, f, self, let .0 = self.$field); 134 | } 135 | } 136 | 137 | impl ::std::fmt::Display for $name { 138 | #[inline] 139 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 140 | f.write_str(&self.$field)?; 141 | Ok(()) 142 | } 143 | } 144 | 145 | impl ::std::ops::Deref for $name { 146 | type Target = str; 147 | 148 | #[inline] 149 | fn deref(&self) -> &Self::Target { 150 | &self.$field 151 | } 152 | } 153 | 154 | impl $crate::Validated for $name {} 155 | 156 | impl $crate::ValidatedWrapper for $name { 157 | type Error = $crate::ValidatedCustomizedStringError; 158 | 159 | #[inline] 160 | fn from_string($from_string_input: String) -> ::std::result::Result { 161 | $name::from_string($from_string_input) 162 | } 163 | 164 | #[inline] 165 | fn from_str($from_str_input: &str) -> ::std::result::Result { 166 | $name::from_str($from_str_input) 167 | } 168 | } 169 | 170 | impl<'a> $name { 171 | #[inline] 172 | pub fn as_str(&'a self) -> &'a str { 173 | &self.$field 174 | } 175 | 176 | #[inline] 177 | pub fn into_string(self) -> String { 178 | self.$field 179 | } 180 | 181 | #[inline] 182 | pub fn from_string($from_string_input: String) -> ::std::result::Result { 183 | let $field = match $from_string { 184 | Ok(s)=> s, 185 | Err(e)=> return Err(e) 186 | }; 187 | 188 | Ok($name {$field}) 189 | } 190 | 191 | #[inline] 192 | pub fn from_str($from_str_input: &str) -> ::std::result::Result { 193 | let $field = match $from_str { 194 | Ok(s)=> s, 195 | Err(e)=> return Err(e) 196 | }; 197 | 198 | Ok($name {$field}) 199 | } 200 | 201 | #[inline] 202 | pub unsafe fn from_string_unchecked($from_string_input: String) -> Self { 203 | $name {$field:$from_string_input} 204 | } 205 | } 206 | 207 | impl ::std::str::FromStr for $name { 208 | type Err = $crate::ValidatedCustomizedStringError; 209 | 210 | #[inline] 211 | fn from_str(s: &str) -> Result { 212 | $name::from_str(s) 213 | } 214 | } 215 | 216 | validated_customized_string_struct_implement_from_form_value!($name); 217 | 218 | validated_customized_string_struct_implement_se_de!($name); 219 | }; 220 | ( $name:ident, $field:ident, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block ) => { 221 | validated_customized_string_struct!($name, $field, $from_string_input $from_string, $from_str_input $from_str); 222 | }; 223 | ( $name:ident, $field:ident, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block ) => { 224 | validated_customized_string_struct!($name, $field, $from_string_input $from_string, $from_str_input $from_str); 225 | }; 226 | } 227 | 228 | #[macro_export] 229 | macro_rules! validated_customized_string { 230 | ( $name:ident, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block ) => { 231 | #[derive(Clone, PartialEq, Eq, Hash)] 232 | struct $name { 233 | s: String 234 | } 235 | 236 | validated_customized_string_struct!($name, s, $from_string_input $from_string, $from_str_input $from_str); 237 | }; 238 | ( $name:ident, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block ) => { 239 | validated_customized_string!($name, $from_string_input $from_string, $from_str_input $from_str); 240 | }; 241 | ( $name:ident, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block ) => { 242 | validated_customized_string!($name, $from_string_input $from_string, $from_str_input $from_str); 243 | }; 244 | ( $v:vis $name:ident, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block ) => { 245 | #[derive(Clone, PartialEq, Eq, Hash)] 246 | $v struct $name { 247 | s: String 248 | } 249 | 250 | validated_customized_string_struct!($name, s, $from_string_input $from_string, $from_str_input $from_str); 251 | }; 252 | ( $v:vis $name:ident, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block ) => { 253 | validated_customized_string!($v $name, $from_string_input $from_string, $from_str_input $from_str); 254 | }; 255 | ( $v:vis $name:ident, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block ) => { 256 | validated_customized_string!($v $name, $from_string_input $from_string, $from_str_input $from_str); 257 | }; 258 | } 259 | 260 | #[macro_export] 261 | macro_rules! validated_customized_regex_string_struct { 262 | ( $name:ident, $field:ident, $re:expr ) => { 263 | validated_customized_string_struct!($name, $field, 264 | input { 265 | let re = $crate::regex::Regex::new($re).map_err(|err| $crate::ValidatedCustomizedStringError::RegexError(err))?; 266 | 267 | if re.is_match(&input) { 268 | Ok(input) 269 | } else{ 270 | Err($crate::ValidatedCustomizedStringError::NotMatch) 271 | } 272 | }, 273 | input { 274 | let re = $crate::regex::Regex::new($re).map_err(|err| $crate::ValidatedCustomizedStringError::RegexError(err))?; 275 | 276 | if re.is_match(input) { 277 | Ok(input.to_string()) 278 | } else{ 279 | Err($crate::ValidatedCustomizedStringError::NotMatch) 280 | } 281 | }); 282 | }; 283 | ( $name:ident, $field:ident, ref $re:expr ) => { 284 | validated_customized_string_struct!($name, $field, 285 | input { 286 | let re: &$crate::regex::Regex = &$re; 287 | 288 | if re.is_match(&input) { 289 | Ok(input) 290 | } else{ 291 | Err($crate::ValidatedCustomizedStringError::NotMatch) 292 | } 293 | }, 294 | input { 295 | let re: &$crate::regex::Regex = &$re; 296 | 297 | if re.is_match(input) { 298 | Ok(input.to_string()) 299 | } else{ 300 | Err($crate::ValidatedCustomizedStringError::NotMatch) 301 | } 302 | }); 303 | }; 304 | } 305 | 306 | #[macro_export] 307 | macro_rules! validated_customized_regex_string { 308 | ( $name:ident, $re:expr ) => { 309 | #[derive(Clone, PartialEq, Eq, Hash)] 310 | struct $name { 311 | s: String 312 | } 313 | 314 | validated_customized_regex_string_struct!($name, s, $re); 315 | }; 316 | ( $v:vis $name:ident, $re:expr ) => { 317 | #[derive(Clone, PartialEq, Eq, Hash)] 318 | $v struct $name { 319 | s: String 320 | } 321 | 322 | validated_customized_regex_string_struct!($name, s, $re); 323 | }; 324 | ( $name:ident, ref $re:expr ) => { 325 | #[derive(Clone, PartialEq, Eq, Hash)] 326 | struct $name { 327 | s: String 328 | } 329 | 330 | validated_customized_regex_string_struct!($name, s, ref $re); 331 | }; 332 | ( $v:vis $name:ident, ref $re:expr ) => { 333 | #[derive(Clone, PartialEq, Eq, Hash)] 334 | $v struct $name { 335 | s: String 336 | } 337 | 338 | validated_customized_regex_string_struct!($name, s, ref $re); 339 | }; 340 | } 341 | -------------------------------------------------------------------------------- /src/macro_validated_customized_vec.rs: -------------------------------------------------------------------------------- 1 | use super::ValidatedWrapper; 2 | 3 | use std::error::Error; 4 | use std::fmt::{self, Debug, Display, Formatter}; 5 | use std::str::Utf8Error; 6 | 7 | #[derive(Debug, PartialEq, Clone)] 8 | pub enum ValidatedCustomizedVecError { 9 | Overflow, 10 | Underflow, 11 | NotSupport, 12 | UTF8Error(Utf8Error), 13 | } 14 | 15 | impl Display for ValidatedCustomizedVecError { 16 | #[inline] 17 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 18 | Debug::fmt(self, f) 19 | } 20 | } 21 | 22 | impl Error for ValidatedCustomizedVecError {} 23 | 24 | pub trait ValidatedVecWrapper: ValidatedWrapper { 25 | fn from_vec(v: Vec) -> Result; 26 | } 27 | 28 | #[cfg(feature = "serdely")] 29 | pub struct VecVisitor(pub Vec, pub Vec); 30 | 31 | #[cfg(feature = "serdely")] 32 | impl<'de, V: ValidatedVecWrapper, T: ValidatedWrapper + serde::Deserialize<'de>> 33 | serde::de::Visitor<'de> for VecVisitor 34 | { 35 | type Value = V; 36 | 37 | #[inline] 38 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 39 | formatter.write_fmt(format_args!("an array({})", stringify!($name))) 40 | } 41 | 42 | #[inline] 43 | fn visit_seq(self, mut seq: A) -> Result 44 | where 45 | A: serde::de::SeqAccess<'de>, { 46 | let mut v = Vec::::new(); 47 | 48 | while let Some(e) = seq.next_element()? { 49 | v.push(e); 50 | } 51 | 52 | Ok(V::from_vec(v).map_err(|err| serde::de::Error::custom(err.to_string()))?) 53 | } 54 | } 55 | 56 | #[cfg(feature = "serdely")] 57 | #[doc(hidden)] 58 | #[macro_export] 59 | macro_rules! validated_customized_vec_struct_implement_se_de { 60 | ($name:ident) => { 61 | impl<'de, T: $crate::ValidatedWrapper + $crate::serde::Deserialize<'de>> 62 | $crate::serde::Deserialize<'de> for $name 63 | { 64 | #[inline] 65 | fn deserialize(deserializer: D) -> ::std::result::Result 66 | where 67 | D: $crate::serde::Deserializer<'de>, { 68 | deserializer 69 | .deserialize_seq($crate::VecVisitor(Vec::<$name>::new(), Vec::::new())) 70 | } 71 | } 72 | 73 | impl $crate::serde::Serialize 74 | for $name 75 | { 76 | #[inline] 77 | fn serialize(&self, serializer: S) -> ::std::result::Result 78 | where 79 | S: $crate::serde::Serializer, { 80 | serializer.collect_seq(self.as_vec().iter()) 81 | } 82 | } 83 | }; 84 | } 85 | 86 | #[cfg(not(feature = "serdely"))] 87 | #[doc(hidden)] 88 | #[macro_export] 89 | macro_rules! validated_customized_vec_struct_implement_se_de { 90 | ($name:ident) => {}; 91 | } 92 | 93 | #[cfg(feature = "rocketly")] 94 | #[doc(hidden)] 95 | #[macro_export] 96 | macro_rules! validated_customized_vec_struct_implement_from_form_value { 97 | ($name:ident) => { 98 | impl<'a, T: $crate::ValidatedWrapper> $crate::rocket::request::FromFormValue<'a> 99 | for $name 100 | { 101 | type Error = $crate::ValidatedCustomizedVecError; 102 | 103 | #[inline] 104 | fn from_form_value( 105 | form_value: &'a $crate::rocket::http::RawStr, 106 | ) -> ::std::result::Result { 107 | $name::from_string( 108 | form_value 109 | .url_decode() 110 | .map_err(|err| $crate::ValidatedCustomizedVecError::UTF8Error(err))?, 111 | ) 112 | } 113 | } 114 | 115 | impl<'a, T: $crate::ValidatedWrapper> $crate::rocket::request::FromParam<'a> for $name { 116 | type Error = $crate::ValidatedCustomizedVecError; 117 | 118 | #[inline] 119 | fn from_param( 120 | param: &'a $crate::rocket::http::RawStr, 121 | ) -> ::std::result::Result { 122 | $name::from_string( 123 | param 124 | .url_decode() 125 | .map_err(|err| $crate::ValidatedCustomizedVecError::UTF8Error(err))?, 126 | ) 127 | } 128 | } 129 | }; 130 | } 131 | 132 | #[cfg(not(feature = "rocketly"))] 133 | #[doc(hidden)] 134 | #[macro_export] 135 | macro_rules! validated_customized_vec_struct_implement_from_form_value { 136 | ($name:ident) => {}; 137 | } 138 | 139 | #[macro_export] 140 | macro_rules! validated_customized_vec_struct { 141 | ( $name:ident, $field:ident, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block, $from_vec_input:ident $from_vec:block ) => { 142 | impl ::std::fmt::Debug for $name { 143 | #[inline] 144 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 145 | $crate::debug_helper::impl_debug_for_tuple_struct!($name, f, self, let .0 = self.$field); 146 | } 147 | } 148 | 149 | impl ::std::fmt::Display for $name { 150 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 151 | f.write_str("[")?; 152 | 153 | let len = self.$field.len(); 154 | 155 | if len > 0 { 156 | for n in self.$field.iter().take(len - 1) { 157 | ::std::fmt::Display::fmt(n, f)?; 158 | 159 | 160 | f.write_str(", ")?; 161 | } 162 | 163 | ::std::fmt::Display::fmt(&self.$field[len - 1], f)?; 164 | } 165 | 166 | f.write_str("]")?; 167 | 168 | Ok(()) 169 | } 170 | } 171 | 172 | impl ::std::cmp::PartialEq for $name { 173 | #[inline] 174 | fn eq(&self, other: &Self) -> bool { 175 | self.$field.eq(&other.$field) 176 | } 177 | } 178 | 179 | impl ::std::cmp::Eq for $name {} 180 | 181 | impl ::std::ops::Deref for $name { 182 | type Target = Vec; 183 | 184 | #[inline] 185 | fn deref(&self) -> &Self::Target { 186 | &self.$field 187 | } 188 | } 189 | 190 | impl $crate::Validated for $name {} 191 | 192 | impl $crate::ValidatedWrapper for $name { 193 | type Error = $crate::ValidatedCustomizedVecError; 194 | 195 | #[inline] 196 | fn from_string($from_string_input: String) -> ::std::result::Result { 197 | $name::from_string($from_string_input) 198 | } 199 | 200 | #[inline] 201 | fn from_str($from_str_input: &str) -> ::std::result::Result { 202 | $name::from_str($from_str_input) 203 | } 204 | } 205 | 206 | impl $crate::ValidatedVecWrapper for $name { 207 | #[inline] 208 | fn from_vec($from_vec_input: Vec) -> ::std::result::Result { 209 | $name::from_vec($from_vec_input) 210 | } 211 | } 212 | 213 | impl $name { 214 | #[inline] 215 | pub fn as_vec(&self) -> &Vec { 216 | &self.$field 217 | } 218 | 219 | #[inline] 220 | pub fn into_vec(self) -> Vec { 221 | self.$field 222 | } 223 | 224 | #[inline] 225 | pub fn from_string($from_string_input: String) -> ::std::result::Result { 226 | let $field = match $from_string { 227 | Ok(s)=> s, 228 | Err(e)=> return Err(e) 229 | }; 230 | 231 | Ok($name {$field}) 232 | } 233 | 234 | #[inline] 235 | pub fn from_str($from_str_input: &str) -> ::std::result::Result { 236 | let $field = match $from_str { 237 | Ok(s)=> s, 238 | Err(e)=> return Err(e) 239 | }; 240 | 241 | Ok($name {$field}) 242 | } 243 | 244 | #[inline] 245 | pub fn from_vec($from_vec_input: Vec) -> ::std::result::Result { 246 | let $field = match $from_vec { 247 | Ok(s)=> s, 248 | Err(e)=> return Err(e) 249 | }; 250 | 251 | Ok($name {$field}) 252 | } 253 | 254 | #[inline] 255 | pub unsafe fn from_vec_unchecked($from_vec_input: Vec) -> Self { 256 | $name {$field:$from_vec_input} 257 | } 258 | } 259 | 260 | impl ::std::str::FromStr for $name { 261 | type Err = $crate::ValidatedCustomizedVecError; 262 | 263 | #[inline] 264 | fn from_str(s: &str) -> Result { 265 | $name::from_str(s) 266 | } 267 | } 268 | 269 | validated_customized_vec_struct_implement_from_form_value!($name); 270 | 271 | validated_customized_vec_struct_implement_se_de!($name); 272 | }; 273 | } 274 | 275 | #[macro_export] 276 | macro_rules! validated_customized_vec { 277 | ( $name:ident, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block, $from_vec_input:ident $from_vec:block ) => { 278 | #[derive(Clone)] 279 | struct $name { 280 | v: Vec 281 | } 282 | 283 | validated_customized_vec_struct!($name, v, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 284 | }; 285 | ( $name:ident, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block, from_vec $from_vec_input:ident $from_vec:block ) => { 286 | validated_customized_vec!($name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 287 | }; 288 | ( $name:ident, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block, from_vec $from_vec_input:ident $from_vec:block ) => { 289 | validated_customized_vec!($name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 290 | }; 291 | ( $name:ident, from_vec $from_vec_input:ident $from_vec:block, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block ) => { 292 | validated_customized_vec!($name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 293 | }; 294 | ( $name:ident, from_vec $from_vec_input:ident $from_vec:block, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block ) => { 295 | validated_customized_vec!($name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 296 | }; 297 | ( $name:ident, from_string $from_string_input:ident $from_string:block, from_vec $from_vec_input:ident $from_vec:block, from_str $from_str_input:ident $from_str:block ) => { 298 | validated_customized_vec!($name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 299 | }; 300 | ( $name:ident, from_str $from_str_input:ident $from_str:block, from_vec $from_vec_input:ident $from_vec:block, from_string $from_string_input:ident $from_string:block ) => { 301 | validated_customized_vec!($name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 302 | }; 303 | ( $v:vis $name:ident, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block, $from_vec_input:ident $from_vec:block ) => { 304 | #[derive(Clone)] 305 | $v struct $name { 306 | v: Vec 307 | } 308 | 309 | validated_customized_vec_struct!($name, v, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 310 | }; 311 | ( $v:vis $name:ident, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block, from_vec $from_vec_input:ident $from_vec:block ) => { 312 | validated_customized_vec!($v $name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 313 | }; 314 | ( $v:vis $name:ident, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block, from_vec $from_vec_input:ident $from_vec:block ) => { 315 | validated_customized_vec!($v $name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 316 | }; 317 | ( $v:vis $name:ident, from_vec $from_vec_input:ident $from_vec:block, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block ) => { 318 | validated_customized_vec!($v $name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 319 | }; 320 | ( $v:vis $name:ident, from_vec $from_vec_input:ident $from_vec:block, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block ) => { 321 | validated_customized_vec!($v $name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 322 | }; 323 | ( $v:vis $name:ident, from_string $from_string_input:ident $from_string:block, from_vec $from_vec_input:ident $from_vec:block, from_str $from_str_input:ident $from_str:block ) => { 324 | validated_customized_vec!($v $name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 325 | }; 326 | ( $v:vis $name:ident, from_str $from_str_input:ident $from_str:block, from_vec $from_vec_input:ident $from_vec:block, from_string $from_string_input:ident $from_string:block ) => { 327 | validated_customized_vec!($v $name, $from_string_input $from_string, $from_str_input $from_str, $from_vec_input $from_vec); 328 | }; 329 | } 330 | 331 | #[macro_export] 332 | macro_rules! validated_customized_ranged_length_vec_struct { 333 | ( $name:ident, $field:expr, $min:expr, $max:expr, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block) => { 334 | validated_customized_vec_struct!($name, v, 335 | $from_string_input $from_string, 336 | $from_str_input $from_str, 337 | input { 338 | let len = input.len(); 339 | 340 | if len > $max { 341 | Err($crate::ValidatedCustomizedVecError::Overflow) 342 | } else if len < $min { 343 | Err($crate::ValidatedCustomizedVecError::Underflow) 344 | } else { 345 | Ok(input) 346 | } 347 | }); 348 | }; 349 | } 350 | 351 | #[macro_export] 352 | macro_rules! validated_customized_ranged_length_vec { 353 | ( $name:ident, $min:expr, $max:expr, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block) => { 354 | #[derive(Clone)] 355 | struct $name { 356 | v: Vec 357 | } 358 | 359 | validated_customized_ranged_length_vec_struct!($name, v, $min, $max, $from_string_input $from_string, $from_str_input $from_str); 360 | }; 361 | ( $name:ident, $min:expr, $max:expr, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block) => { 362 | validated_customized_ranged_length_vec!($name, $min, $max, $from_string_input $from_string, $from_str_input $from_str); 363 | }; 364 | ( $name:ident, $min:expr, $max:expr, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block) => { 365 | validated_customized_ranged_length_vec!($name, $min, $max, $from_string_input $from_string, $from_str_input $from_str); 366 | }; 367 | ( $name:ident, $min:expr, $max:expr) => { 368 | validated_customized_ranged_length_vec!($name, $min, $max, 369 | _input {Err($crate::ValidatedCustomizedVecError::NotSupport)}, 370 | _input {Err($crate::ValidatedCustomizedVecError::NotSupport)}); 371 | }; 372 | ( $name:ident, $equal:expr, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block) => { 373 | validated_customized_ranged_length_vec!($name, $equal, $equal, $from_string_input $from_string, $from_str_input $from_str); 374 | }; 375 | ( $name:ident, $equal:expr) => { 376 | validated_customized_ranged_length_vec!($name, $equal, $equal); 377 | }; 378 | ( $v:vis $name:ident, $min:expr, $max:expr, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block) => { 379 | #[derive(Clone)] 380 | $v struct $name { 381 | v: Vec 382 | } 383 | 384 | validated_customized_ranged_length_vec_struct!($name, v, $min, $max, $from_string_input $from_string, $from_str_input $from_str); 385 | }; 386 | ( $v:vis $name:ident, $min:expr, $max:expr, from_string $from_string_input:ident $from_string:block, from_str $from_str_input:ident $from_str:block) => { 387 | validated_customized_ranged_length_vec!($v $name, $min, $max, $from_string_input $from_string, $from_str_input $from_str); 388 | }; 389 | ( $v:vis $name:ident, $min:expr, $max:expr, from_str $from_str_input:ident $from_str:block, from_string $from_string_input:ident $from_string:block) => { 390 | validated_customized_ranged_length_vec!($v $name, $min, $max, $from_string_input $from_string, $from_str_input $from_str); 391 | }; 392 | ( $v:vis $name:ident, $min:expr, $max:expr) => { 393 | validated_customized_ranged_length_vec!($v $name, $min, $max, 394 | _input {Err($crate::ValidatedCustomizedVecError::NotSupport)}, 395 | _input {Err($crate::ValidatedCustomizedVecError::NotSupport)}); 396 | }; 397 | ( $v:vis $name:ident, $equal:expr, $from_string_input:ident $from_string:block, $from_str_input:ident $from_str:block) => { 398 | validated_customized_ranged_length_vec!($v $name, $equal, $equal, $from_string_input $from_string, $from_str_input $from_str); 399 | }; 400 | ( $v:vis $name:ident, $equal:expr) => { 401 | validated_customized_ranged_length_vec!($v $name, $equal, $equal); 402 | }; 403 | } 404 | -------------------------------------------------------------------------------- /src/short_crypt_qr_code_alphanumeric/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::FromStr; 10 | 11 | lazy_static! { 12 | static ref SHORT_CRYPT_QR_CODE_ALPHANUMERIC_RE: Regex = { 13 | Regex::new(r"^([A-Z0-9]{8})*([A-Z0-9]|[A-Z0-9]{3}|[A-Z0-9]{5,6}|[A-Z0-9]{8})$").unwrap() 14 | }; 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone)] 18 | pub enum ShortCryptQRCodeAlphanumericError { 19 | IncorrectFormat, 20 | } 21 | 22 | impl Display for ShortCryptQRCodeAlphanumericError { 23 | #[inline] 24 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 25 | Debug::fmt(self, f) 26 | } 27 | } 28 | 29 | impl Error for ShortCryptQRCodeAlphanumericError {} 30 | 31 | pub type ShortCryptQRCodeAlphanumericResult = 32 | Result; 33 | 34 | #[derive(Debug, PartialEq)] 35 | pub struct ShortCryptQRCodeAlphanumericValidator {} 36 | 37 | #[derive(Clone, PartialEq, Eq, Hash)] 38 | pub struct ShortCryptQRCodeAlphanumeric { 39 | short_crypt_qr_code_alphanumeric: String, 40 | } 41 | 42 | impl ShortCryptQRCodeAlphanumeric { 43 | #[inline] 44 | pub fn get_short_crypt_qr_code_alphanumeric_url(&self) -> &str { 45 | &self.short_crypt_qr_code_alphanumeric 46 | } 47 | 48 | #[inline] 49 | pub fn into_string(self) -> String { 50 | self.short_crypt_qr_code_alphanumeric 51 | } 52 | 53 | #[allow(clippy::missing_safety_doc)] 54 | #[inline] 55 | pub unsafe fn from_string_unchecked( 56 | short_crypt_qr_code_alphanumeric: String, 57 | ) -> ShortCryptQRCodeAlphanumeric { 58 | ShortCryptQRCodeAlphanumeric { 59 | short_crypt_qr_code_alphanumeric, 60 | } 61 | } 62 | } 63 | 64 | impl Deref for ShortCryptQRCodeAlphanumeric { 65 | type Target = str; 66 | 67 | #[inline] 68 | fn deref(&self) -> &Self::Target { 69 | &self.short_crypt_qr_code_alphanumeric 70 | } 71 | } 72 | 73 | impl Validated for ShortCryptQRCodeAlphanumeric {} 74 | 75 | impl Debug for ShortCryptQRCodeAlphanumeric { 76 | #[inline] 77 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 78 | impl_debug_for_tuple_struct!(ShortCryptQRCodeAlphanumeric, f, self, let .0 = self.short_crypt_qr_code_alphanumeric); 79 | } 80 | } 81 | 82 | impl Display for ShortCryptQRCodeAlphanumeric { 83 | #[inline] 84 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 85 | f.write_str(&self.short_crypt_qr_code_alphanumeric)?; 86 | Ok(()) 87 | } 88 | } 89 | 90 | impl ShortCryptQRCodeAlphanumericValidator { 91 | #[inline] 92 | pub fn is_short_crypt_qr_code_alphanumeric_url( 93 | &self, 94 | short_crypt_qr_code_alphanumeric_url: &str, 95 | ) -> bool { 96 | self.parse_inner(short_crypt_qr_code_alphanumeric_url).is_ok() 97 | } 98 | 99 | #[inline] 100 | pub fn parse_string( 101 | &self, 102 | short_crypt_qr_code_alphanumeric_url: String, 103 | ) -> ShortCryptQRCodeAlphanumericResult { 104 | let mut short_crypt_qr_code_alphanumeric_url_inner = 105 | self.parse_inner(&short_crypt_qr_code_alphanumeric_url)?; 106 | 107 | short_crypt_qr_code_alphanumeric_url_inner.short_crypt_qr_code_alphanumeric = 108 | short_crypt_qr_code_alphanumeric_url; 109 | 110 | Ok(short_crypt_qr_code_alphanumeric_url_inner) 111 | } 112 | 113 | #[inline] 114 | pub fn parse_str( 115 | &self, 116 | short_crypt_qr_code_alphanumeric_url: &str, 117 | ) -> ShortCryptQRCodeAlphanumericResult { 118 | let mut short_crypt_qr_code_alphanumeric_url_inner = 119 | self.parse_inner(short_crypt_qr_code_alphanumeric_url)?; 120 | 121 | short_crypt_qr_code_alphanumeric_url_inner 122 | .short_crypt_qr_code_alphanumeric 123 | .push_str(short_crypt_qr_code_alphanumeric_url); 124 | 125 | Ok(short_crypt_qr_code_alphanumeric_url_inner) 126 | } 127 | 128 | #[inline] 129 | fn parse_inner( 130 | &self, 131 | short_crypt_qr_code_alphanumeric_url: &str, 132 | ) -> ShortCryptQRCodeAlphanumericResult { 133 | if SHORT_CRYPT_QR_CODE_ALPHANUMERIC_RE.is_match(short_crypt_qr_code_alphanumeric_url) { 134 | Ok(ShortCryptQRCodeAlphanumeric { 135 | short_crypt_qr_code_alphanumeric: String::new(), 136 | }) 137 | } else { 138 | Err(ShortCryptQRCodeAlphanumericError::IncorrectFormat) 139 | } 140 | } 141 | } 142 | 143 | #[cfg(test)] 144 | mod tests { 145 | use super::*; 146 | 147 | #[test] 148 | fn test_short_crypt_qr_code_alphanumeric_url_methods() { 149 | let short_crypt_qr_code_alphanumeric_url = "3BHNNR45XZH8PU".to_string(); 150 | 151 | let scqacv = ShortCryptQRCodeAlphanumericValidator {}; 152 | 153 | let short_crypt_qr_code_alphanumeric_url = 154 | scqacv.parse_string(short_crypt_qr_code_alphanumeric_url).unwrap(); 155 | 156 | assert_eq!( 157 | "3BHNNR45XZH8PU", 158 | short_crypt_qr_code_alphanumeric_url.get_short_crypt_qr_code_alphanumeric_url() 159 | ); 160 | } 161 | 162 | #[test] 163 | fn test_short_crypt_qr_code_alphanumeric_url_lv1() { 164 | let short_crypt_qr_code_alphanumeric_url = "3BHNNR45XZH8PU".to_string(); 165 | 166 | let scqacv = ShortCryptQRCodeAlphanumericValidator {}; 167 | 168 | scqacv.parse_string(short_crypt_qr_code_alphanumeric_url).unwrap(); 169 | } 170 | } 171 | 172 | // ShortCryptQRCodeAlphanumeric's wrapper struct is itself 173 | impl ValidatedWrapper for ShortCryptQRCodeAlphanumeric { 174 | type Error = ShortCryptQRCodeAlphanumericError; 175 | 176 | #[inline] 177 | fn from_string(short_crypt_qr_code_alphanumeric_url: String) -> Result { 178 | ShortCryptQRCodeAlphanumeric::from_string(short_crypt_qr_code_alphanumeric_url) 179 | } 180 | 181 | #[inline] 182 | fn from_str(short_crypt_qr_code_alphanumeric_url: &str) -> Result { 183 | ShortCryptQRCodeAlphanumeric::from_str(short_crypt_qr_code_alphanumeric_url) 184 | } 185 | } 186 | 187 | impl ShortCryptQRCodeAlphanumeric { 188 | #[inline] 189 | pub fn from_string( 190 | short_crypt_qr_code_alphanumeric_url: String, 191 | ) -> Result { 192 | ShortCryptQRCodeAlphanumeric::create_validator() 193 | .parse_string(short_crypt_qr_code_alphanumeric_url) 194 | } 195 | 196 | #[inline] 197 | #[allow(clippy::should_implement_trait)] 198 | pub fn from_str( 199 | short_crypt_qr_code_alphanumeric_url: &str, 200 | ) -> Result { 201 | ShortCryptQRCodeAlphanumeric::create_validator() 202 | .parse_str(short_crypt_qr_code_alphanumeric_url) 203 | } 204 | 205 | #[inline] 206 | fn create_validator() -> ShortCryptQRCodeAlphanumericValidator { 207 | ShortCryptQRCodeAlphanumericValidator {} 208 | } 209 | } 210 | 211 | impl FromStr for ShortCryptQRCodeAlphanumeric { 212 | type Err = ShortCryptQRCodeAlphanumericError; 213 | 214 | #[inline] 215 | fn from_str(s: &str) -> Result { 216 | ShortCryptQRCodeAlphanumeric::from_str(s) 217 | } 218 | } 219 | 220 | #[cfg(feature = "rocketly")] 221 | impl<'a> ::rocket::request::FromFormValue<'a> for ShortCryptQRCodeAlphanumeric { 222 | type Error = ShortCryptQRCodeAlphanumericError; 223 | 224 | #[inline] 225 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 226 | ShortCryptQRCodeAlphanumeric::from_str(form_value) 227 | } 228 | } 229 | 230 | #[cfg(feature = "rocketly")] 231 | impl<'a> ::rocket::request::FromParam<'a> for ShortCryptQRCodeAlphanumeric { 232 | type Error = ShortCryptQRCodeAlphanumericError; 233 | 234 | #[inline] 235 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 236 | ShortCryptQRCodeAlphanumeric::from_str(param) 237 | } 238 | } 239 | 240 | #[cfg(feature = "serdely")] 241 | struct StringVisitor; 242 | 243 | #[cfg(feature = "serdely")] 244 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 245 | type Value = ShortCryptQRCodeAlphanumeric; 246 | 247 | #[inline] 248 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 249 | formatter.write_str("a ShortCrypt QR code alphanumeric string") 250 | } 251 | 252 | #[inline] 253 | fn visit_str(self, v: &str) -> Result 254 | where 255 | E: ::serde::de::Error, { 256 | ShortCryptQRCodeAlphanumeric::from_str(v).map_err(|err| E::custom(err.to_string())) 257 | } 258 | 259 | #[inline] 260 | fn visit_string(self, v: String) -> Result 261 | where 262 | E: ::serde::de::Error, { 263 | ShortCryptQRCodeAlphanumeric::from_string(v).map_err(|err| E::custom(err.to_string())) 264 | } 265 | } 266 | 267 | #[cfg(feature = "serdely")] 268 | impl<'de> ::serde::Deserialize<'de> for ShortCryptQRCodeAlphanumeric { 269 | #[inline] 270 | fn deserialize(deserializer: D) -> Result 271 | where 272 | D: ::serde::Deserializer<'de>, { 273 | deserializer.deserialize_string(StringVisitor) 274 | } 275 | } 276 | 277 | #[cfg(feature = "serdely")] 278 | impl ::serde::Serialize for ShortCryptQRCodeAlphanumeric { 279 | #[inline] 280 | fn serialize(&self, serializer: S) -> Result 281 | where 282 | S: ::serde::Serializer, { 283 | serializer.serialize_str(&self.short_crypt_qr_code_alphanumeric) 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/short_crypt_url_component/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::FromStr; 10 | 11 | lazy_static! { 12 | static ref SHORT_CRYPT_URL_COMPONENT_RE: Regex = 13 | Regex::new(r"^([A-Za-z0-9\-_]{4})*([A-Za-z0-9\-_]|[A-Za-z0-9\-_]{3,4})$").unwrap(); 14 | } 15 | 16 | #[derive(Debug, PartialEq, Clone)] 17 | pub enum ShortCryptUrlComponentError { 18 | IncorrectFormat, 19 | } 20 | 21 | impl Display for ShortCryptUrlComponentError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for ShortCryptUrlComponentError {} 29 | 30 | pub type ShortCryptUrlComponentResult = Result; 31 | 32 | #[derive(Debug, PartialEq)] 33 | pub struct ShortCryptUrlComponentValidator {} 34 | 35 | #[derive(Clone, PartialEq, Eq, Hash)] 36 | pub struct ShortCryptUrlComponent { 37 | short_crypt_url_component: String, 38 | } 39 | 40 | impl ShortCryptUrlComponent { 41 | #[inline] 42 | pub fn get_short_crypt_url_component(&self) -> &str { 43 | &self.short_crypt_url_component 44 | } 45 | 46 | #[inline] 47 | pub fn into_string(self) -> String { 48 | self.short_crypt_url_component 49 | } 50 | 51 | #[allow(clippy::missing_safety_doc)] 52 | #[inline] 53 | pub unsafe fn from_string_unchecked( 54 | short_crypt_url_component: String, 55 | ) -> ShortCryptUrlComponent { 56 | ShortCryptUrlComponent { 57 | short_crypt_url_component, 58 | } 59 | } 60 | } 61 | 62 | impl Deref for ShortCryptUrlComponent { 63 | type Target = str; 64 | 65 | #[inline] 66 | fn deref(&self) -> &Self::Target { 67 | &self.short_crypt_url_component 68 | } 69 | } 70 | 71 | impl Validated for ShortCryptUrlComponent {} 72 | 73 | impl Debug for ShortCryptUrlComponent { 74 | #[inline] 75 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 76 | impl_debug_for_tuple_struct!(ShortCryptUrlComponent, f, self, let .0 = self.short_crypt_url_component); 77 | } 78 | } 79 | 80 | impl Display for ShortCryptUrlComponent { 81 | #[inline] 82 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 83 | f.write_str(&self.short_crypt_url_component)?; 84 | Ok(()) 85 | } 86 | } 87 | 88 | impl ShortCryptUrlComponentValidator { 89 | #[inline] 90 | pub fn is_short_crypt_url_component(&self, short_crypt_url_component: &str) -> bool { 91 | self.parse_inner(short_crypt_url_component).is_ok() 92 | } 93 | 94 | #[inline] 95 | pub fn parse_string(&self, short_crypt_url_component: String) -> ShortCryptUrlComponentResult { 96 | let mut short_crypt_url_component_inner = self.parse_inner(&short_crypt_url_component)?; 97 | 98 | short_crypt_url_component_inner.short_crypt_url_component = short_crypt_url_component; 99 | 100 | Ok(short_crypt_url_component_inner) 101 | } 102 | 103 | #[inline] 104 | pub fn parse_str(&self, short_crypt_url_component: &str) -> ShortCryptUrlComponentResult { 105 | let mut short_crypt_url_component_inner = self.parse_inner(short_crypt_url_component)?; 106 | 107 | short_crypt_url_component_inner 108 | .short_crypt_url_component 109 | .push_str(short_crypt_url_component); 110 | 111 | Ok(short_crypt_url_component_inner) 112 | } 113 | 114 | #[inline] 115 | fn parse_inner(&self, short_crypt_url_component: &str) -> ShortCryptUrlComponentResult { 116 | if SHORT_CRYPT_URL_COMPONENT_RE.is_match(short_crypt_url_component) { 117 | Ok(ShortCryptUrlComponent { 118 | short_crypt_url_component: String::new(), 119 | }) 120 | } else { 121 | Err(ShortCryptUrlComponentError::IncorrectFormat) 122 | } 123 | } 124 | } 125 | 126 | #[cfg(test)] 127 | mod tests { 128 | use super::*; 129 | 130 | #[test] 131 | fn test_short_crypt_url_component_methods() { 132 | let short_crypt_url_component = "2E87Wx52-Tvo".to_string(); 133 | 134 | let scuv = ShortCryptUrlComponentValidator {}; 135 | 136 | let short_crypt_url_component = scuv.parse_string(short_crypt_url_component).unwrap(); 137 | 138 | assert_eq!("2E87Wx52-Tvo", short_crypt_url_component.get_short_crypt_url_component()); 139 | } 140 | 141 | #[test] 142 | fn test_short_crypt_url_component_lv1() { 143 | let short_crypt_url_component = "2E87Wx52-Tvo".to_string(); 144 | 145 | let scuv = ShortCryptUrlComponentValidator {}; 146 | 147 | scuv.parse_string(short_crypt_url_component).unwrap(); 148 | } 149 | } 150 | 151 | // ShortCryptUrlComponent's wrapper struct is itself 152 | impl ValidatedWrapper for ShortCryptUrlComponent { 153 | type Error = ShortCryptUrlComponentError; 154 | 155 | #[inline] 156 | fn from_string(short_crypt_url_component: String) -> Result { 157 | ShortCryptUrlComponent::from_string(short_crypt_url_component) 158 | } 159 | 160 | #[inline] 161 | fn from_str(short_crypt_url_component: &str) -> Result { 162 | ShortCryptUrlComponent::from_str(short_crypt_url_component) 163 | } 164 | } 165 | 166 | impl ShortCryptUrlComponent { 167 | #[inline] 168 | pub fn from_string( 169 | short_crypt_url_component: String, 170 | ) -> Result { 171 | ShortCryptUrlComponent::create_validator().parse_string(short_crypt_url_component) 172 | } 173 | 174 | #[inline] 175 | #[allow(clippy::should_implement_trait)] 176 | pub fn from_str(short_crypt_url_component: &str) -> Result { 177 | ShortCryptUrlComponent::create_validator().parse_str(short_crypt_url_component) 178 | } 179 | 180 | #[inline] 181 | fn create_validator() -> ShortCryptUrlComponentValidator { 182 | ShortCryptUrlComponentValidator {} 183 | } 184 | } 185 | 186 | impl FromStr for ShortCryptUrlComponent { 187 | type Err = ShortCryptUrlComponentError; 188 | 189 | #[inline] 190 | fn from_str(s: &str) -> Result { 191 | ShortCryptUrlComponent::from_str(s) 192 | } 193 | } 194 | 195 | #[cfg(feature = "rocketly")] 196 | impl<'a> ::rocket::request::FromFormValue<'a> for ShortCryptUrlComponent { 197 | type Error = ShortCryptUrlComponentError; 198 | 199 | #[inline] 200 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 201 | ShortCryptUrlComponent::from_str(form_value) 202 | } 203 | } 204 | 205 | #[cfg(feature = "rocketly")] 206 | impl<'a> ::rocket::request::FromParam<'a> for ShortCryptUrlComponent { 207 | type Error = ShortCryptUrlComponentError; 208 | 209 | #[inline] 210 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 211 | ShortCryptUrlComponent::from_str(param) 212 | } 213 | } 214 | 215 | #[cfg(feature = "serdely")] 216 | struct StringVisitor; 217 | 218 | #[cfg(feature = "serdely")] 219 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 220 | type Value = ShortCryptUrlComponent; 221 | 222 | #[inline] 223 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 224 | formatter.write_str("a ShortCrypt URL component string") 225 | } 226 | 227 | #[inline] 228 | fn visit_str(self, v: &str) -> Result 229 | where 230 | E: ::serde::de::Error, { 231 | ShortCryptUrlComponent::from_str(v).map_err(|err| E::custom(err.to_string())) 232 | } 233 | 234 | #[inline] 235 | fn visit_string(self, v: String) -> Result 236 | where 237 | E: ::serde::de::Error, { 238 | ShortCryptUrlComponent::from_string(v).map_err(|err| E::custom(err.to_string())) 239 | } 240 | } 241 | 242 | #[cfg(feature = "serdely")] 243 | impl<'de> ::serde::Deserialize<'de> for ShortCryptUrlComponent { 244 | #[inline] 245 | fn deserialize(deserializer: D) -> Result 246 | where 247 | D: ::serde::Deserializer<'de>, { 248 | deserializer.deserialize_string(StringVisitor) 249 | } 250 | } 251 | 252 | #[cfg(feature = "serdely")] 253 | impl ::serde::Serialize for ShortCryptUrlComponent { 254 | #[inline] 255 | fn serialize(&self, serializer: S) -> Result 256 | where 257 | S: ::serde::Serializer, { 258 | serializer.serialize_str(&self.short_crypt_url_component) 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/text/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::ops::Deref; 9 | use std::str::{FromStr, Utf8Error}; 10 | 11 | lazy_static! { 12 | static ref TEXT_RE: Regex = Regex::new("^([^\x00-\x08\x0A-\x1F\x7F]|\r\n|\n\r|\n)*$").unwrap(); 13 | } 14 | 15 | #[derive(Debug, PartialEq, Clone)] 16 | pub enum TextError { 17 | IncorrectFormat, 18 | UTF8Error(Utf8Error), 19 | } 20 | 21 | impl Display for TextError { 22 | #[inline] 23 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 24 | Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl Error for TextError {} 29 | 30 | impl From for TextError { 31 | #[inline] 32 | fn from(err: Utf8Error) -> Self { 33 | TextError::UTF8Error(err) 34 | } 35 | } 36 | 37 | pub type TextResult = Result; 38 | 39 | #[derive(Debug, PartialEq)] 40 | pub struct TextValidator {} 41 | 42 | #[derive(Clone, PartialEq, Eq, Hash)] 43 | pub struct Text { 44 | text: String, 45 | } 46 | 47 | impl Text { 48 | #[inline] 49 | pub fn get_text(&self) -> &str { 50 | &self.text 51 | } 52 | 53 | #[inline] 54 | pub fn into_string(self) -> String { 55 | self.text 56 | } 57 | 58 | #[allow(clippy::missing_safety_doc)] 59 | #[inline] 60 | pub unsafe fn from_string_unchecked(text: String) -> Text { 61 | Text { 62 | text, 63 | } 64 | } 65 | } 66 | 67 | impl Deref for Text { 68 | type Target = str; 69 | 70 | #[inline] 71 | fn deref(&self) -> &Self::Target { 72 | &self.text 73 | } 74 | } 75 | 76 | impl Validated for Text {} 77 | 78 | impl Debug for Text { 79 | #[inline] 80 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 81 | impl_debug_for_tuple_struct!(Text, f, self, let .0 = self.text); 82 | } 83 | } 84 | 85 | impl Display for Text { 86 | #[inline] 87 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 88 | f.write_str(&self.text)?; 89 | Ok(()) 90 | } 91 | } 92 | 93 | impl TextValidator { 94 | #[inline] 95 | pub fn is_text(&self, text: &str) -> bool { 96 | self.parse_inner(text).is_ok() 97 | } 98 | 99 | #[inline] 100 | pub fn parse_string(&self, text: String) -> TextResult { 101 | let mut text_inner = self.parse_inner(&text)?; 102 | 103 | text_inner.text = text; 104 | 105 | Ok(text_inner) 106 | } 107 | 108 | #[inline] 109 | pub fn parse_str(&self, text: &str) -> TextResult { 110 | let mut text_inner = self.parse_inner(text)?; 111 | 112 | text_inner.text.push_str(text); 113 | 114 | Ok(text_inner) 115 | } 116 | 117 | #[inline] 118 | fn parse_inner(&self, text: &str) -> TextResult { 119 | if TEXT_RE.is_match(text) { 120 | Ok(Text { 121 | text: String::new(), 122 | }) 123 | } else { 124 | Err(TextError::IncorrectFormat) 125 | } 126 | } 127 | } 128 | 129 | #[cfg(test)] 130 | mod tests { 131 | use super::*; 132 | 133 | #[test] 134 | fn test_text_methods() { 135 | let text = "abc123ABC中文\r\n測試 123\n\rQQQ\n".to_string(); 136 | 137 | let bv = TextValidator {}; 138 | 139 | let text = bv.parse_string(text).unwrap(); 140 | 141 | assert_eq!("abc123ABC中文\r\n測試 123\n\rQQQ\n", text.get_text()); 142 | } 143 | 144 | #[test] 145 | fn test_text_lv1() { 146 | let text = "abc123ABC中文\n測試 123\n\n".to_string(); 147 | 148 | let bv = TextValidator {}; 149 | 150 | bv.parse_string(text).unwrap(); 151 | } 152 | } 153 | 154 | // Text's wrapper struct is itself 155 | impl ValidatedWrapper for Text { 156 | type Error = TextError; 157 | 158 | #[inline] 159 | fn from_string(text: String) -> Result { 160 | Text::from_string(text) 161 | } 162 | 163 | #[inline] 164 | fn from_str(text: &str) -> Result { 165 | Text::from_str(text) 166 | } 167 | } 168 | 169 | impl Text { 170 | #[inline] 171 | pub fn from_string(text: String) -> Result { 172 | Text::create_validator().parse_string(text) 173 | } 174 | 175 | #[inline] 176 | #[allow(clippy::should_implement_trait)] 177 | pub fn from_str(text: &str) -> Result { 178 | Text::create_validator().parse_str(text) 179 | } 180 | 181 | #[inline] 182 | fn create_validator() -> TextValidator { 183 | TextValidator {} 184 | } 185 | } 186 | 187 | impl FromStr for Text { 188 | type Err = TextError; 189 | 190 | #[inline] 191 | fn from_str(s: &str) -> Result { 192 | Text::from_str(s) 193 | } 194 | } 195 | 196 | #[cfg(feature = "rocketly")] 197 | impl<'a> ::rocket::request::FromParam<'a> for Text { 198 | type Error = TextError; 199 | 200 | #[inline] 201 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 202 | Text::from_string(param.url_decode()?) 203 | } 204 | } 205 | 206 | #[cfg(feature = "rocketly")] 207 | impl<'a> ::rocket::request::FromFormValue<'a> for Text { 208 | type Error = TextError; 209 | 210 | #[inline] 211 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 212 | Text::from_string(form_value.url_decode()?) 213 | } 214 | } 215 | 216 | #[cfg(feature = "serdely")] 217 | struct StringVisitor; 218 | 219 | #[cfg(feature = "serdely")] 220 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 221 | type Value = Text; 222 | 223 | #[inline] 224 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 225 | formatter.write_str("a Text string") 226 | } 227 | 228 | #[inline] 229 | fn visit_str(self, v: &str) -> Result 230 | where 231 | E: ::serde::de::Error, { 232 | Text::from_str(v).map_err(|err| E::custom(err.to_string())) 233 | } 234 | 235 | #[inline] 236 | fn visit_string(self, v: String) -> Result 237 | where 238 | E: ::serde::de::Error, { 239 | Text::from_string(v).map_err(|err| E::custom(err.to_string())) 240 | } 241 | } 242 | 243 | #[cfg(feature = "serdely")] 244 | impl<'de> ::serde::Deserialize<'de> for Text { 245 | #[inline] 246 | fn deserialize(deserializer: D) -> Result 247 | where 248 | D: ::serde::Deserializer<'de>, { 249 | deserializer.deserialize_str(StringVisitor) 250 | } 251 | } 252 | 253 | #[cfg(feature = "serdely")] 254 | impl ::serde::Serialize for Text { 255 | #[inline] 256 | fn serialize(&self, serializer: S) -> Result 257 | where 258 | S: ::serde::Serializer, { 259 | serializer.serialize_str(&self.text) 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/uri/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::str::{FromStr, Utf8Error}; 11 | 12 | lazy_static! { 13 | static ref URI_RE: Regex = { 14 | Regex::new(r"^(?i)([a-z][a-z0-9+.-]+):(//([^@]+@)?([a-z0-9.\-_~]+)(:\d+)?)?((?:[a-z0-9-._~]|%[a-f0-9]|[!$&'()*+,;=:@])+(?:/(?:[a-z0-9-._~]|%[a-f0-9]|[!$&'()*+,;=:@])*)*|(?:/(?:[a-z0-9-._~]|%[a-f0-9]|[!$&'()*+,;=:@])+)*)?(\?(?:[a-z0-9-._~]|%[a-f0-9]|[!$&'()*+,;=:@]|[/?])+)?(\#(?:[a-z0-9-._~]|%[a-f0-9]|[!$&'()*+,;=:@]|[/?])+)?$").unwrap() 15 | }; 16 | } 17 | 18 | #[derive(Debug, PartialEq, Clone)] 19 | pub enum URIError { 20 | IncorrectFormat, 21 | UTF8Error(Utf8Error), 22 | } 23 | 24 | impl Display for URIError { 25 | #[inline] 26 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 27 | Debug::fmt(self, f) 28 | } 29 | } 30 | 31 | impl Error for URIError {} 32 | 33 | impl From for URIError { 34 | #[inline] 35 | fn from(err: Utf8Error) -> Self { 36 | URIError::UTF8Error(err) 37 | } 38 | } 39 | 40 | pub type URIResult = Result; 41 | 42 | #[derive(Debug, PartialEq)] 43 | pub struct URIValidator {} 44 | 45 | #[derive(Clone)] 46 | pub struct URI { 47 | full_uri: String, 48 | scheme: (usize, usize), 49 | authority: Option<(usize, usize)>, 50 | user_info: Option<(usize, usize)>, 51 | host: Option<(usize, usize)>, 52 | port: Option, 53 | path: Option<(usize, usize)>, 54 | query: Option<(usize, usize)>, 55 | fragment: Option<(usize, usize)>, 56 | } 57 | 58 | impl URI { 59 | #[inline] 60 | pub fn get_full_uri(&self) -> &str { 61 | &self.full_uri 62 | } 63 | 64 | #[inline] 65 | pub fn get_scheme(&self) -> &str { 66 | &self.full_uri[self.scheme.0..self.scheme.1] 67 | } 68 | 69 | #[inline] 70 | pub fn get_authority(&self) -> Option<&str> { 71 | if let Some(authority) = self.authority { 72 | Some(&self.full_uri[authority.0..authority.1]) 73 | } else { 74 | None 75 | } 76 | } 77 | 78 | #[inline] 79 | pub fn get_user_info(&self) -> Option<&str> { 80 | if let Some(user_info) = self.user_info { 81 | Some(&self.full_uri[user_info.0..user_info.1]) 82 | } else { 83 | None 84 | } 85 | } 86 | 87 | #[inline] 88 | pub fn get_host(&self) -> Option<&str> { 89 | if let Some(host) = self.host { 90 | Some(&self.full_uri[host.0..host.1]) 91 | } else { 92 | None 93 | } 94 | } 95 | 96 | #[inline] 97 | pub fn get_port(&self) -> Option { 98 | self.port 99 | } 100 | 101 | #[inline] 102 | pub fn get_path(&self) -> Option<&str> { 103 | if let Some(path) = self.path { 104 | Some(&self.full_uri[path.0..path.1]) 105 | } else { 106 | None 107 | } 108 | } 109 | 110 | #[inline] 111 | pub fn get_query(&self) -> Option<&str> { 112 | if let Some(query) = self.query { 113 | Some(&self.full_uri[query.0..query.1]) 114 | } else { 115 | None 116 | } 117 | } 118 | 119 | #[inline] 120 | pub fn get_fragment(&self) -> Option<&str> { 121 | if let Some(fragment) = self.fragment { 122 | Some(&self.full_uri[fragment.0..fragment.1]) 123 | } else { 124 | None 125 | } 126 | } 127 | 128 | #[inline] 129 | pub fn into_string(self) -> String { 130 | self.full_uri 131 | } 132 | } 133 | 134 | impl Deref for URI { 135 | type Target = str; 136 | 137 | #[inline] 138 | fn deref(&self) -> &Self::Target { 139 | &self.full_uri 140 | } 141 | } 142 | 143 | impl Validated for URI {} 144 | 145 | impl Debug for URI { 146 | #[inline] 147 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 148 | impl_debug_for_tuple_struct!(URI, f, self, let .0 = self.full_uri); 149 | } 150 | } 151 | 152 | impl Display for URI { 153 | #[inline] 154 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 155 | f.write_str(&self.full_uri)?; 156 | Ok(()) 157 | } 158 | } 159 | 160 | impl PartialEq for URI { 161 | #[inline] 162 | fn eq(&self, other: &Self) -> bool { 163 | self.full_uri.eq(&other.full_uri) 164 | } 165 | } 166 | 167 | impl Eq for URI {} 168 | 169 | impl Hash for URI { 170 | #[inline] 171 | fn hash(&self, state: &mut H) { 172 | self.full_uri.hash(state) 173 | } 174 | } 175 | 176 | impl URIValidator { 177 | #[inline] 178 | pub fn is_uri(&self, full_uri: &str) -> bool { 179 | self.parse_inner(full_uri).is_ok() 180 | } 181 | 182 | #[inline] 183 | pub fn parse_string(&self, full_uri: String) -> URIResult { 184 | let mut uri_inner = self.parse_inner(&full_uri)?; 185 | 186 | uri_inner.full_uri = full_uri; 187 | 188 | Ok(uri_inner) 189 | } 190 | 191 | #[inline] 192 | pub fn parse_str(&self, full_uri: &str) -> URIResult { 193 | let mut uri_inner = self.parse_inner(full_uri)?; 194 | 195 | uri_inner.full_uri.push_str(full_uri); 196 | 197 | Ok(uri_inner) 198 | } 199 | 200 | fn parse_inner(&self, full_uri: &str) -> URIResult { 201 | let c = match URI_RE.captures(full_uri) { 202 | Some(c) => c, 203 | None => return Err(URIError::IncorrectFormat), 204 | }; 205 | 206 | let scheme = match c.get(1) { 207 | Some(cc) => (cc.start(), cc.end()), 208 | None => unreachable!(), 209 | }; 210 | 211 | let authority = match c.get(2) { 212 | Some(cc) => Some((cc.start() + 2, cc.end())), 213 | None => None, 214 | }; 215 | 216 | let user_info = match c.get(3) { 217 | Some(cc) => Some((cc.start(), cc.end() - 1)), 218 | None => None, 219 | }; 220 | 221 | let host = match c.get(4) { 222 | Some(cc) => Some((cc.start(), cc.end())), 223 | None => None, 224 | }; 225 | 226 | let port = match c.get(5) { 227 | Some(cc) => Some(full_uri[(cc.start() + 1)..cc.end()].parse().unwrap()), 228 | None => None, 229 | }; 230 | 231 | let path = match c.get(6) { 232 | Some(cc) => Some((cc.start(), cc.end())), 233 | None => None, 234 | }; 235 | 236 | let query = match c.get(7) { 237 | Some(cc) => Some((cc.start() + 1, cc.end())), 238 | None => None, 239 | }; 240 | 241 | let fragment = match c.get(8) { 242 | Some(cc) => Some((cc.start() + 1, cc.end())), 243 | None => None, 244 | }; 245 | 246 | Ok(URI { 247 | full_uri: String::new(), 248 | scheme, 249 | authority, 250 | user_info, 251 | host, 252 | port, 253 | path, 254 | query, 255 | fragment, 256 | }) 257 | } 258 | } 259 | 260 | #[cfg(test)] 261 | mod tests { 262 | use super::*; 263 | 264 | #[test] 265 | fn test_uri_methods() { 266 | let uri = "ssh://root@127.0.0.1:886/path/to?query=1#fragment".to_string(); 267 | 268 | let uv = URIValidator {}; 269 | 270 | let uri = uv.parse_string(uri).unwrap(); 271 | 272 | assert_eq!("ssh://root@127.0.0.1:886/path/to?query=1#fragment", uri.get_full_uri()); 273 | assert_eq!("ssh", uri.get_scheme()); 274 | assert_eq!("root@127.0.0.1:886", uri.get_authority().unwrap()); 275 | assert_eq!("root", uri.get_user_info().unwrap()); 276 | assert_eq!("127.0.0.1", uri.get_host().unwrap()); 277 | assert_eq!(886, uri.get_port().unwrap()); 278 | assert_eq!("/path/to", uri.get_path().unwrap()); 279 | assert_eq!("query=1", uri.get_query().unwrap()); 280 | assert_eq!("fragment", uri.get_fragment().unwrap()); 281 | } 282 | } 283 | 284 | // URI's wrapper struct is itself 285 | impl ValidatedWrapper for URI { 286 | type Error = URIError; 287 | 288 | #[inline] 289 | fn from_string(full_uri: String) -> Result { 290 | URI::from_string(full_uri) 291 | } 292 | 293 | #[inline] 294 | fn from_str(full_uri: &str) -> Result { 295 | URI::from_str(full_uri) 296 | } 297 | } 298 | 299 | impl URI { 300 | #[inline] 301 | pub fn from_string(full_uri: String) -> Result { 302 | URI::create_validator().parse_string(full_uri) 303 | } 304 | 305 | #[inline] 306 | #[allow(clippy::should_implement_trait)] 307 | pub fn from_str(full_uri: &str) -> Result { 308 | URI::create_validator().parse_str(full_uri) 309 | } 310 | 311 | #[inline] 312 | fn create_validator() -> URIValidator { 313 | URIValidator {} 314 | } 315 | } 316 | 317 | impl FromStr for URI { 318 | type Err = URIError; 319 | 320 | #[inline] 321 | fn from_str(s: &str) -> Result { 322 | URI::from_str(s) 323 | } 324 | } 325 | 326 | #[cfg(feature = "rocketly")] 327 | impl<'a> ::rocket::request::FromFormValue<'a> for URI { 328 | type Error = URIError; 329 | 330 | #[inline] 331 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 332 | URI::from_string(form_value.url_decode()?) 333 | } 334 | } 335 | 336 | #[cfg(feature = "rocketly")] 337 | impl<'a> ::rocket::request::FromParam<'a> for URI { 338 | type Error = URIError; 339 | 340 | #[inline] 341 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 342 | URI::from_string(param.url_decode()?) 343 | } 344 | } 345 | 346 | #[cfg(feature = "serdely")] 347 | struct StringVisitor; 348 | 349 | #[cfg(feature = "serdely")] 350 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 351 | type Value = URI; 352 | 353 | #[inline] 354 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 355 | formatter.write_str("a Base64 string") 356 | } 357 | 358 | #[inline] 359 | fn visit_str(self, v: &str) -> Result 360 | where 361 | E: ::serde::de::Error, { 362 | URI::from_str(v).map_err(|err| E::custom(err.to_string())) 363 | } 364 | 365 | #[inline] 366 | fn visit_string(self, v: String) -> Result 367 | where 368 | E: ::serde::de::Error, { 369 | URI::from_string(v).map_err(|err| E::custom(err.to_string())) 370 | } 371 | } 372 | 373 | #[cfg(feature = "serdely")] 374 | impl<'de> ::serde::Deserialize<'de> for URI { 375 | #[inline] 376 | fn deserialize(deserializer: D) -> Result 377 | where 378 | D: ::serde::Deserializer<'de>, { 379 | deserializer.deserialize_string(StringVisitor) 380 | } 381 | } 382 | 383 | #[cfg(feature = "serdely")] 384 | impl ::serde::Serialize for URI { 385 | #[inline] 386 | fn serialize(&self, serializer: S) -> Result 387 | where 388 | S: ::serde::Serializer, { 389 | serializer.serialize_str(&self.full_uri) 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/uuid/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper, ValidatorOption}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::str::Utf8Error; 11 | 12 | lazy_static! { 13 | static ref UUID_UPPERCASE_RE: Regex = 14 | Regex::new("^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$").unwrap(); 15 | static ref UUID_LOWERCASE_RE: Regex = 16 | Regex::new("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$").unwrap(); 17 | static ref UUID_RE: Regex = 18 | Regex::new("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") 19 | .unwrap(); 20 | } 21 | 22 | #[derive(Debug, PartialEq, Clone)] 23 | pub enum UUIDError { 24 | IncorrectFormat, 25 | UTF8Error(Utf8Error), 26 | } 27 | 28 | impl Display for UUIDError { 29 | #[inline] 30 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 31 | Debug::fmt(self, f) 32 | } 33 | } 34 | 35 | impl Error for UUIDError {} 36 | 37 | impl From for UUIDError { 38 | #[inline] 39 | fn from(err: Utf8Error) -> Self { 40 | UUIDError::UTF8Error(err) 41 | } 42 | } 43 | 44 | pub type UUIDResult = Result; 45 | 46 | #[derive(Debug, PartialEq)] 47 | pub struct UUIDValidator { 48 | pub lowercase: ValidatorOption, 49 | } 50 | 51 | #[derive(Clone)] 52 | enum UUIDCase { 53 | Upper, 54 | Lower, 55 | Both, 56 | } 57 | 58 | #[derive(Clone)] 59 | pub struct UUID { 60 | uuid: String, 61 | case: UUIDCase, 62 | } 63 | 64 | impl UUID { 65 | #[inline] 66 | pub fn get_full_uuid(&self) -> &str { 67 | &self.uuid 68 | } 69 | 70 | #[inline] 71 | pub fn has_lowercase(&self) -> bool { 72 | match self.case { 73 | UUIDCase::Upper => false, 74 | UUIDCase::Lower => true, 75 | UUIDCase::Both => true, 76 | } 77 | } 78 | 79 | #[inline] 80 | pub fn has_uppercase(&self) -> bool { 81 | match self.case { 82 | UUIDCase::Upper => true, 83 | UUIDCase::Lower => false, 84 | UUIDCase::Both => true, 85 | } 86 | } 87 | 88 | #[inline] 89 | pub fn has_both_case(&self) -> bool { 90 | match self.case { 91 | UUIDCase::Both => true, 92 | _ => false, 93 | } 94 | } 95 | 96 | #[inline] 97 | pub fn into_string(self) -> String { 98 | self.uuid 99 | } 100 | } 101 | 102 | impl Deref for UUID { 103 | type Target = str; 104 | 105 | #[inline] 106 | fn deref(&self) -> &Self::Target { 107 | &self.uuid 108 | } 109 | } 110 | 111 | impl Validated for UUID {} 112 | 113 | impl Debug for UUID { 114 | #[inline] 115 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 116 | impl_debug_for_tuple_struct!(UUID, f, self, let .0 = self.uuid); 117 | } 118 | } 119 | 120 | impl Display for UUID { 121 | #[inline] 122 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 123 | f.write_str(&self.uuid)?; 124 | Ok(()) 125 | } 126 | } 127 | 128 | impl PartialEq for UUID { 129 | #[inline] 130 | fn eq(&self, other: &Self) -> bool { 131 | self.uuid.eq(&other.uuid) 132 | } 133 | } 134 | 135 | impl Eq for UUID {} 136 | 137 | impl Hash for UUID { 138 | #[inline] 139 | fn hash(&self, state: &mut H) { 140 | self.uuid.hash(state) 141 | } 142 | } 143 | 144 | impl UUIDValidator { 145 | #[inline] 146 | pub fn is_uuid(&self, uuid: &str) -> bool { 147 | self.parse_inner(uuid).is_ok() 148 | } 149 | 150 | #[inline] 151 | pub fn parse_string(&self, uuid: String) -> UUIDResult { 152 | let mut uuid_inner = self.parse_inner(&uuid)?; 153 | 154 | uuid_inner.uuid = uuid; 155 | 156 | Ok(uuid_inner) 157 | } 158 | 159 | #[inline] 160 | pub fn parse_str(&self, uuid: &str) -> UUIDResult { 161 | let mut uuid_inner = self.parse_inner(uuid)?; 162 | 163 | uuid_inner.uuid.push_str(uuid); 164 | 165 | Ok(uuid_inner) 166 | } 167 | 168 | fn parse_inner(&self, uuid: &str) -> UUIDResult { 169 | if uuid.len() != 36 { 170 | Err(UUIDError::IncorrectFormat) 171 | } else { 172 | match self.lowercase { 173 | ValidatorOption::Must => { 174 | if UUID_LOWERCASE_RE.is_match(uuid) { 175 | Ok(UUID { 176 | uuid: String::new(), 177 | case: UUIDCase::Lower, 178 | }) 179 | } else { 180 | Err(UUIDError::IncorrectFormat) 181 | } 182 | } 183 | ValidatorOption::NotAllow => { 184 | if UUID_UPPERCASE_RE.is_match(uuid) { 185 | Ok(UUID { 186 | uuid: String::new(), 187 | case: UUIDCase::Upper, 188 | }) 189 | } else { 190 | Err(UUIDError::IncorrectFormat) 191 | } 192 | } 193 | ValidatorOption::Allow => { 194 | if UUID_UPPERCASE_RE.is_match(uuid) { 195 | Ok(UUID { 196 | uuid: String::new(), 197 | case: UUIDCase::Upper, 198 | }) 199 | } else if UUID_LOWERCASE_RE.is_match(uuid) { 200 | Ok(UUID { 201 | uuid: String::new(), 202 | case: UUIDCase::Lower, 203 | }) 204 | } else if UUID_RE.is_match(uuid) { 205 | Ok(UUID { 206 | uuid: String::new(), 207 | case: UUIDCase::Both, 208 | }) 209 | } else { 210 | Err(UUIDError::IncorrectFormat) 211 | } 212 | } 213 | } 214 | } 215 | } 216 | } 217 | 218 | #[cfg(test)] 219 | mod tests { 220 | use super::*; 221 | 222 | #[test] 223 | fn test_uuid_methods() { 224 | let uuid = "80a6572b-ebb8-4bf8-94b8-5c198299d118".to_string(); 225 | 226 | let uv = UUIDValidator { 227 | lowercase: ValidatorOption::Must, 228 | }; 229 | 230 | let uuid = uv.parse_string(uuid).unwrap(); 231 | 232 | assert_eq!("80a6572b-ebb8-4bf8-94b8-5c198299d118", uuid.get_full_uuid()); 233 | } 234 | 235 | #[test] 236 | fn test_uuid_lv1() { 237 | let uuid = "80a6572b-ebb8-4bf8-94b8-5c198299d118".to_string(); 238 | 239 | let bv = UUIDValidator { 240 | lowercase: ValidatorOption::Allow, 241 | }; 242 | 243 | bv.parse_string(uuid).unwrap(); 244 | } 245 | 246 | #[test] 247 | fn test_uuid_lv2() { 248 | let uuid = "80A6572B-EBB8-4BF8-94B8-5C198299D118".to_string(); 249 | 250 | let bv = UUIDValidator { 251 | lowercase: ValidatorOption::NotAllow, 252 | }; 253 | 254 | bv.parse_string(uuid).unwrap(); 255 | } 256 | } 257 | 258 | // TODO ---------- 259 | 260 | macro_rules! extend { 261 | ($name:ident, $lowercase:expr) => { 262 | #[derive(Clone, PartialEq, Eq, Hash)] 263 | pub struct $name(UUID); 264 | 265 | impl From<$name> for UUID { 266 | #[inline] 267 | fn from(d: $name) -> Self { 268 | d.0 269 | } 270 | } 271 | 272 | impl Deref for $name { 273 | type Target = str; 274 | 275 | #[inline] 276 | fn deref(&self) -> &Self::Target { 277 | &self.0.uuid 278 | } 279 | } 280 | 281 | impl Validated for $name {} 282 | 283 | impl ValidatedWrapper for $name { 284 | type Error = UUIDError; 285 | 286 | #[inline] 287 | fn from_string(full_uuid: String) -> Result { 288 | $name::from_string(full_uuid) 289 | } 290 | 291 | #[inline] 292 | fn from_str(full_uuid: &str) -> Result { 293 | $name::from_str(full_uuid) 294 | } 295 | } 296 | 297 | impl Debug for $name { 298 | #[inline] 299 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 300 | f.write_fmt(format_args!("{}({})", stringify!($name), self.0))?; 301 | Ok(()) 302 | } 303 | } 304 | 305 | impl Display for $name { 306 | #[inline] 307 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 308 | Display::fmt(&self.0, f) 309 | } 310 | } 311 | 312 | impl $name { 313 | #[inline] 314 | pub fn from_string(full_uuid: String) -> Result<$name, UUIDError> { 315 | Ok($name($name::create_validator().parse_string(full_uuid)?)) 316 | } 317 | 318 | #[inline] 319 | #[allow(clippy::should_implement_trait)] 320 | pub fn from_str(full_uuid: &str) -> Result<$name, UUIDError> { 321 | Ok($name($name::create_validator().parse_str(full_uuid)?)) 322 | } 323 | 324 | #[inline] 325 | pub fn from_uuid(uuid: UUID) -> Result<$name, UUIDError> { 326 | match $lowercase { 327 | ValidatorOption::Must => { 328 | if uuid.has_uppercase() { 329 | return Err(UUIDError::IncorrectFormat); 330 | } 331 | } 332 | ValidatorOption::NotAllow => { 333 | if uuid.has_lowercase() { 334 | return Err(UUIDError::IncorrectFormat); 335 | } 336 | } 337 | _ => (), 338 | } 339 | 340 | Ok($name(uuid)) 341 | } 342 | 343 | #[inline] 344 | pub fn into_uuid(self) -> UUID { 345 | self.0 346 | } 347 | 348 | #[inline] 349 | pub fn as_uuid(&self) -> &UUID { 350 | &self.0 351 | } 352 | 353 | #[inline] 354 | fn create_validator() -> UUIDValidator { 355 | UUIDValidator { 356 | lowercase: $lowercase, 357 | } 358 | } 359 | } 360 | 361 | impl $name { 362 | #[inline] 363 | pub fn get_full_uuid(&self) -> &str { 364 | self.0.get_full_uuid() 365 | } 366 | } 367 | 368 | impl std::str::FromStr for $name { 369 | type Err = UUIDError; 370 | 371 | #[inline] 372 | fn from_str(s: &str) -> Result<$name, UUIDError> { 373 | $name::from_str(s) 374 | } 375 | } 376 | 377 | #[cfg(feature = "rocketly")] 378 | impl<'a> ::rocket::request::FromFormValue<'a> for $name { 379 | type Error = UUIDError; 380 | 381 | #[inline] 382 | fn from_form_value( 383 | form_value: &'a ::rocket::http::RawStr, 384 | ) -> Result { 385 | $name::from_string(form_value.url_decode()?) 386 | } 387 | } 388 | 389 | #[cfg(feature = "rocketly")] 390 | impl<'a> ::rocket::request::FromParam<'a> for $name { 391 | type Error = UUIDError; 392 | 393 | #[inline] 394 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 395 | $name::from_string(param.url_decode()?) 396 | } 397 | } 398 | 399 | #[cfg(feature = "serdely")] 400 | impl<'de> ::serde::Deserialize<'de> for $name { 401 | #[inline] 402 | fn deserialize(deserializer: D) -> Result 403 | where 404 | D: ::serde::Deserializer<'de>, { 405 | struct StringVisitor; 406 | 407 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 408 | type Value = $name; 409 | 410 | #[inline] 411 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 412 | formatter.write_fmt(format_args!( 413 | "a uuid({:?}) string", 414 | $name::create_validator() 415 | )) 416 | } 417 | 418 | #[inline] 419 | fn visit_str(self, v: &str) -> Result 420 | where 421 | E: ::serde::de::Error, { 422 | $name::from_str(v).map_err(|err| E::custom(err.to_string())) 423 | } 424 | 425 | #[inline] 426 | fn visit_string(self, v: String) -> Result 427 | where 428 | E: ::serde::de::Error, { 429 | $name::from_string(v).map_err(|err| E::custom(err.to_string())) 430 | } 431 | } 432 | 433 | deserializer.deserialize_string(StringVisitor) 434 | } 435 | } 436 | 437 | #[cfg(feature = "serdely")] 438 | impl ::serde::Serialize for $name { 439 | #[inline] 440 | fn serialize(&self, serializer: S) -> Result 441 | where 442 | S: ::serde::Serializer, { 443 | serializer.serialize_str(self.0.get_full_uuid()) 444 | } 445 | } 446 | }; 447 | } 448 | 449 | extend!(UUIDAllowAnyCase, ValidatorOption::Allow); 450 | 451 | impl UUIDAllowAnyCase { 452 | #[inline] 453 | pub fn has_lowercase(&self) -> bool { 454 | self.0.has_lowercase() 455 | } 456 | 457 | #[inline] 458 | pub fn has_uppercase(&self) -> bool { 459 | self.0.has_uppercase() 460 | } 461 | 462 | #[inline] 463 | pub fn has_both_case(&self) -> bool { 464 | self.0.has_both_case() 465 | } 466 | } 467 | 468 | extend!(UUIDUpperCase, ValidatorOption::NotAllow); 469 | 470 | impl UUIDUpperCase {} 471 | 472 | extend!(UUIDLowerCase, ValidatorOption::Must); 473 | 474 | impl UUIDLowerCase {} 475 | -------------------------------------------------------------------------------- /src/validator_option.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum ValidatorOption { 3 | Must, 4 | Allow, 5 | NotAllow, 6 | } 7 | 8 | impl ValidatorOption { 9 | #[inline] 10 | pub fn allow(&self) -> bool { 11 | match self { 12 | ValidatorOption::Must => true, 13 | ValidatorOption::Allow => true, 14 | ValidatorOption::NotAllow => false, 15 | } 16 | } 17 | 18 | #[inline] 19 | pub fn not_allow(&self) -> bool { 20 | match self { 21 | ValidatorOption::Must => false, 22 | ValidatorOption::Allow => false, 23 | ValidatorOption::NotAllow => true, 24 | } 25 | } 26 | 27 | #[inline] 28 | pub fn must(&self) -> bool { 29 | match self { 30 | ValidatorOption::Must => true, 31 | ValidatorOption::Allow => false, 32 | ValidatorOption::NotAllow => false, 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/version/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use self::regex::Regex; 4 | use super::{Validated, ValidatedWrapper}; 5 | 6 | use std::error::Error; 7 | use std::fmt::{self, Debug, Display, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::str::{FromStr, Utf8Error}; 11 | 12 | lazy_static! { 13 | static ref VERSION_RE: Regex = 14 | Regex::new(r"^(\d)+(\.(\d)+)?(\.(\d)+)?(-([^\x00-\x1F\x7F]+))?$").unwrap(); 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone)] 18 | pub enum VersionError { 19 | IncorrectFormat, 20 | UTF8Error(Utf8Error), 21 | } 22 | 23 | impl Display for VersionError { 24 | #[inline] 25 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 26 | Debug::fmt(self, f) 27 | } 28 | } 29 | 30 | impl Error for VersionError {} 31 | 32 | impl From for VersionError { 33 | #[inline] 34 | fn from(err: Utf8Error) -> Self { 35 | VersionError::UTF8Error(err) 36 | } 37 | } 38 | 39 | pub type VersionResult = Result; 40 | 41 | #[derive(Debug, PartialEq)] 42 | pub struct VersionValidator {} 43 | 44 | #[derive(Clone)] 45 | pub struct Version { 46 | full_version: String, 47 | major: u16, 48 | minor: Option, 49 | patch: Option, 50 | label: Option, 51 | } 52 | 53 | impl Version { 54 | #[inline] 55 | pub fn get_full_version(&self) -> &str { 56 | &self.full_version 57 | } 58 | 59 | #[inline] 60 | pub fn get_full_version_without_label(&self) -> &str { 61 | if let Some(label) = self.label { 62 | &self.full_version[..(label - 1)] 63 | } else { 64 | &self.full_version 65 | } 66 | } 67 | 68 | #[inline] 69 | pub fn get_label(&self) -> Option<&str> { 70 | if let Some(label) = self.label { 71 | Some(&self.full_version[label..]) 72 | } else { 73 | None 74 | } 75 | } 76 | 77 | #[inline] 78 | pub fn get_major(&self) -> u16 { 79 | self.major 80 | } 81 | 82 | #[inline] 83 | pub fn get_minor(&self) -> Option { 84 | self.minor 85 | } 86 | 87 | #[inline] 88 | pub fn get_patch(&self) -> Option { 89 | self.patch 90 | } 91 | 92 | #[inline] 93 | pub fn into_string(self) -> String { 94 | self.full_version 95 | } 96 | } 97 | 98 | impl Deref for Version { 99 | type Target = str; 100 | 101 | #[inline] 102 | fn deref(&self) -> &Self::Target { 103 | &self.full_version 104 | } 105 | } 106 | 107 | impl Validated for Version {} 108 | 109 | impl Debug for Version { 110 | #[inline] 111 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 112 | impl_debug_for_tuple_struct!(Version, f, self, let .0 = self.full_version); 113 | } 114 | } 115 | 116 | impl Display for Version { 117 | #[inline] 118 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 119 | f.write_str(&self.full_version)?; 120 | Ok(()) 121 | } 122 | } 123 | 124 | impl PartialEq for Version { 125 | #[inline] 126 | fn eq(&self, other: &Self) -> bool { 127 | self.full_version.eq(&other.full_version) 128 | } 129 | } 130 | 131 | impl Eq for Version {} 132 | 133 | impl Hash for Version { 134 | #[inline] 135 | fn hash(&self, state: &mut H) { 136 | self.full_version.hash(state) 137 | } 138 | } 139 | 140 | impl VersionValidator { 141 | #[inline] 142 | pub fn is_short_crypt_url_component_url(&self, short_crypt_url_component_url: &str) -> bool { 143 | self.parse_inner(short_crypt_url_component_url).is_ok() 144 | } 145 | 146 | #[inline] 147 | pub fn parse_string(&self, full_version: String) -> VersionResult { 148 | let mut full_version_inner = self.parse_inner(&full_version)?; 149 | 150 | full_version_inner.full_version = full_version; 151 | 152 | Ok(full_version_inner) 153 | } 154 | 155 | #[inline] 156 | pub fn parse_str(&self, full_version: &str) -> VersionResult { 157 | let mut full_version_inner = self.parse_inner(full_version)?; 158 | 159 | full_version_inner.full_version.push_str(full_version); 160 | 161 | Ok(full_version_inner) 162 | } 163 | 164 | fn parse_inner(&self, full_version: &str) -> VersionResult { 165 | let c = match VERSION_RE.captures(full_version) { 166 | Some(c) => c, 167 | None => return Err(VersionError::IncorrectFormat), 168 | }; 169 | 170 | let major = match c.get(1) { 171 | Some(m) => { 172 | match m.as_str().parse::() { 173 | Ok(m) => m, 174 | Err(_) => { 175 | return Err(VersionError::IncorrectFormat); 176 | } 177 | } 178 | } 179 | None => unreachable!(), 180 | }; 181 | 182 | let minor = match c.get(3) { 183 | Some(m) => { 184 | match m.as_str().parse::() { 185 | Ok(m) => Some(m), 186 | Err(_) => { 187 | return Err(VersionError::IncorrectFormat); 188 | } 189 | } 190 | } 191 | None => None, 192 | }; 193 | 194 | let patch = match c.get(5) { 195 | Some(m) => { 196 | match m.as_str().parse::() { 197 | Ok(m) => Some(m), 198 | Err(_) => { 199 | return Err(VersionError::IncorrectFormat); 200 | } 201 | } 202 | } 203 | None => None, 204 | }; 205 | 206 | let label = match c.get(7) { 207 | Some(m) => Some(m.start()), 208 | None => None, 209 | }; 210 | 211 | Ok(Version { 212 | full_version: String::new(), 213 | major, 214 | minor, 215 | patch, 216 | label, 217 | }) 218 | } 219 | } 220 | 221 | #[cfg(test)] 222 | mod tests { 223 | use super::*; 224 | 225 | #[test] 226 | fn test_version_methods() { 227 | let version = "1.2.3-alpha.1".to_string(); 228 | 229 | let vv = VersionValidator {}; 230 | 231 | let version = vv.parse_string(version).unwrap(); 232 | 233 | assert_eq!("1.2.3-alpha.1", version.get_full_version()); 234 | assert_eq!("1.2.3", version.get_full_version_without_label()); 235 | assert_eq!(1, version.get_major()); 236 | assert_eq!(2, version.get_minor().unwrap()); 237 | assert_eq!(3, version.get_patch().unwrap()); 238 | assert_eq!("alpha.1", version.get_label().unwrap()); 239 | } 240 | 241 | #[test] 242 | fn test_version_lv1() { 243 | let version = "0.1.2".to_string(); 244 | 245 | let vv = VersionValidator {}; 246 | 247 | vv.parse_string(version).unwrap(); 248 | } 249 | } 250 | 251 | // Version's wrapper struct is itself 252 | impl ValidatedWrapper for Version { 253 | type Error = VersionError; 254 | 255 | #[inline] 256 | fn from_string(full_version: String) -> Result { 257 | Version::from_string(full_version) 258 | } 259 | 260 | #[inline] 261 | fn from_str(full_version: &str) -> Result { 262 | Version::from_str(full_version) 263 | } 264 | } 265 | 266 | impl Version { 267 | #[inline] 268 | pub fn from_string(full_version: String) -> Result { 269 | Version::create_validator().parse_string(full_version) 270 | } 271 | 272 | #[inline] 273 | #[allow(clippy::should_implement_trait)] 274 | pub fn from_str(full_version: &str) -> Result { 275 | Version::create_validator().parse_str(full_version) 276 | } 277 | 278 | #[inline] 279 | fn create_validator() -> VersionValidator { 280 | VersionValidator {} 281 | } 282 | } 283 | 284 | impl FromStr for Version { 285 | type Err = VersionError; 286 | 287 | #[inline] 288 | fn from_str(s: &str) -> Result { 289 | Version::from_str(s) 290 | } 291 | } 292 | 293 | #[cfg(feature = "rocketly")] 294 | impl<'a> ::rocket::request::FromFormValue<'a> for Version { 295 | type Error = VersionError; 296 | 297 | #[inline] 298 | fn from_form_value(form_value: &'a ::rocket::http::RawStr) -> Result { 299 | Version::from_string(form_value.url_decode()?) 300 | } 301 | } 302 | 303 | #[cfg(feature = "rocketly")] 304 | impl<'a> ::rocket::request::FromParam<'a> for Version { 305 | type Error = VersionError; 306 | 307 | #[inline] 308 | fn from_param(param: &'a ::rocket::http::RawStr) -> Result { 309 | Version::from_string(param.url_decode()?) 310 | } 311 | } 312 | 313 | #[cfg(feature = "serdely")] 314 | struct StringVisitor; 315 | 316 | #[cfg(feature = "serdely")] 317 | impl<'de> ::serde::de::Visitor<'de> for StringVisitor { 318 | type Value = Version; 319 | 320 | #[inline] 321 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 322 | formatter.write_str("a version string") 323 | } 324 | 325 | #[inline] 326 | fn visit_str(self, v: &str) -> Result 327 | where 328 | E: ::serde::de::Error, { 329 | Version::from_str(v).map_err(|err| E::custom(err.to_string())) 330 | } 331 | 332 | #[inline] 333 | fn visit_string(self, v: String) -> Result 334 | where 335 | E: ::serde::de::Error, { 336 | Version::from_string(v).map_err(|err| E::custom(err.to_string())) 337 | } 338 | } 339 | 340 | #[cfg(feature = "serdely")] 341 | impl<'de> ::serde::Deserialize<'de> for Version { 342 | #[inline] 343 | fn deserialize(deserializer: D) -> Result 344 | where 345 | D: ::serde::Deserializer<'de>, { 346 | deserializer.deserialize_string(StringVisitor) 347 | } 348 | } 349 | 350 | #[cfg(feature = "serdely")] 351 | impl ::serde::Serialize for Version { 352 | #[inline] 353 | fn serialize(&self, serializer: S) -> Result 354 | where 355 | S: ::serde::Serializer, { 356 | serializer.serialize_str(&self.full_version) 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /tests/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate validators; 3 | #[macro_use] 4 | extern crate lazy_static; 5 | 6 | extern crate regex; 7 | 8 | use std::collections::HashSet; 9 | 10 | use regex::Regex; 11 | 12 | #[cfg(feature = "phone-number")] 13 | use validators::PhoneNumberCountry; 14 | 15 | #[test] 16 | fn validated_customized_string() { 17 | validated_customized_string!(S1, 18 | from_string input { 19 | Ok(input) 20 | }, 21 | from_str input { 22 | Ok(input.to_string()) 23 | } 24 | ); 25 | 26 | validated_customized_string!(pub S2, 27 | from_string input { 28 | Ok(input) 29 | }, 30 | from_str input { 31 | Ok(input.to_string()) 32 | } 33 | ); 34 | } 35 | 36 | #[test] 37 | fn validated_customized_regex_string() { 38 | validated_customized_regex_string!(S1, "^(Hi|Hello)$"); 39 | validated_customized_regex_string!(pub S2, r"^[\S\s]+$"); 40 | } 41 | 42 | #[test] 43 | fn validated_customized_regex_string_static() { 44 | lazy_static! { 45 | static ref RE_S1: Regex = { Regex::new("^(Hi|Hello)$").unwrap() }; 46 | } 47 | 48 | lazy_static! { 49 | static ref RE_S2: Regex = { Regex::new(r"^[\S\s]+$").unwrap() }; 50 | } 51 | 52 | validated_customized_regex_string!(S1, ref RE_S1); 53 | validated_customized_regex_string!(pub S2, ref RE_S2); 54 | } 55 | 56 | #[test] 57 | fn validated_customized_number() { 58 | validated_customized_number!(N1, u8, 59 | from_string _input { 60 | Ok(5) 61 | }, 62 | from_str _input { 63 | Ok(6) 64 | }, 65 | from_number input { 66 | Ok(input) 67 | } 68 | ); 69 | 70 | validated_customized_number!(pub N2, u16, 71 | from_string _input { 72 | Ok(5) 73 | }, 74 | from_str _input { 75 | Ok(6) 76 | }, 77 | from_number input { 78 | Ok(input) 79 | } 80 | ); 81 | } 82 | 83 | #[test] 84 | fn validated_customized_regex_number() { 85 | validated_customized_regex_number!(N1, u8, r"^[1-8][0-9]$"); 86 | validated_customized_regex_number!(pub N2, u16, r"^[0-1]?[1-8][0-9]$"); 87 | validated_customized_regex_number!(N3, f32, r"^[0-1]?[1-8][0-9]\.[0-9]$"); 88 | } 89 | 90 | #[test] 91 | fn validated_customized_regex_number_static() { 92 | lazy_static! { 93 | static ref RE_N1: Regex = { Regex::new(r"^[1-8][0-9]$").unwrap() }; 94 | } 95 | 96 | lazy_static! { 97 | static ref RE_N2: Regex = { Regex::new(r"^[0-1]?[1-8][0-9]$").unwrap() }; 98 | } 99 | 100 | lazy_static! { 101 | static ref RE_N3: Regex = { Regex::new(r"^[0-1]?[1-8][0-9]\.[0-9]$").unwrap() }; 102 | } 103 | 104 | validated_customized_regex_number!(N1, u8, ref RE_N1); 105 | validated_customized_regex_number!(pub N2, u16, ref RE_N2); 106 | validated_customized_regex_number!(N3, f32, ref RE_N3); 107 | } 108 | 109 | #[test] 110 | fn validated_customized_ranged_number() { 111 | validated_customized_ranged_number!(N1, u8, 0, 100); 112 | validated_customized_ranged_number!(pub N2, u16, 3, 46); 113 | validated_customized_ranged_number!(N3, f32, -45.5, 80.0); 114 | } 115 | 116 | #[test] 117 | fn validated_customized_primitive_number() { 118 | validated_customized_primitive_number!(N1, u8); 119 | validated_customized_primitive_number!(pub N2, u8); 120 | validated_customized_primitive_number!(N3, f32); 121 | } 122 | 123 | #[test] 124 | fn validated_customized_vec() { 125 | validated_customized_vec!(V1, 126 | from_string _input { 127 | Ok(Vec::new()) 128 | }, 129 | from_str _input { 130 | Ok(Vec::new()) 131 | }, 132 | from_vec input { 133 | Ok(input) 134 | } 135 | ); 136 | 137 | validated_customized_vec!(pub V2, 138 | from_string _input { 139 | Ok(Vec::new()) 140 | }, 141 | from_str _input { 142 | Ok(Vec::new()) 143 | }, 144 | from_vec input { 145 | Ok(input) 146 | } 147 | ); 148 | } 149 | 150 | #[test] 151 | fn validated_customized_ranged_length_vec() { 152 | validated_customized_ranged_length_vec!(V1, 0, 10); 153 | validated_customized_ranged_length_vec!(V2, 5); 154 | validated_customized_ranged_length_vec!(pub V3, 0, 10); 155 | validated_customized_ranged_length_vec!(pub V4, 5); 156 | } 157 | 158 | #[test] 159 | fn validated_customized_hash_set() { 160 | validated_customized_hash_set!(S1, 161 | from_string _input { 162 | Ok(HashSet::new()) 163 | }, 164 | from_str _input { 165 | Ok(HashSet::new()) 166 | }, 167 | from_hash_set input { 168 | Ok(input) 169 | } 170 | ); 171 | 172 | validated_customized_hash_set!(pub S2, 173 | from_string _input { 174 | Ok(HashSet::new()) 175 | }, 176 | from_str _input { 177 | Ok(HashSet::new()) 178 | }, 179 | from_hash_set input { 180 | Ok(input) 181 | } 182 | ); 183 | } 184 | 185 | #[test] 186 | fn validated_customized_ranged_length_hash_set() { 187 | validated_customized_ranged_length_hash_set!(S1, 0, 10); 188 | validated_customized_ranged_length_hash_set!(S2, 5); 189 | validated_customized_ranged_length_hash_set!(pub S3, 0, 10); 190 | validated_customized_ranged_length_hash_set!(pub S4, 5); 191 | } 192 | 193 | #[cfg(feature = "phone-number")] 194 | #[test] 195 | fn validated_customized_phone_number() { 196 | validated_customized_phone_number!(P1, PhoneNumberCountry::TW); 197 | validated_customized_phone_number!(pub P2, PhoneNumberCountry::TW, PhoneNumberCountry::US); 198 | validated_customized_phone_number!(P3); 199 | 200 | let phone_number = P1::from_str("0912345678").unwrap(); 201 | assert_eq!("0912345678", phone_number.get_full_phone_number()); 202 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::TW)); 203 | 204 | let phone_number = P2::from_str("626-555-1212").unwrap(); 205 | assert_eq!("626-555-1212", phone_number.get_full_phone_number()); 206 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::US)); 207 | 208 | let phone_number = P3::from_str("626-555-1212").unwrap(); 209 | assert_eq!("626-555-1212", phone_number.get_full_phone_number()); 210 | assert!(phone_number.get_countries().contains(&PhoneNumberCountry::US)); 211 | } 212 | -------------------------------------------------------------------------------- /tests/rocket.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "rocketly-test")] 2 | #![feature(proc_macro_hygiene, decl_macro)] 3 | 4 | #[macro_use] 5 | extern crate validators; 6 | 7 | #[macro_use] 8 | extern crate rocket; 9 | 10 | use validators::domain::DomainLocalhostableWithPort; 11 | use validators::email::Email; 12 | 13 | #[test] 14 | fn from_form_value() { 15 | validated_customized_regex_string!(Lang, r"^(us|cn|tw)$"); 16 | validated_customized_ranged_number!(PersonAge, u8, 0, 130); 17 | 18 | #[derive(FromForm)] 19 | struct Model1 { 20 | _a: DomainLocalhostableWithPort, 21 | _b: Email, 22 | } 23 | 24 | let _m1 = Model1 { 25 | _a: DomainLocalhostableWithPort::from_str("localhost:8080").unwrap(), 26 | _b: Email::from_str("len@magiclen.org").unwrap(), 27 | }; 28 | 29 | #[derive(FromForm)] 30 | struct Model2 { 31 | _a: Lang, 32 | } 33 | 34 | let _m2 = Model2 { 35 | _a: Lang::from_str("tw").unwrap(), 36 | }; 37 | 38 | #[derive(FromForm)] 39 | struct Model3 { 40 | _a: PersonAge, 41 | } 42 | 43 | let _m3 = Model3 { 44 | _a: PersonAge::from_str("18").unwrap(), 45 | }; 46 | } 47 | 48 | #[test] 49 | #[allow(dead_code)] 50 | fn from_param() { 51 | validated_customized_ranged_number!(PersonID, u8, 0, 130); 52 | 53 | #[get("/person/<_id>")] 54 | fn get(_id: PersonID) -> &'static str { 55 | "test" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/serde.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "serdely-test")] 2 | 3 | #[macro_use] 4 | extern crate validators; 5 | #[macro_use] 6 | extern crate serde_json; 7 | 8 | use validators::base32::Base32; 9 | use validators::domain::DomainUnlocalhostableWithoutPort; 10 | 11 | #[test] 12 | fn de_1() { 13 | serde_json::from_str::("\"EB2GK43UEBWWK43TMFTWKCQK\"").unwrap(); 14 | } 15 | 16 | #[test] 17 | fn se_1() { 18 | let base32 = Base32::from_str("EB2GK43UEBWWK43TMFTWKCQK").unwrap(); 19 | 20 | assert_eq!("\"EB2GK43UEBWWK43TMFTWKCQK\"", json!(base32).to_string()); 21 | } 22 | 23 | #[test] 24 | fn de_2() { 25 | serde_json::from_str::("\"magiclen.org\"").unwrap(); 26 | } 27 | 28 | #[test] 29 | fn se_2() { 30 | let domain = DomainUnlocalhostableWithoutPort::from_str("magiclen.org").unwrap(); 31 | 32 | assert_eq!("\"magiclen.org\"", json!(domain).to_string()); 33 | } 34 | 35 | #[test] 36 | fn de_3() { 37 | validated_customized_regex_string!(S1, "^(Hi|Hello)$"); 38 | 39 | serde_json::from_str::("\"Hi\"").unwrap(); 40 | } 41 | 42 | #[test] 43 | fn se_3() { 44 | validated_customized_regex_string!(S1, "^(Hi|Hello)$"); 45 | 46 | let s = S1::from_str("Hello").unwrap(); 47 | 48 | assert_eq!("\"Hello\"", json!(s).to_string()); 49 | } 50 | 51 | #[test] 52 | fn de_4() { 53 | validated_customized_ranged_number!(Score, u8, 0, 100); 54 | 55 | serde_json::from_value::(serde_json::Value::from(23)).unwrap(); 56 | } 57 | 58 | #[test] 59 | fn se_4() { 60 | validated_customized_ranged_number!(Score, u8, 0, 100); 61 | 62 | let s = Score::from_number(23).unwrap(); 63 | 64 | assert_eq!("23", json!(s).to_string()); 65 | } 66 | 67 | #[test] 68 | fn de_5() { 69 | validated_customized_ranged_length_vec!(Greet, 1, 5); 70 | validated_customized_regex_string!(S1, "^(Hi|Hello)$"); 71 | 72 | let v = vec!["Hi", "Hello"]; 73 | 74 | serde_json::from_value::>(serde_json::Value::from(v)).unwrap(); 75 | } 76 | 77 | #[test] 78 | fn se_5() { 79 | validated_customized_regex_string!(S1, "^(Hi|Hello)$"); 80 | 81 | let s1 = S1::from_str("Hi").unwrap(); 82 | let s2 = S1::from_str("Hello").unwrap(); 83 | 84 | let v = vec![s1, s2]; 85 | 86 | assert_eq!("[\"Hi\",\"Hello\"]", json!(v).to_string()); 87 | } 88 | --------------------------------------------------------------------------------