1fn make_pattern_for_digit_range(start: char, end: char, remaining: i32) -> String {
14 let mut out = String::new();
15 if start == end {
16 out.push(start);
17 } else {
18 out.push('[');
19 out.push(start);
20 out.push('-');
21 out.push(end);
22 out.push(']');
23 }
24 if remaining > 0 {
25 out.push_str(&format!("\\d{{{remaining}}}"));
26 }
27 out
28}
29
30fn generate_number_patterns(lower: i64, upper: i64) -> Vec<String> {
33 let mut patterns = Vec::new();
34 let lower_len = lower.to_string().len() as i32;
35 let upper_len = upper.to_string().len() as i32;
36
37 for len in lower_len..=upper_len {
38 let digit_min = 10i64.pow((len - 1) as u32);
39 let digit_max = 10i64.pow(len as u32) - 1;
40 let start = if len == lower_len { lower } else { digit_min };
41 let end = if len == upper_len { upper } else { digit_max };
42 let start_str = start.to_string();
43 let end_str = end.to_string();
44 let sb: Vec<char> = start_str.chars().collect();
45 let eb: Vec<char> = end_str.chars().collect();
46
47 if len == 1 {
48 patterns.push(make_pattern_for_digit_range(sb[0], eb[0], 0));
49 continue;
50 }
51
52 let mut prefix = 0usize;
53 while prefix < len as usize && sb[prefix] == eb[prefix] {
54 prefix += 1;
55 }
56 if prefix == len as usize {
57 patterns.push(start_str.clone());
58 continue;
59 }
60 if prefix > 0 && prefix >= (len as usize).saturating_sub(2) {
61 let common: String = sb[..prefix].iter().collect();
62 patterns.push(format!(
63 "{common}{}",
64 make_pattern_for_digit_range(sb[prefix], eb[prefix], len - prefix as i32 - 1)
65 ));
66 continue;
67 }
68
69 if len == lower_len && len == upper_len {
70 push_full_len_patterns(
71 &mut patterns,
72 start,
73 end,
74 digit_min,
75 digit_max,
76 &sb,
77 &eb,
78 len,
79 );
80 } else if len == lower_len && len != upper_len {
81 if start == digit_min {
82 patterns.push(format!("[1-9]\\d{{{}}}", len - 1));
83 } else {
84 push_start_open(&mut patterns, &sb, len);
85 patterns.push(start_str.clone());
86 }
87 } else if len != lower_len && len == upper_len {
88 if end == digit_max {
89 patterns.push(format!("[1-9]\\d{{{}}}", len - 1));
90 } else {
91 push_end_open(&mut patterns, &eb, len);
92 patterns.push(end_str.clone());
93 }
94 } else {
95 patterns.push(format!("[1-9]\\d{{{}}}", len - 1));
96 }
97 }
98 patterns
99}
100
101#[allow(clippy::too_many_arguments)]
104fn push_full_len_patterns(
105 patterns: &mut Vec<String>,
106 start: i64,
107 end: i64,
108 digit_min: i64,
109 digit_max: i64,
110 sb: &[char],
111 eb: &[char],
112 len: i32,
113) {
114 let start_str: String = sb.iter().collect();
115 let end_str: String = eb.iter().collect();
116 if start == digit_max {
117 patterns.push(start_str);
118 } else if start == digit_min {
119 if end == digit_max {
120 patterns.push(format!("[1-9]\\d{{{}}}", len - 1));
121 } else {
122 for (i, &ec) in eb.iter().enumerate() {
123 if i == 0 {
124 if ec > '1' {
125 patterns.push(make_pattern_for_digit_range('1', prev_char(ec), len - 1));
126 }
127 } else {
128 let pref: String = eb[..i].iter().collect();
129 if ec > '0' {
130 patterns.push(format!(
131 "{pref}{}",
132 make_pattern_for_digit_range('0', prev_char(ec), len - i as i32 - 1)
133 ));
134 }
135 }
136 }
137 patterns.push(end_str);
138 }
139 } else if end == digit_max {
140 for (i, &sc) in sb.iter().enumerate() {
141 if i == 0 {
142 if sc < '9' {
143 patterns.push(make_pattern_for_digit_range(next_char(sc), '9', len - 1));
144 }
145 } else {
146 let pref: String = sb[..i].iter().collect();
147 if sc < '9' {
148 patterns.push(format!(
149 "{pref}{}",
150 make_pattern_for_digit_range(next_char(sc), '9', len - i as i32 - 1)
151 ));
152 }
153 }
154 }
155 patterns.push(start_str);
156 } else {
157 push_full_distinct_first(patterns, sb, eb, len);
158 }
159}
160
161fn push_full_distinct_first(patterns: &mut Vec<String>, sb: &[char], eb: &[char], len: i32) {
164 let start_str: String = sb.iter().collect();
165 let end_str: String = eb.iter().collect();
166 let sf = sb[0];
167 let ef = eb[0];
168 if (ef as i32) - (sf as i32) > 1 {
169 patterns.push(make_pattern_for_digit_range(
170 next_char(sf),
171 prev_char(ef),
172 len - 1,
173 ));
174 }
175 for (i, &sc) in sb.iter().enumerate() {
176 if i == 0 {
177 let pref: String = sb[..1].iter().collect();
178 if sb[1] < '9' {
179 patterns.push(format!(
180 "{pref}{}",
181 make_pattern_for_digit_range(next_char(sb[1]), '9', len - 2)
182 ));
183 }
184 } else {
185 let pref: String = sb[..i].iter().collect();
186 if sc < '9' {
187 patterns.push(format!(
188 "{pref}{}",
189 make_pattern_for_digit_range(next_char(sc), '9', len - i as i32 - 1)
190 ));
191 }
192 }
193 }
194 patterns.push(start_str);
195 for (i, &ec) in eb.iter().enumerate() {
196 if i == 0 {
197 let pref: String = eb[..1].iter().collect();
198 if eb[1] > '0' {
199 patterns.push(format!(
200 "{pref}{}",
201 make_pattern_for_digit_range('0', prev_char(eb[1]), len - 2)
202 ));
203 }
204 } else {
205 let pref: String = eb[..i].iter().collect();
206 if ec > '0' {
207 patterns.push(format!(
208 "{pref}{}",
209 make_pattern_for_digit_range('0', prev_char(ec), len - i as i32 - 1)
210 ));
211 }
212 }
213 }
214 patterns.push(end_str);
215}
216
217fn push_start_open(patterns: &mut Vec<String>, sb: &[char], len: i32) {
219 for (i, &sc) in sb.iter().enumerate() {
220 if i == 0 {
221 if sc < '9' {
222 patterns.push(make_pattern_for_digit_range(next_char(sc), '9', len - 1));
223 }
224 } else {
225 let pref: String = sb[..i].iter().collect();
226 if sc < '9' {
227 patterns.push(format!(
228 "{pref}{}",
229 make_pattern_for_digit_range(next_char(sc), '9', len - i as i32 - 1)
230 ));
231 }
232 }
233 }
234}
235
236fn push_end_open(patterns: &mut Vec<String>, eb: &[char], len: i32) {
238 for (i, &ec) in eb.iter().enumerate() {
239 if i == 0 {
240 if ec > '1' {
241 patterns.push(make_pattern_for_digit_range('1', prev_char(ec), len - 1));
242 }
243 } else {
244 let pref: String = eb[..i].iter().collect();
245 if ec > '0' {
246 patterns.push(format!(
247 "{pref}{}",
248 make_pattern_for_digit_range('0', prev_char(ec), len - i as i32 - 1)
249 ));
250 }
251 }
252 }
253}
254
255fn next_char(c: char) -> char {
256 char::from(c as u8 + 1)
257}
258fn prev_char(c: char) -> char {
259 char::from(c as u8 - 1)
260}
261
262fn generate_sub_range_regex(lower: i64, upper: i64) -> String {
265 let patterns = generate_number_patterns(lower, upper);
266 format!("({})", patterns.join("|"))
267}
268
269pub fn generate_range_regex(start: Option<i64>, end: Option<i64>) -> String {
272 let mut parts: Vec<String> = Vec::new();
273
274 match (start, end) {
275 (None, None) => return "^-?\\d+$".to_string(),
276 (Some(s), None) => {
277 if s <= 0 {
278 if s < 0 {
279 parts.push(format!("-{}", generate_sub_range_regex(s.abs(), 1)));
280 }
281 parts.push("0".to_string());
282 parts.push("[1-9]\\d*".to_string());
283 } else {
284 let start_str = s.to_string();
285 let sb: Vec<char> = start_str.chars().collect();
286 let len = sb.len() as i32;
287 if len == 1 {
288 parts.push(make_pattern_for_digit_range(sb[0], '9', 0));
289 parts.push("[1-9]\\d*".to_string());
290 } else {
291 parts.push(start_str.clone());
292 push_start_open(&mut parts, &sb, len);
293 parts.push(format!("[1-9]\\d{{{len},}}"));
294 }
295 }
296 }
297 (None, Some(e)) => {
298 if e >= 0 {
299 parts.push("-[1-9]\\d*".to_string());
300 parts.push("0".to_string());
301 if e > 0 {
302 parts.push(generate_sub_range_regex(1, e));
303 }
304 } else {
305 let end_str = (-e).to_string();
306 let eb: Vec<char> = end_str.chars().collect();
307 let len = eb.len() as i32;
308 if len == 1 {
309 parts.push(format!("-{}", make_pattern_for_digit_range(eb[0], '9', 0)));
310 parts.push("-[1-9]\\d*".to_string());
311 } else {
312 parts.push(e.to_string());
313 for (i, &ec) in eb.iter().enumerate() {
314 if i == 0 {
315 if ec > '1' {
316 parts.push(format!(
317 "-{}",
318 make_pattern_for_digit_range('1', prev_char(ec), len - 1)
319 ));
320 }
321 } else {
322 let pref: String = eb[..i].iter().collect();
323 if ec > '0' {
324 parts.push(format!(
325 "-{pref}{}",
326 make_pattern_for_digit_range(
327 '0',
328 prev_char(ec),
329 len - i as i32 - 1
330 )
331 ));
332 }
333 }
334 }
335 parts.push(format!("-[1-9]\\d{{{len},}}"));
336 }
337 }
338 }
339 (Some(s), Some(e)) => {
340 if s > e {
341 return "^()$".to_string();
342 }
343 if s < 0 {
344 let neg_end = (-1i64).min(e);
345 parts.push(format!("-{}", generate_sub_range_regex(-neg_end, -s)));
346 }
347 if s <= 0 && e >= 0 {
348 parts.push("0".to_string());
349 }
350 if e > 0 {
351 let pos_start = 1i64.max(s);
352 parts.push(generate_sub_range_regex(pos_start, e));
353 }
354 }
355 }
356
357 format!("^({})$", parts.join("|"))
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn matches_cpp_examples() {
366 assert_eq!(generate_range_regex(Some(12), Some(16)), r"^((1[2-6]))$");
367 assert_eq!(generate_range_regex(Some(1), Some(10)), r"^(([1-9]|10))$");
368 assert_eq!(
369 generate_range_regex(Some(-5), Some(10)),
370 r"^(-([1-5])|0|([1-9]|10))$"
371 );
372 assert_eq!(generate_range_regex(Some(-15), Some(-10)), r"^(-(1[0-5]))$");
373 assert_eq!(generate_range_regex(None, None), r"^-?\d+$");
374 assert_eq!(generate_range_regex(Some(5), None), r"^([5-9]|[1-9]\d*)$");
375 assert_eq!(generate_range_regex(None, Some(0)), r"^(-[1-9]\d*|0)$");
376 assert_eq!(generate_range_regex(Some(5), Some(5)), r"^((5))$");
377 assert_eq!(
378 generate_range_regex(Some(-10), Some(0)),
379 r"^(-([1-9]|10)|0)$"
380 );
381 assert_eq!(generate_range_regex(Some(0), Some(10)), r"^(0|([1-9]|10))$");
382 }
383
384 #[test]
385 fn matches_multi_length_examples() {
386 assert_eq!(
387 generate_range_regex(Some(1), Some(9999)),
388 r"^(([1-9]|[1-9]\d{1}|[1-9]\d{2}|[1-9]\d{3}))$"
389 );
390 assert_eq!(
391 generate_range_regex(Some(s_int()), None),
392 r"^([5-9]|[1-9]\d*)$"
393 );
394 }
395
396 fn s_int() -> i64 {
397 5
398 }
399}