Skip to main content

protocheck_core/validators/
string.rs

1use 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
58// Char length
59string_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
70// Bytes length
71string_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// Patterns
79#[cfg(feature = "regex")]
80string_validator!(
81  pattern,
82  &regex::Regex,
83  |value: &str, regex: &regex::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}