Skip to main content

protify/validators/string/
builder.rs

1#[doc(hidden)]
2pub mod state;
3use super::well_known_strings::WellKnownStrings;
4use crate::validators::*;
5pub(crate) use state::*;
6
7/// Builder for [`StringValidator`].
8#[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	/// Adds a [`CelProgram`] to this validator.
75	#[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	/// Specifies that this validator should always be ignored.
86	#[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	/// Specifies that this validator should be ignored if the value is either unset or equal to its protobuf zero value.
100	#[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	/// Specifies that the field must be set (if optional) or not equal to its zero value (if not optional) in order to be valid.
114	#[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	/// Specifies that the given string field must be of this exact length.
128	#[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	/// Specifies that the given string field must have a length that is equal to or higher than the given value.
142	#[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	/// Specifies that the given string field must have a length that is equal to or lower than the given value.
156	#[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	/// Specifies the exact byte length that this field's value must have in order to be considered valid.
170	#[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	/// Specifies the minimum byte length for this field's value to be considered valid.
184	#[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	/// Specifies the minimum byte length for this field's value to be considered valid.
198	#[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	/// Specifies a regex pattern that this field's value should match in order to be considered valid.
212	#[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	/// Specifies the prefix that this field's value should contain in order to be considered valid.
228	#[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	/// Specifies the suffix that this field's value should contain in order to be considered valid.
242	#[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	/// Specifies a substring that this field's value should contain in order to be considered valid.
256	#[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	/// Specifies a substring that this field's value must not contain in order to be considered valid.
270	#[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	/// Specifies that only the values in this list will be considered valid for this field.
287	#[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	/// Specifies that the values in this list will be considered NOT valid for this field.
301	#[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	/// Specifies that only this specific value will be considered valid for this field.
318	#[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	/// Builds the validator.
332	#[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}