protocheck_core/validators/
string.rs1use paste::paste;
2
3#[cfg(feature = "ip")]
4use super::well_known_strings::ip::*;
5use super::well_known_strings::*;
6use crate::{
7 field_data::FieldContext,
8 protovalidate::Violation,
9 validators::static_data::{base_violations::create_violation, strings_violations::*},
10};
11
12macro_rules! create_string_violation {
13 ($check:ident, $field_context:ident, $violation_name:ident, $error_message:expr) => {
14 create_violation!(
15 string,
16 $check,
17 $field_context,
18 $violation_name,
19 $error_message
20 )
21 };
22}
23
24macro_rules! well_known_rule {
25 (
26 $name:ident,
27 $definition:literal
28 ) => {
29 paste! {
30 pub fn $name(field_context: &FieldContext, value: &str) -> Result<(), Violation> {
31 let check = [<is_valid _ $name>](value);
32
33 create_string_violation!(check, field_context, $name, concat!("must be a valid ", $definition))
34 }
35 }
36 };
37}
38
39macro_rules! string_validator {
40 (
41 $name:ident,
42 $target_type:ty,
43 $validation_expression:expr
44 ) => {
45 pub fn $name(
46 field_context: &FieldContext,
47 value: &str,
48 target: $target_type,
49 error_message: &'static str,
50 ) -> Result<(), Violation> {
51 let check = ($validation_expression)(value, target);
52
53 create_string_violation!(check, field_context, $name, error_message)
54 }
55 };
56}
57
58string_validator!(max_len, u64, |value: &str, max_len: u64| value
60 .chars()
61 .count()
62 <= max_len as usize);
63string_validator!(min_len, u64, |value: &str, min_len: u64| value
64 .chars()
65 .count()
66 >= min_len as usize);
67string_validator!(len, u64, |value: &str, max_len: u64| value.chars().count()
68 == max_len as usize);
69
70string_validator!(len_bytes, u64, |value: &str, len: u64| value.len()
72 == len as usize);
73string_validator!(max_bytes, u64, |value: &str, max_bytes: u64| value.len()
74 <= max_bytes as usize);
75string_validator!(min_bytes, u64, |value: &str, min_bytes: u64| value.len()
76 >= min_bytes as usize);
77
78#[cfg(feature = "regex")]
80string_validator!(
81 pattern,
82 ®ex::Regex,
83 |value: &str, regex: ®ex::Regex| regex.is_match(value)
84);
85string_validator!(contains, &str, |value: &str, substring: &str| value
86 .contains(substring));
87string_validator!(not_contains, &str, |value: &str, substring: &str| !value
88 .contains(substring));
89string_validator!(prefix, &str, |value: &str, prefix: &str| value
90 .starts_with(prefix));
91string_validator!(suffix, &str, |value: &str, suffix: &str| value
92 .ends_with(suffix));
93
94well_known_rule!(
95 host_and_port,
96 "pair of host (hostname or IP address) and port"
97);
98
99well_known_rule!(hostname, "hostname");
100
101#[cfg(feature = "uri")]
102well_known_rule!(uri, "uri");
103#[cfg(feature = "uri")]
104well_known_rule!(uri_ref, "URI reference");
105
106well_known_rule!(address, "hostname or ip address");
107well_known_rule!(ip, "ip address");
108well_known_rule!(ipv4, "ipv4 address");
109well_known_rule!(ipv6, "ipv6 address");
110#[cfg(feature = "ip")]
111well_known_rule!(ip_prefix, "ip prefix");
112#[cfg(feature = "ip")]
113well_known_rule!(ipv4_prefix, "ipv4 prefix");
114#[cfg(feature = "ip")]
115well_known_rule!(ipv6_prefix, "ipv6 prefix");
116#[cfg(feature = "ip")]
117well_known_rule!(ip_with_prefixlen, "ip address with prefix length");
118#[cfg(feature = "ip")]
119well_known_rule!(ipv4_with_prefixlen, "ipv4 address with prefix length");
120#[cfg(feature = "ip")]
121well_known_rule!(ipv6_with_prefixlen, "ipv6 address with prefix length");
122
123#[cfg(feature = "regex")]
124well_known_rule!(email, "email address");
125#[cfg(feature = "regex")]
126well_known_rule!(uuid, "uuid");
127#[cfg(feature = "regex")]
128well_known_rule!(tuuid, "trimmed uuid");
129
130#[cfg(feature = "regex")]
131pub fn header_name(
132 field_context: &FieldContext,
133 value: &str,
134 strict: bool,
135) -> Result<(), Violation> {
136 let check = is_valid_http_header_name(value, strict);
137
138 if check {
139 Ok(())
140 } else {
141 Err(create_violation(
142 field_context,
143 &STRING_WELL_KNOWN_REGEX_VIOLATION,
144 "string.well_known_regex.header_name",
145 "must be a valid HTTP header name",
146 ))
147 }
148}
149
150#[cfg(feature = "regex")]
151pub fn header_value(
152 field_context: &FieldContext,
153 value: &str,
154 strict: bool,
155) -> Result<(), Violation> {
156 let check = is_valid_http_header_value(value, strict);
157
158 if check {
159 Ok(())
160 } else {
161 Err(create_violation(
162 field_context,
163 &STRING_WELL_KNOWN_REGEX_VIOLATION,
164 "string.well_known_regex.header_value",
165 "must be a valid HTTP header value",
166 ))
167 }
168}