icydb_model/base/validator/
len.rs1use crate::{prelude::*, visitor::Validator};
8use std::{
9 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
10 hash::BuildHasher,
11};
12
13#[expect(clippy::len_without_is_empty)]
18pub trait HasLen {
19 fn len(&self) -> usize;
20}
21
22impl HasLen for Blob {
23 fn len(&self) -> usize {
24 Self::len(self)
25 }
26}
27
28impl HasLen for str {
29 fn len(&self) -> usize {
30 self.chars().count()
31 }
32}
33
34impl HasLen for String {
35 fn len(&self) -> usize {
36 self.chars().count()
37 }
38}
39
40impl<T> HasLen for [T] {
41 fn len(&self) -> usize {
42 <[T]>::len(self)
43 }
44}
45
46impl<T> HasLen for Vec<T> {
47 fn len(&self) -> usize {
48 Self::len(self)
49 }
50}
51
52impl<T, S: BuildHasher> HasLen for HashSet<T, S> {
53 fn len(&self) -> usize {
54 Self::len(self)
55 }
56}
57
58impl<K, V, S: BuildHasher> HasLen for HashMap<K, V, S> {
59 fn len(&self) -> usize {
60 Self::len(self)
61 }
62}
63
64impl<T> HasLen for BTreeSet<T> {
65 fn len(&self) -> usize {
66 Self::len(self)
67 }
68}
69
70impl<K, V> HasLen for BTreeMap<K, V> {
71 fn len(&self) -> usize {
72 Self::len(self)
73 }
74}
75
76#[validator]
83pub struct Equal {
84 target: usize,
85}
86
87impl Equal {
88 pub fn new(target: impl TryInto<usize>) -> Self {
89 Self {
90 target: target.try_into().unwrap_or_default(),
91 }
92 }
93}
94
95impl<T: HasLen + ?Sized> Validator<T> for Equal {
96 fn validate(&self, t: &T, ctx: &mut dyn VisitorContext) {
97 let len = t.len();
98
99 if len != self.target {
100 ctx.issue(format!("length ({len}) is not equal to {}", self.target));
101 }
102 }
103}
104
105#[validator]
110pub struct Min {
111 target: usize,
112}
113
114impl Min {
115 pub fn new(target: impl TryInto<usize>) -> Self {
116 Self {
117 target: target.try_into().unwrap_or_default(),
118 }
119 }
120}
121
122impl<T: HasLen + ?Sized> Validator<T> for Min {
123 fn validate(&self, t: &T, ctx: &mut dyn VisitorContext) {
124 let len = t.len();
125
126 if len < self.target {
127 ctx.issue(format!(
128 "length ({len}) is lower than minimum of {}",
129 self.target
130 ));
131 }
132 }
133}
134
135#[validator]
140pub struct Max {
141 target: usize,
142}
143
144impl Max {
145 pub fn new(target: impl TryInto<usize>) -> Self {
146 Self {
147 target: target.try_into().unwrap_or_default(),
148 }
149 }
150}
151
152impl<T: HasLen + ?Sized> Validator<T> for Max {
153 fn validate(&self, t: &T, ctx: &mut dyn VisitorContext) {
154 let len = t.len();
155
156 if len > self.target {
157 ctx.issue(format!(
158 "length ({len}) is greater than maximum of {}",
159 self.target
160 ));
161 }
162 }
163}
164
165#[validator]
170pub struct Range {
171 min: usize,
172 max: usize,
173}
174
175impl Range {
176 pub fn new(min: impl TryInto<usize>, max: impl TryInto<usize>) -> Self {
177 Self {
178 min: min.try_into().unwrap_or_default(),
179 max: max.try_into().unwrap_or_default(),
180 }
181 }
182}
183
184impl<T: HasLen + ?Sized> Validator<T> for Range {
185 fn validate(&self, t: &T, ctx: &mut dyn VisitorContext) {
186 let len = t.len();
187
188 if len < self.min || len > self.max {
189 ctx.issue(format!(
190 "length ({len}) must be between {} and {} (inclusive)",
191 self.min, self.max
192 ));
193 }
194 }
195}
196
197#[cfg(test)]
202mod tests;