1use std::borrow::Cow;
5
6use crate::value::value_type::ValueType;
7
8pub fn value_max(value: ValueType) -> Cow<'static, str> {
9 match value {
10 ValueType::Decimal {
11 precision,
12 scale,
13 } => Cow::Owned(nines(precision.value(), scale.value())),
14 ValueType::Option(inner) => value_max(*inner),
15 other => Cow::Borrowed(fixed_max(other)),
16 }
17}
18
19pub fn value_range(value: ValueType) -> Cow<'static, str> {
20 match value {
21 ValueType::Decimal {
22 precision,
23 scale,
24 } => {
25 let max = nines(precision.value(), scale.value());
26 Cow::Owned(format!("-{max} to {max}"))
27 }
28 ValueType::Option(inner) => value_range(*inner),
29 other => Cow::Borrowed(fixed_range(other)),
30 }
31}
32
33fn nines(precision: u8, scale: u8) -> String {
34 let integer_digits = precision.saturating_sub(scale) as usize;
35 let integer = if integer_digits == 0 {
36 "0".to_string()
37 } else {
38 let digits = "9".repeat(integer_digits);
39 let mut grouped = String::with_capacity(digits.len() + digits.len() / 3);
40 for (index, digit) in digits.chars().enumerate() {
41 if index > 0 && (digits.len() - index).is_multiple_of(3) {
42 grouped.push('_');
43 }
44 grouped.push(digit);
45 }
46 grouped
47 };
48 if scale == 0 {
49 integer
50 } else {
51 format!("{integer}.{}", "9".repeat(scale as usize))
52 }
53}
54
55fn fixed_max(value: ValueType) -> &'static str {
56 match value {
57 ValueType::Boolean => unreachable!(),
58 ValueType::Float4 => "+3.4e38",
59 ValueType::Float8 => "+1.8e308",
60 ValueType::Int1 => "127",
61 ValueType::Int2 => "32_767",
62 ValueType::Int4 => "2_147_483_647",
63 ValueType::Int8 => "9_223_372_036_854_775_807",
64 ValueType::Int16 => "170_141_183_460_469_231_731_687_303_715_884_105_727",
65 ValueType::Utf8 => unreachable!(),
66 ValueType::Uint1 => "255",
67 ValueType::Uint2 => "65_535",
68 ValueType::Uint4 => "4_294_967_295",
69 ValueType::Uint8 => "18_446_744_073_709_551_615",
70 ValueType::Uint16 => "340_282_366_920_938_463_463_374_607_431_768_211_455",
71 ValueType::Date => unreachable!(),
72 ValueType::DateTime => unreachable!(),
73 ValueType::Time => unreachable!(),
74 ValueType::Duration => unreachable!(),
75 ValueType::IdentityId => unreachable!(),
76 ValueType::Uuid4 => unreachable!(),
77 ValueType::Uuid7 => unreachable!(),
78 ValueType::Blob => unreachable!(),
79 ValueType::Decimal {
80 ..
81 } => unreachable!(),
82 ValueType::Option(_) => unreachable!(),
83 ValueType::Any => unreachable!(),
84 ValueType::DictionaryId => unreachable!(),
85 ValueType::List(_) => unreachable!(),
86 ValueType::Record(_) => unreachable!(),
87 ValueType::Tuple(_) => unreachable!(),
88 ValueType::Digest {
89 ..
90 } => unreachable!(),
91 }
92}
93
94fn fixed_range(value: ValueType) -> &'static str {
95 match value {
96 ValueType::Boolean => unreachable!(),
97 ValueType::Float4 => "-3.4e38 to +3.4e38",
98 ValueType::Float8 => "-1.8e308 to +1.8e308",
99 ValueType::Int1 => "-128 to 127",
100 ValueType::Int2 => "-32_768 to 32_767",
101 ValueType::Int4 => "-2_147_483_648 to 2_147_483_647",
102 ValueType::Int8 => "-9_223_372_036_854_775_808 to 9_223_372_036_854_775_807",
103 ValueType::Int16 => {
104 "-170_141_183_460_469_231_731_687_303_715_884_105_728 to 170_141_183_460_469_231_731_687_303_715_884_105_727"
105 }
106 ValueType::Utf8 => unreachable!(),
107 ValueType::Uint1 => "0 to 255",
108 ValueType::Uint2 => "0 to 65_535",
109 ValueType::Uint4 => "0 to 4_294_967_295",
110 ValueType::Uint8 => "0 to 18_446_744_073_709_551_615",
111 ValueType::Uint16 => "0 to 340_282_366_920_938_463_463_374_607_431_768_211_455",
112 ValueType::Date => unreachable!(),
113 ValueType::DateTime => unreachable!(),
114 ValueType::Time => unreachable!(),
115 ValueType::Duration => unreachable!(),
116 ValueType::IdentityId => unreachable!(),
117 ValueType::Uuid4 => unreachable!(),
118 ValueType::Uuid7 => unreachable!(),
119 ValueType::Blob => unreachable!(),
120 ValueType::Decimal {
121 ..
122 } => unreachable!(),
123 ValueType::Option(_) => unreachable!(),
124 ValueType::Any => unreachable!(),
125 ValueType::DictionaryId => unreachable!(),
126 ValueType::List(_) => unreachable!(),
127 ValueType::Record(_) => unreachable!(),
128 ValueType::Tuple(_) => unreachable!(),
129 ValueType::Digest {
130 ..
131 } => unreachable!(),
132 }
133}
134
135#[cfg(test)]
136pub mod tests {
137
138 mod value_max {
139 use crate::{error::util::value_max, value::value_type::ValueType};
140
141 #[test]
142 fn test_signed_ints() {
143 assert_eq!(value_max(ValueType::Int1), "127");
144 assert_eq!(value_max(ValueType::Int2), "32_767");
145 assert_eq!(value_max(ValueType::Int4), "2_147_483_647");
146 assert_eq!(value_max(ValueType::Int8), "9_223_372_036_854_775_807");
147 assert_eq!(value_max(ValueType::Int16), "170_141_183_460_469_231_731_687_303_715_884_105_727");
148 }
149
150 #[test]
151 fn test_unsigned_ints() {
152 assert_eq!(value_max(ValueType::Uint1), "255");
153 assert_eq!(value_max(ValueType::Uint2), "65_535");
154 assert_eq!(value_max(ValueType::Uint4), "4_294_967_295");
155 assert_eq!(value_max(ValueType::Uint8), "18_446_744_073_709_551_615");
156 assert_eq!(value_max(ValueType::Uint16), "340_282_366_920_938_463_463_374_607_431_768_211_455");
157 }
158
159 #[test]
160 fn test_floats() {
161 assert_eq!(value_max(ValueType::Float4), "+3.4e38");
162 assert_eq!(value_max(ValueType::Float8), "+1.8e308");
163 }
164 }
165
166 mod value_range {
167 use crate::{error::util::value_range, value::value_type::ValueType};
168
169 #[test]
170 fn test_signed_ints() {
171 assert_eq!(value_range(ValueType::Int1), "-128 to 127");
172 assert_eq!(value_range(ValueType::Int2), "-32_768 to 32_767");
173 assert_eq!(value_range(ValueType::Int4), "-2_147_483_648 to 2_147_483_647");
174 assert_eq!(
175 value_range(ValueType::Int8),
176 "-9_223_372_036_854_775_808 to 9_223_372_036_854_775_807"
177 );
178 assert_eq!(
179 value_range(ValueType::Int16),
180 "-170_141_183_460_469_231_731_687_303_715_884_105_728 to 170_141_183_460_469_231_731_687_303_715_884_105_727"
181 );
182 }
183
184 #[test]
185 fn test_unsigned_ints() {
186 assert_eq!(value_range(ValueType::Uint1), "0 to 255");
187 assert_eq!(value_range(ValueType::Uint2), "0 to 65_535");
188 assert_eq!(value_range(ValueType::Uint4), "0 to 4_294_967_295");
189 assert_eq!(value_range(ValueType::Uint8), "0 to 18_446_744_073_709_551_615");
190 assert_eq!(
191 value_range(ValueType::Uint16),
192 "0 to 340_282_366_920_938_463_463_374_607_431_768_211_455"
193 );
194 }
195
196 #[test]
197 fn test_floats() {
198 assert_eq!(value_range(ValueType::Float4), "-3.4e38 to +3.4e38");
199 assert_eq!(value_range(ValueType::Float8), "-1.8e308 to +1.8e308");
200 }
201 }
202
203 mod parameterized {
204 use crate::{
205 error::util::{value_max, value_range},
206 value::{
207 constraint::{precision::Precision, scale::Scale},
208 value_type::ValueType,
209 },
210 };
211
212 #[test]
213 fn range_follows_the_declared_precision_and_scale() {
214 let decimal = ValueType::decimal(Precision::new(10), Scale::new(2));
216 assert_eq!(value_max(decimal.clone()), "99_999_999.99");
217 assert_eq!(value_range(decimal), "-99_999_999.99 to 99_999_999.99");
218 assert_eq!(value_max(ValueType::decimal(Precision::new(2), Scale::new(2))), "0.99");
219 }
220 }
221}