protify/validators/string/
builder.rs1#[doc(hidden)]
2pub mod state;
3use super::well_known_strings::WellKnownStrings;
4use crate::validators::*;
5pub(crate) use state::*;
6
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9pub struct StringValidatorBuilder<S: State = Empty> {
10 _state: PhantomData<S>,
11
12 data: StringValidator,
13}
14
15impl ProtoValidation for String {
16 #[doc(hidden)]
17 type Target = str;
18 #[doc(hidden)]
19 type Stored = Self;
20 type Validator = StringValidator;
21 type ValidatorBuilder = StringValidatorBuilder;
22
23 #[doc(hidden)]
24 type UniqueStore<'a>
25 = RefHybridStore<'a, str>
26 where
27 Self: 'a;
28
29 #[doc(hidden)]
30 const HAS_DEFAULT_VALIDATOR: bool = false;
31}
32impl<S: State> ValidatorBuilderFor<String> for StringValidatorBuilder<S> {
33 type Validator = StringValidator;
34 #[inline]
35 fn build_validator(self) -> StringValidator {
36 self.build()
37 }
38}
39
40impl<S: State> Default for StringValidatorBuilder<S> {
41 #[inline]
42 fn default() -> Self {
43 Self {
44 _state: PhantomData,
45 data: StringValidator::default(),
46 }
47 }
48}
49
50impl StringValidator {
51 #[must_use]
52 #[inline]
53 pub fn builder() -> StringValidatorBuilder {
54 StringValidatorBuilder::default()
55 }
56}
57
58impl<S: State> From<StringValidatorBuilder<S>> for ProtoOption {
59 #[inline(never)]
60 #[cold]
61 fn from(value: StringValidatorBuilder<S>) -> Self {
62 value.build().into()
63 }
64}
65
66#[allow(
67 clippy::must_use_candidate,
68 clippy::use_self,
69 clippy::return_self_not_must_use
70)]
71impl<S: State> StringValidatorBuilder<S> {
72 custom_error_messages_method!(String);
73
74 #[inline]
76 pub fn cel(mut self, program: CelProgram) -> StringValidatorBuilder<S> {
77 self.data.cel.push(program);
78
79 StringValidatorBuilder {
80 _state: PhantomData,
81 data: self.data,
82 }
83 }
84
85 #[inline]
87 pub fn ignore_always(mut self) -> StringValidatorBuilder<SetIgnore<S>>
88 where
89 S::Ignore: IsUnset,
90 {
91 self.data.ignore = Ignore::Always;
92
93 StringValidatorBuilder {
94 _state: PhantomData,
95 data: self.data,
96 }
97 }
98
99 #[inline]
101 pub fn ignore_if_zero_value(mut self) -> StringValidatorBuilder<SetIgnore<S>>
102 where
103 S::Ignore: IsUnset,
104 {
105 self.data.ignore = Ignore::IfZeroValue;
106
107 StringValidatorBuilder {
108 _state: PhantomData,
109 data: self.data,
110 }
111 }
112
113 #[inline]
115 pub fn required(mut self) -> StringValidatorBuilder<SetRequired<S>>
116 where
117 S::Required: IsUnset,
118 {
119 self.data.required = true;
120
121 StringValidatorBuilder {
122 _state: PhantomData,
123 data: self.data,
124 }
125 }
126
127 #[inline]
129 pub fn len(mut self, val: usize) -> StringValidatorBuilder<SetLen<S>>
130 where
131 S::Len: IsUnset,
132 {
133 self.data.len = Some(val);
134
135 StringValidatorBuilder {
136 _state: PhantomData,
137 data: self.data,
138 }
139 }
140
141 #[inline]
143 pub fn min_len(mut self, val: usize) -> StringValidatorBuilder<SetMinLen<S>>
144 where
145 S::MinLen: IsUnset,
146 {
147 self.data.min_len = Some(val);
148
149 StringValidatorBuilder {
150 _state: PhantomData,
151 data: self.data,
152 }
153 }
154
155 #[inline]
157 pub fn max_len(mut self, val: usize) -> StringValidatorBuilder<SetMaxLen<S>>
158 where
159 S::MaxLen: IsUnset,
160 {
161 self.data.max_len = Some(val);
162
163 StringValidatorBuilder {
164 _state: PhantomData,
165 data: self.data,
166 }
167 }
168
169 #[inline]
171 pub fn len_bytes(mut self, val: usize) -> StringValidatorBuilder<SetLenBytes<S>>
172 where
173 S::LenBytes: IsUnset,
174 {
175 self.data.len_bytes = Some(val);
176
177 StringValidatorBuilder {
178 _state: PhantomData,
179 data: self.data,
180 }
181 }
182
183 #[inline]
185 pub fn min_bytes(mut self, val: usize) -> StringValidatorBuilder<SetMinBytes<S>>
186 where
187 S::MinBytes: IsUnset,
188 {
189 self.data.min_bytes = Some(val);
190
191 StringValidatorBuilder {
192 _state: PhantomData,
193 data: self.data,
194 }
195 }
196
197 #[inline]
199 pub fn max_bytes(mut self, val: usize) -> StringValidatorBuilder<SetMaxBytes<S>>
200 where
201 S::MaxBytes: IsUnset,
202 {
203 self.data.max_bytes = Some(val);
204
205 StringValidatorBuilder {
206 _state: PhantomData,
207 data: self.data,
208 }
209 }
210
211 #[inline]
213 #[cfg(feature = "regex")]
214 #[track_caller]
215 pub fn pattern(mut self, val: impl IntoRegex) -> StringValidatorBuilder<SetPattern<S>>
216 where
217 S::Pattern: IsUnset,
218 {
219 self.data.pattern = Some(val.__into_regex());
220
221 StringValidatorBuilder {
222 _state: PhantomData,
223 data: self.data,
224 }
225 }
226
227 #[inline]
229 pub fn prefix<T: Into<FixedStr>>(mut self, val: T) -> StringValidatorBuilder<SetPrefix<S>>
230 where
231 S::Prefix: IsUnset,
232 {
233 self.data.prefix = Some(val.into());
234
235 StringValidatorBuilder {
236 _state: PhantomData,
237 data: self.data,
238 }
239 }
240
241 #[inline]
243 pub fn suffix<T: Into<FixedStr>>(mut self, val: T) -> StringValidatorBuilder<SetSuffix<S>>
244 where
245 S::Suffix: IsUnset,
246 {
247 self.data.suffix = Some(val.into());
248
249 StringValidatorBuilder {
250 _state: PhantomData,
251 data: self.data,
252 }
253 }
254
255 #[inline]
257 pub fn contains<T: Into<FixedStr>>(mut self, val: T) -> StringValidatorBuilder<SetContains<S>>
258 where
259 S::Contains: IsUnset,
260 {
261 self.data.contains = Some(val.into());
262
263 StringValidatorBuilder {
264 _state: PhantomData,
265 data: self.data,
266 }
267 }
268
269 #[inline]
271 pub fn not_contains<T: Into<FixedStr>>(
272 mut self,
273 val: T,
274 ) -> StringValidatorBuilder<SetNotContains<S>>
275 where
276 S::NotContains: IsUnset,
277 {
278 self.data.not_contains = Some(val.into());
279
280 StringValidatorBuilder {
281 _state: PhantomData,
282 data: self.data,
283 }
284 }
285
286 #[inline]
288 pub fn in_(mut self, val: impl IntoSortedList<FixedStr>) -> StringValidatorBuilder<SetIn<S>>
289 where
290 S::In: IsUnset,
291 {
292 self.data.in_ = Some(val.into_sorted_list());
293
294 StringValidatorBuilder {
295 _state: PhantomData,
296 data: self.data,
297 }
298 }
299
300 #[inline]
302 pub fn not_in(
303 mut self,
304 val: impl IntoSortedList<FixedStr>,
305 ) -> StringValidatorBuilder<SetNotIn<S>>
306 where
307 S::NotIn: IsUnset,
308 {
309 self.data.not_in = Some(val.into_sorted_list());
310
311 StringValidatorBuilder {
312 _state: PhantomData,
313 data: self.data,
314 }
315 }
316
317 #[inline]
319 pub fn const_<T: Into<FixedStr>>(mut self, val: T) -> StringValidatorBuilder<SetConst<S>>
320 where
321 S::Const: IsUnset,
322 {
323 self.data.const_ = Some(val.into());
324
325 StringValidatorBuilder {
326 _state: PhantomData,
327 data: self.data,
328 }
329 }
330
331 #[inline]
333 pub fn build(self) -> StringValidator {
334 self.data
335 }
336}
337
338macro_rules! well_known_impl {
339 ($name:ident, $doc:literal) => {
340 paste::paste! {
341 #[doc = $doc]
342 #[inline]
343 #[allow(rustdoc::bare_urls)]
344 pub fn [< $name:snake >](mut self) -> StringValidatorBuilder<SetWellKnown<S>>
345 where
346 S::WellKnown: IsUnset,
347 {
348 self.data.well_known = Some(WellKnownStrings::$name);
349
350 StringValidatorBuilder {
351 _state: PhantomData,
352 data: self.data,
353 }
354 }
355 }
356 };
357}
358
359impl<S: State> StringValidatorBuilder<S> {
360 #[cfg(feature = "regex")]
361 well_known_impl!(
362 Email,
363 r#"
364 `email` specifies that the field value must be a valid email address, for
365 example "foo@example.com".
366 Conforms to the definition for a valid email address from the [HTML standard](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address).
367 Note that this standard willfully deviates from [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322),
368 which allows many unexpected forms of email addresses and will easily match
369 a typographical error.
370 "#
371 );
372 well_known_impl!(
373 Hostname,
374 r#"
375 `hostname` specifies that the field value must be a valid hostname, for
376 example "foo.example.com".
377
378 A valid hostname follows the rules below:
379 - The name consists of one or more labels, separated by a dot (".").
380 - Each label can be 1 to 63 alphanumeric characters.
381 - A label can contain hyphens ("-"), but must not start or end with a hyphen.
382 - The right-most label must not be digits only.
383 - The name can have a trailing dot—for example, "foo.example.com.".
384 - The name can be 253 characters at most, excluding the optional trailing dot.
385 "#
386 );
387 well_known_impl!(
388 Ip,
389 r#"
390 `ip` specifies that the field value must be a valid IP (v4 or v6) address.
391
392 IPv4 addresses are expected in the dotted decimal format—for example, "192.168.5.21".
393 IPv6 addresses are expected in their text representation—for example, "::1",
394 or "2001:0DB8:ABCD:0012::0".
395
396 Both formats are well-defined in the internet standard [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
397 Zone identifiers for IPv6 addresses (for example, "fe80::a%en1") are supported.
398 "#
399 );
400 well_known_impl!(
401 Ipv4,
402 r#"
403 `ipv4` specifies that the field value must be a valid IPv4 address—for
404 example "192.168.5.21".
405 "#
406 );
407 well_known_impl!(
408 Ipv6,
409 r#"
410 `ipv6` specifies that the field value must be a valid IPv6 address—for
411 example "::1", or "d7a:115c:a1e0:ab12:4843:cd96:626b:430b".
412 "#
413 );
414 well_known_impl!(
415 Uri,
416 r#"
417 `uri` specifies that the field value must be a valid URI, for example
418 "https://example.com/foo/bar?baz=quux#frag".
419
420 URI is defined in the internet standard [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
421 Zone Identifiers in IPv6 address literals are supported ([RFC 6874](https://datatracker.ietf.org/doc/html/rfc6874)).
422 "#
423 );
424 well_known_impl!(
425 UriRef,
426 r#"
427 `uri_ref` specifies that the field value must be a valid URI Reference—either
428 a URI such as "https://example.com/foo/bar?baz=quux#frag", or a Relative
429 Reference such as "./foo/bar?query".
430
431 URI, URI Reference, and Relative Reference are defined in the internet
432 standard [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986). Zone
433 Identifiers in IPv6 address literals are supported ([RFC 6874](https://datatracker.ietf.org/doc/html/rfc6874)).
434 "#
435 );
436 well_known_impl!(
437 Address,
438 r#"
439 `address` specifies that the field value must be either a valid hostname
440 (for example, "example.com"), or a valid IP (v4 or v6) address (for example,
441 "192.168.0.1", or "::1").
442 "#
443 );
444 #[cfg(feature = "regex")]
445 well_known_impl!(
446 Uuid,
447 r"
448 `uuid` specifies that the field value must be a valid UUID as defined by
449 [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.2).
450 "
451 );
452 #[cfg(feature = "regex")]
453 well_known_impl!(
454 Ulid,
455 r"
456 `ulid` specifies that the field value must be a valid ULID as defined by the
457 [ULID specification](https://github.com/ulid/spec).
458 "
459 );
460 #[cfg(feature = "regex")]
461 well_known_impl!(
462 Tuuid,
463 r"
464 `tuuid` (trimmed UUID) specifies that the field value must be a valid UUID as
465 defined by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.2) with all dashes
466 omitted.
467 "
468 );
469 well_known_impl!(
470 IpWithPrefixlen,
471 r#"
472 `ip_with_prefixlen` specifies that the field value must be a valid IP
473 (v4 or v6) address with prefix length—for example, "192.168.5.21/16" or
474 "2001:0DB8:ABCD:0012::F1/64".
475 "#
476 );
477 well_known_impl!(
478 Ipv4WithPrefixlen,
479 r#"
480 `ipv4_with_prefixlen` specifies that the field value must be a valid
481 IPv4 address with prefix length—for example, "192.168.5.21/16".
482 "#
483 );
484 well_known_impl!(
485 Ipv6WithPrefixlen,
486 r#"
487 `ipv6_with_prefixlen` specifies that the field value must be a valid
488 IPv6 address with prefix length—for example, "2001:0DB8:ABCD:0012::F1/64".
489 "#
490 );
491 well_known_impl!(
492 IpPrefix,
493 r#"
494 `ip_prefix` specifies that the field value must be a valid IP (v4 or v6)
495 prefix—for example, "192.168.0.0/16" or "2001:0DB8:ABCD:0012::0/64".
496
497 The prefix must have all zeros for the unmasked bits. For example,
498 "2001:0DB8:ABCD:0012::0/64" designates the left-most 64 bits for the
499 prefix, and the remaining 64 bits must be zero.
500 "#
501 );
502 well_known_impl!(
503 Ipv4Prefix,
504 r#"
505 `ipv4_prefix` specifies that the field value must be a valid IPv4
506 prefix, for example "192.168.0.0/16".
507
508 The prefix must have all zeros for the unmasked bits. For example,
509 "192.168.0.0/16" designates the left-most 16 bits for the prefix,
510 and the remaining 16 bits must be zero.
511 "#
512 );
513 well_known_impl!(
514 Ipv6Prefix,
515 r#"
516 `ipv6_prefix` specifies that the field value must be a valid IPv6 prefix—for
517 example, "2001:0DB8:ABCD:0012::0/64".
518
519 The prefix must have all zeros for the unmasked bits. For example,
520 "2001:0DB8:ABCD:0012::0/64" designates the left-most 64 bits for the
521 prefix, and the remaining 64 bits must be zero.
522 "#
523 );
524 well_known_impl!(
525 HostAndPort,
526 r#"
527 `host_and_port` specifies that the field value must be valid host/port
528 pair—for example, "example.com:8080".
529
530 The host can be one of:
531 - An IPv4 address in dotted decimal format—for example, "192.168.5.21".
532 - An IPv6 address enclosed in square brackets—for example, "[2001:0DB8:ABCD:0012::F1]".
533 - A hostname—for example, "example.com".
534
535 The port is separated by a colon. It must be non-empty, with a decimal number
536 in the range of 0-65535, inclusive.
537 "#
538 );
539 #[cfg(feature = "regex")]
540 well_known_impl!(
541 HeaderNameLoose,
542 r"
543 Specifies that the value must be a valid HTTP header name.
544
545 All characters are considered valid except for `\r\n\0`.
546 Use `header_name_strict` for stricter enforcement."
547 );
548 #[cfg(feature = "regex")]
549 well_known_impl!(
550 HeaderNameStrict,
551 r"Specifies that the value must be a valid HTTP header name, according to the [RFC specification](https://datatracker.ietf.org/doc/html/rfc7230#section-3)"
552 );
553 #[cfg(feature = "regex")]
554 well_known_impl!(
555 HeaderValueLoose,
556 r"
557 Specifies that the value must be a valid HTTP header value.
558
559 All characters are considered valid except for `\r\n\0`.
560 Use `header_value_strict` for stricter enforcement."
561 );
562 #[cfg(feature = "regex")]
563 well_known_impl!(
564 HeaderValueStrict,
565 r"Specifies that the value must be a valid HTTP header value, according to the [RFC specification](https://datatracker.ietf.org/doc/html/rfc7230#section-3)"
566 );
567}