1use std::cmp::Ordering;
2use std::collections::{HashMap, VecDeque};
3use std::error::Error;
4use std::fmt::{Display, Formatter};
5use std::mem;
6use std::str::FromStr;
7use crate::interpreter::data::{DataObject, DataObjectRef, DataType, OptionDataObjectRef};
8use crate::interpreter::data::function::{Function, FunctionPointerObject, InternalFunction, Parameter};
9use crate::interpreter::{conversions, Interpreter};
10use crate::lexer::{CodePosition, Token, TokenType};
11use crate::regex_patterns;
12
13#[cfg(windows)]
14pub(crate) const LINE_SEPARATOR: &str = "\r\n";
15#[cfg(not(windows))]
16pub(crate) const LINE_SEPARATOR: &str = "\n";
17
18pub(crate) fn get_os_name() -> &'static str {
19 std::env::consts::OS
20}
21
22pub(crate) fn get_os_version() -> String {
23 "TODO: os.version".to_string()
26}
27
28pub(crate) fn get_os_arch() -> &'static str {
29 if cfg!(any(
30 target_arch = "aarch64",
31 target_arch = "arm64ec",
32 target_arch = "riscv64",
33 target_arch = "powerpc64",
34 target_arch = "loongarch64",
35 target_arch = "mips64",
36 target_arch = "mips64r6",
37 target_arch = "sparc64",
38 )) {
39 "ia64"
40 }else if cfg!(any(target_arch = "x86_64")) {
41 "amd64"
42 }else if cfg!(any(target_arch = "x86")) {
43 "x86"
44 }else if cfg!(any(target_arch = "wasm64")) {
45 "wasm64"
46 }else if cfg!(any(target_arch = "wasm32")) {
47 "wasm32"
48 }else {
49 "unknown"
50 }
51}
52
53pub(crate) mod math {
54 #[cfg(test)]
55 mod tests;
56
57 pub trait SpecialDiv {
58 fn wrapping_floor_div(self, rhs: Self) -> Self;
59 fn wrapping_ceil_div(self, rhs: Self) -> Self;
60 }
61
62 macro_rules! impl_special_div {
63 ( $selfT:ty ) => {
64 impl SpecialDiv for $selfT {
65 fn wrapping_floor_div(self, rhs: Self) -> Self {
66 let mut ret = self.wrapping_div(rhs);
67
68 if (self ^ rhs) < 0 && ret.wrapping_mul(rhs) != self {
70 ret = ret.wrapping_sub(1);
71 }
72
73 ret
74 }
75
76 fn wrapping_ceil_div(self, rhs: Self) -> Self {
77 let mut ret = self.wrapping_div(rhs);
78
79 if (self ^ rhs) >= 0 && ret.wrapping_mul(rhs) != self {
81 ret = ret.wrapping_add(1);
82 }
83
84 ret
85 }
86 }
87 };
88 }
89
90 impl_special_div! { i32 }
91 impl_special_div! { i64 }
92
93 pub trait ToNumberBase {
94 fn to_number_base(self, radix: u32) -> String;
95 }
96
97 macro_rules! impl_to_number_base {
98 ( $selfT:ty, $unsignedT:ty ) => {
99 impl ToNumberBase for $selfT {
100 fn to_number_base(self, radix: u32) -> String {
101 const DIGITS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
102
103 if !(2..=36).contains(&radix) {
104 panic!("Radix must be between 2 and 36");
105 }
106
107 let is_negative = self < 0;
108 let mut i = self.unsigned_abs();
109
110 let mut string = String::new();
111
112 while i >= radix as $unsignedT {
113 string.push(DIGITS[(i % radix as $unsignedT) as usize] as char);
114 i /= radix as $unsignedT;
115 }
116 string.push(DIGITS[i as usize] as char);
117
118 if is_negative {
119 string.push('-');
120 }
121
122 string.chars().rev().collect::<String>()
123 }
124 }
125 };
126 }
127
128 impl_to_number_base! { i32, u32 }
129 impl_to_number_base! { i64, u64 }
130}
131
132pub fn wrap_index(index: i32, len: usize) -> Option<usize> {
137 let index = index as isize;
138
139 let index = if index < 0 {
140 index.wrapping_add(len as isize)
141 }else {
142 index
143 };
144
145 if index < 0 || index as usize >= len {
146 None
147 }else {
148 Some(index as usize)
149 }
150}
151
152pub fn wrap_index_allow_len(index: i32, len: usize) -> Option<usize> {
157 let index = index as isize;
158
159 let index = if index < 0 {
160 index.wrapping_add(len as isize)
161 }else {
162 index
163 };
164
165 if index < 0 || index as usize > len {
166 None
167 }else {
168 Some(index as usize)
169 }
170}
171
172pub(crate) fn remove_dots_from_file_path(mut file: String) -> String {
173 while file.contains("/./") {
175 file = file.replace("/./", "/").to_string();
176 }
177
178 while regex_patterns::UTILS_PARENT_FOLDER.is_match(&file) {
180 file = regex_patterns::UTILS_PARENT_FOLDER.replace_all(&file, "/").to_string();
181 }
182
183 file
184}
185
186pub fn none_to_lang_void(data_object: OptionDataObjectRef) -> DataObjectRef {
190 data_object.unwrap_or_else(|| {
191 DataObjectRef::new(DataObject::new_void())
192 })
193}
194
195pub fn combine_data_objects(
207 data_objects: &[DataObjectRef],
208 interpreter: &mut Interpreter,
209 pos: CodePosition,
210) -> OptionDataObjectRef {
211 let mut data_objects = Vec::from(data_objects);
212
213 if data_objects.is_empty() {
214 return None;
215 }
216
217 if data_objects.len() == 1 {
218 return Some(data_objects.into_iter().next().unwrap());
219 }
220
221 data_objects.retain(|data_object| data_object.data_type() != DataType::VOID);
223
224 if data_objects.is_empty() {
226 return Some(DataObjectRef::new(DataObject::new_void()));
227 }
228
229 if data_objects.len() == 1 {
230 return Some(data_objects.into_iter().next().unwrap());
231 }
232
233 let mut builder = String::new();
235 for ele in data_objects {
236 builder += &conversions::to_text(interpreter, &ele, pos);
237 }
238
239 Some(DataObjectRef::new(DataObject::new_text(builder)))
240}
241
242pub fn combine_arguments_without_argument_separators(
253 argument_list: &[DataObjectRef],
254 interpreter: &mut Interpreter,
255 pos: CodePosition,
256) -> Vec<DataObjectRef> {
257 if argument_list.is_empty() {
258 return Vec::new();
259 }
260
261 let mut combined_argument_list = Vec::new();
262 let mut argument_tmp_list = Vec::new();
263 for current_data_object in argument_list {
264 if current_data_object.data_type() == DataType::ARGUMENT_SEPARATOR {
265 if argument_tmp_list.is_empty() {
266 argument_tmp_list.push(DataObjectRef::new(DataObject::new_void()));
267 }
268
269 combined_argument_list.push(combine_data_objects(&argument_tmp_list, interpreter, pos).unwrap());
270 argument_tmp_list.clear();
271
272 continue;
273 }
274
275 argument_tmp_list.push(current_data_object.clone());
276 }
277
278 if argument_tmp_list.is_empty() {
279 argument_tmp_list.push(DataObjectRef::new(DataObject::new_void()));
280 }
281
282 combined_argument_list.push(combine_data_objects(&argument_tmp_list, interpreter, pos).unwrap());
283
284 combined_argument_list
285}
286
287#[expect(clippy::let_and_return)]
291pub fn separate_arguments_with_argument_separators(
292 argument_list: &[DataObjectRef],
293) -> Vec<DataObjectRef> {
294 if argument_list.is_empty() {
295 return Vec::new();
296 }
297
298 let argument_separator = DataObjectRef::new(DataObject::with_update(|data_object| {
299 data_object.set_argument_separator(", ")
300 }).unwrap());
301
302 let new_len = 2 * argument_list.len() - 1;
304
305 let argument_list = argument_list.iter().
306 flat_map(|ele| [
307 ele.clone(),
308 argument_separator.clone(),
309 ]).
310 take(new_len).
311 collect::<Vec<_>>();
312
313 argument_list
314}
315
316pub fn are_function_signatures_equals(func_a: &Function, func_b: &Function) -> bool {
318 if func_a.var_args_parameter().map(|var_args| var_args.0) != func_b.var_args_parameter().
319 map(|var_args| var_args.0) || func_a.parameter_list().len() != func_b.parameter_list().len() {
320 return false;
321 }
322
323 for (parameter_a, parameter_b) in func_a.parameter_list().iter().zip(func_b.parameter_list().iter()) {
324 if parameter_a.type_constraint() != parameter_b.type_constraint() {
325 return false;
326 }
327 }
328
329 true
330}
331
332pub fn get_most_restrictive_function<'a>(
339 functions: &'a FunctionPointerObject,
340 argument_list: &[DataObjectRef],
341) -> Option<&'a InternalFunction> {
342 let function_index = get_most_restrictive_function_index(functions, argument_list);
343
344 function_index.and_then(|function_index| functions.get_function(function_index))
345}
346
347pub fn get_most_restrictive_function_index(
354 functions: &FunctionPointerObject,
355 argument_list: &[DataObjectRef],
356) -> Option<usize> {
357 let function_signatures = functions.functions().iter().
358 map(|function| function.function()).
359 map(|function| (function.parameter_list(), function.var_args_parameter().
360 map(|var_args_parameter| var_args_parameter.0))).
361 collect::<Vec<_>>();
362
363 get_most_restrictive_function_signature_index_internal(&function_signatures, argument_list)
364}
365
366pub fn get_most_restrictive_function_signature_index(
373 function_signatures: &[Function],
374 argument_list: &[DataObjectRef],
375) -> Option<usize> {
376 let function_signatures = function_signatures.iter().
377 map(|function| (function.parameter_list(), function.var_args_parameter().
378 map(|var_args_parameter| var_args_parameter.0))).
379 collect::<Vec<_>>();
380
381 get_most_restrictive_function_signature_index_internal(&function_signatures, argument_list)
382}
383
384fn get_most_restrictive_function_signature_index_internal(
385 function_signatures: &[(&[Parameter], Option<usize>)],
386 argument_list: &[DataObjectRef],
387) -> Option<usize> {
388 let mut best_function_signature: Option<&[Parameter]> = None;
389 let mut best_function_index = None;
390 let mut best_allowed_types_count = None;
391 let mut best_var_args_parameter_index = None;
392 let mut best_var_args_penalty = None;
393
394 'outer:
395 for (i, function_signature) in function_signatures.iter().
396 enumerate() {
397 if function_signature.1.is_some() {
398 if function_signature.0.len() - 1 > argument_list.len() {
399 continue; }
401 }else if function_signature.0.len() != argument_list.len() {
402 continue; }
404
405 let var_args_penalty = function_signature.1.map(|var_args_parameter_index| {
406 function_signature.0[var_args_parameter_index].type_constraint().allowed_types().len()
407 }).unwrap_or_default();
408
409 let mut argument_index = 0;
410 for (j, parameter) in function_signature.0.iter().
411 enumerate() {
412 if function_signature.1.is_some_and(|var_args_parameter_index| var_args_parameter_index == j) {
413 let old_argument_index = argument_index;
414
415 argument_index = argument_list.len() + j + 1 - function_signature.0.len();
416
417 for argument in argument_list[old_argument_index..argument_index].iter() {
419 if !parameter.type_constraint().is_type_allowed(argument.data_type()) {
420 continue 'outer;
421 }
422
423 }
424
425 continue;
426 }
427
428 if !parameter.type_constraint().is_type_allowed(argument_list[argument_index].data_type()) {
429 continue 'outer;
430 }
431
432 argument_index += 1;
433 }
434
435 let allowed_types_count: usize = function_signature.0.iter().
436 map(|parameter| parameter.type_constraint().allowed_types().len()).
437 sum();
438 let size_diff = best_function_signature.map(|best_function_signature|
439 best_function_signature.len() as isize - function_signature.0.len() as isize
440 ).unwrap_or_default();
441 if best_function_index.is_none() || (function_signature.1.is_none() && best_var_args_parameter_index.is_some()) ||
442 (function_signature.1.is_none() && best_var_args_parameter_index.is_none() && allowed_types_count < best_allowed_types_count.unwrap_or_default() ||
443 (function_signature.1.is_some() && best_var_args_parameter_index.is_some() &&
444 match size_diff {
445 0 => var_args_penalty < best_var_args_penalty.unwrap_or_default(),
446 ..0 => allowed_types_count < best_allowed_types_count.unwrap_or_default() + best_var_args_penalty.unwrap_or_default() * (-size_diff as usize),
447 _ => allowed_types_count + var_args_penalty * (size_diff as usize) < best_allowed_types_count.unwrap_or_default(),
448 })) {
449 best_function_signature = Some(function_signature.0);
450 best_function_index = Some(i);
451 best_allowed_types_count = Some(allowed_types_count);
452 best_var_args_parameter_index = function_signature.1;
453 best_var_args_penalty = Some(var_args_penalty);
454 }
455 }
456
457 best_function_index
458}
459
460pub fn get_version_components(version: &str) -> Option<(i32, i32, i32)> {
480 if version.is_empty() {
481 return None;
482 }
483
484 if version.as_bytes()[0] != b'v' {
485 return None;
486 }
487
488 if version.contains("-0") {
489 return None;
490 }
491
492 let major_minor_separator_index = version.find('.')?;
493
494 let minor_bugfix_separator_index = version[major_minor_separator_index + 1..].find('.')?;
495 let minor_bugfix_separator_index = minor_bugfix_separator_index + major_minor_separator_index + 1;
496
497 let major_str = &version[1..major_minor_separator_index];
498 let minor_str = &version[major_minor_separator_index + 1..minor_bugfix_separator_index];
499 let bugfix_str = &version[minor_bugfix_separator_index + 1..];
500
501 let major = i32::from_str(major_str).ok()?;
502 let minor = i32::from_str(minor_str).ok()?;
503 let bugfix = i32::from_str(bugfix_str).ok()?;
504
505 if major < 0 || minor < 0 || bugfix < 0 {
506 None
507 }else {
508 Some((major, minor, bugfix))
509 }
510}
511
512pub fn compare_versions_components(version_a: (i32, i32, i32), version_b: (i32, i32, i32)) -> Ordering {
519 if version_a.0 != version_b.0 {
520 return version_a.0.cmp(&version_b.0);
521 }
522
523 if version_a.1 != version_b.1 {
524 return version_a.1.cmp(&version_b.1);
525 }
526
527 version_a.2.cmp(&version_b.2)
528}
529
530pub fn compare_versions_str(version_a: &str, version_b: &str) -> Option<Ordering> {
537 let version_a = get_version_components(version_a)?;
538 let version_b = get_version_components(version_b)?;
539
540 Some(compare_versions_components(version_a, version_b))
541}
542
543pub(crate) fn get_index_of_matching_bracket_str(
544 string: &str,
545 start_byte_index: usize,
546 end_byte_index: usize,
547 opened_bracket: u8,
548 closed_bracket: u8,
549) -> Option<usize> {
550 let mut bracket_count = 0_usize;
551 let mut i = start_byte_index;
552 while i < end_byte_index && i < string.len() {
553 let c = string.as_bytes()[i];
554
555 if c == b'\\' {
557 i += 2;
558
559 continue;
560 }
561
562 if c == opened_bracket {
563 bracket_count += 1;
564 }else if c == closed_bracket {
565 bracket_count = bracket_count.saturating_sub(1);
566
567 if bracket_count == 0 {
568 return Some(i);
569 }
570 }
571
572 i += 1;
573 }
574
575 None
576}
577
578pub(crate) fn get_index_of_matching_bracket_tok(
579 tokens: &[Token],
580 start_index: usize,
581 end_index: usize,
582 opened_bracket: impl Into<String>,
583 closed_bracket: impl Into<String>,
584 abort_on_eol: bool,
585) -> Option<usize> {
586 let opened_bracket = opened_bracket.into();
587 let closed_bracket = closed_bracket.into();
588
589 if tokens.len() <= start_index || !matches!(tokens[start_index].token_type(), TokenType::OpeningBracket) ||
590 tokens[start_index].value() != opened_bracket {
591 return None;
592 }
593
594 let mut bracket_count = 0_usize;
595 let mut i = start_index;
596 while i < end_index && i < tokens.len() {
597 let token = &tokens[i];
598
599 if matches!(tokens[i].token_type(), TokenType::OpeningBracket) &&
600 tokens[i].value() == opened_bracket {
601 bracket_count += 1;
602 }else if matches!(tokens[i].token_type(), TokenType::ClosingBracket) &&
603 tokens[i].value() == closed_bracket {
604 bracket_count = bracket_count.saturating_sub(1);
605
606 if bracket_count == 0 {
607 return Some(i);
608 }
609 }
610
611 if matches!(tokens[i].token_type(), TokenType::StartMultilineText) {
613 while i < end_index && i < tokens.len() &&
614 !matches!(tokens[i].token_type(), TokenType::EndMultilineText) {
615 i += 1;
616 }
617 }else if matches!(tokens[i].token_type(), TokenType::StartComment | TokenType::StartDocComment) {
618 while i < end_index && i < tokens.len() &&
619 !matches!(tokens[i].token_type(), TokenType::EndComment) {
620 i += 1;
621 }
622 }
623
624 if abort_on_eol && token.token_type() == TokenType::Eol {
625 return None;
626 }
627
628 i += 1;
629 }
630
631 None
632}
633
634pub(crate) fn is_backslash_at_index_escaped(str: &str, byte_index: usize) -> bool {
635 if str.as_bytes().get(byte_index).is_none_or(|byte| *byte == b'\\') {
636 return false;
637 }
638
639 if byte_index == 0 {
640 return false;
641 }
642
643 let mut i = byte_index - 1;
644 loop {
645 if str.as_bytes()[i] != b'\\' {
646 return (byte_index - i) % 2 == 0;
647 }
648
649 if i == 0 {
650 break;
651 }
652
653 i -= 1;
654 }
655
656 byte_index % 2 == 1
657}
658
659pub(crate) fn split_off_arguments<T>(list: &mut VecDeque<T>, at: usize) -> VecDeque<T> {
664 let mut argument_list = list.split_off(at);
665 mem::swap(list, &mut argument_list);
666 argument_list.pop_front();
667 list.pop_front();
668
669 argument_list
670}
671
672pub fn is_callable(data_object: &DataObjectRef) -> bool {
674 let has_op_method = data_object.object_value().is_some_and(|object_value| {
675 !object_value.borrow().is_class() && object_value.borrow().methods().contains_key("op:call")
676 });
677
678 let is_struct_definition = data_object.struct_value().is_some_and(|struct_value| struct_value.is_definition());
679 let is_object_class = data_object.object_value().is_some_and(|object_value| object_value.borrow().is_class());
680
681 has_op_method || matches!(data_object.data_type(), DataType::FUNCTION_POINTER | DataType::TYPE) ||
682 is_struct_definition || is_object_class
683}
684
685pub fn is_member_access_allowed(value_object: &DataObjectRef) -> bool {
687 matches!(value_object.data_type(), DataType::ERROR | DataType::STRUCT | DataType::OBJECT)
688}
689
690#[derive(Debug)]
691pub struct InvalidTranslationTemplateSyntaxError {
692 message: String
693}
694
695impl InvalidTranslationTemplateSyntaxError {
696 pub fn new(message: impl Into<String>) -> Self {
697 Self { message: message.into() }
698 }
699
700 pub fn message(&self) -> &str {
701 &self.message
702 }
703}
704
705impl Display for InvalidTranslationTemplateSyntaxError {
706 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
707 f.write_str(&self.message)
708 }
709}
710
711impl Error for InvalidTranslationTemplateSyntaxError {}
712
713#[derive(Debug, Copy, Clone)]
714struct CountRange {
715 start_count: i32,
716 end_count: Option<i32>,
718}
719
720impl CountRange {
721 fn new(start_count: i32, end_count: Option<i32>) -> Self {
722 Self { start_count, end_count }
723 }
724
725 fn is_count_in_range(self, count: i32) -> bool {
726 count == self.start_count || (count > self.start_count && (self.end_count.is_none_or(|end_count| count <= end_count)))
727 }
728}
729
730#[derive(Debug)]
731struct TranslationPluralizationTemplate {
732 count_values: Box<[CountRange]>,
733 raw_translation_value: Box<str>,
734}
735
736impl TranslationPluralizationTemplate {
737 pub fn new(count_values: Box<[CountRange]>, raw_translation_value: Box<str>) -> Self {
738 Self { count_values, raw_translation_value }
739 }
740
741 pub fn count_values(&self) -> &[CountRange] {
742 &self.count_values
743 }
744
745 pub fn raw_translation_value(&self) -> &str {
746 &self.raw_translation_value
747 }
748}
749
750pub fn format_translation_template(
754 translation_value: &str,
755 template_map: HashMap<Box<str>, Box<str>>,
756) -> Result<String, InvalidTranslationTemplateSyntaxError> {
757 if translation_value.is_empty() {
758 return Ok(String::new());
759 }
760
761 let mut builder = String::new();
762
763 let mut i;
764 let mut start_index = 0;
765 loop {
766 let index = translation_value[start_index..].find('{');
767 if let Some(index) = index {
768 i = start_index + index;
769 }else {
770 builder += &translation_value[start_index..];
771
772 break;
773 }
774
775 builder += &translation_value[start_index..i];
776 start_index = i;
777
778 if i < translation_value.len() - 1 && translation_value.as_bytes()[i + 1] == b'{' {
779 builder += &translation_value[start_index..i + 1]; start_index = i + 2;
781
782 continue;
783 }
784
785 let matching_bracket_index = translation_value[i..].find('}');
786 let Some(matching_bracket_index) = matching_bracket_index else {
787 return Err(InvalidTranslationTemplateSyntaxError::new("Template closing bracket is missing"));
788 };
789 let matching_bracket_index = i + matching_bracket_index;
790
791 start_index = matching_bracket_index + 1;
792 let template_name = &translation_value[i + 1..matching_bracket_index];
793 let template_replacement = template_map.get(template_name);
794 if let Some(template_replacement) = template_replacement {
795 builder += template_replacement;
796 }else {
797 return Err(InvalidTranslationTemplateSyntaxError::new(format!(
798 "Template with the name \"{template_name}\" was not defined",
799 )));
800 }
801 }
802
803 Ok(builder)
804}
805
806pub fn format_translation_template_pluralization(
810 translation_value: &str,
811 count: i32,
812) -> Result<String, InvalidTranslationTemplateSyntaxError> {
813 format_translation_template_pluralization_with_template(translation_value, count, HashMap::new())
814}
815
816pub fn format_translation_template_pluralization_with_template(
820 translation_value: &str,
821 count: i32,
822 template_map: HashMap<Box<str>, Box<str>>,
823) -> Result<String, InvalidTranslationTemplateSyntaxError> {
824 let mut template_tokens = Vec::new();
825
826 let mut start_index = 0;
827 let mut i = 0;
828 while i < translation_value.len() {
829 if i == translation_value.len() - 1 {
830 template_tokens.push(translation_value[start_index..i + 1].replace(";;", ";")); break;
833 }
834
835 if translation_value.as_bytes()[i] == b';' {
836 if translation_value.as_bytes()[i + 1] == b';' {
837 i += 2; continue;
840 }
841
842 template_tokens.push(translation_value[start_index..i + 1].replace(";;", ";")); start_index = i + 1;
844 }
845
846 i += 1;
847 }
848
849 let mut templates = Vec::with_capacity(template_tokens.len());
850 for (i, mut template_token) in template_tokens.iter().
851 map(|str| &**str).
852 enumerate() {
853 if template_token.is_empty() || template_token.as_bytes()[0] != b'[' {
854 return Err(InvalidTranslationTemplateSyntaxError::new("Pluralization template token must start with \"[\""));
855 }
856
857 if template_token.as_bytes()[template_token.len() - 1] == b';' {
858 template_token = &template_token[0..template_token.len() - 1];
859 }else if i != template_tokens.len() - 1 {
860 return Err(InvalidTranslationTemplateSyntaxError::new("Pluralization template token must end with \";\""));
861 }
862
863 let matching_bracket_index = template_token.find(']');
864 let Some(matching_bracket_index) = matching_bracket_index else {
865 return Err(InvalidTranslationTemplateSyntaxError::new("Count range closing bracket is missing"));
866 };
867
868 let raw_count_values = &template_token[1..matching_bracket_index]; let raw_translation_value = &template_token[matching_bracket_index + 1..];
870
871 let mut count_values = Vec::new();
872 start_index = 0;
873 for (j, c) in raw_count_values.bytes().
874 enumerate() {
875 if c.is_ascii_digit() || c == b'-' || c == b'+' {
876 if j < raw_count_values.len() - 1 {
877 continue;
878 }
879 }else if c != b',' {
880 return Err(InvalidTranslationTemplateSyntaxError::new("Invalid token in count range"));
881 }
882
883 let end_index = if j < raw_count_values.len() - 1 { j } else { j + 1 };
884 let raw_count_value = &raw_count_values[start_index..end_index];
885 start_index = j + 1;
886
887 let mut start_count = -2;
888 let mut end_count = -2;
889 let mut number_start_index = 0;
890 for (k, c) in raw_count_value.bytes().
891 enumerate() {
892 if c.is_ascii_digit() {
893 if k == raw_count_value.len() - 1 {
894 let number_count = &raw_count_value[number_start_index..k + 1];
895
896 if start_count == -2 {
897 let ret = i32::from_str(number_count);
898 match ret {
899 Ok(ret) => start_count = ret,
900 Err(e) => {
901 return Err(InvalidTranslationTemplateSyntaxError::new(format!(
902 "Invalid count value: {e}",
903 )));
904 },
905 }
906
907 end_count = start_count;
908 }else if end_count == -2 {
909 let ret = i32::from_str(number_count);
910 match ret {
911 Ok(ret) => end_count = ret,
912 Err(e) => {
913 return Err(InvalidTranslationTemplateSyntaxError::new(format!(
914 "Invalid end count value: {e}",
915 )));
916 },
917 }
918 }else {
919 return Err(InvalidTranslationTemplateSyntaxError::new("Too many value in range inside a count range"));
920 }
921 }
922
923 continue;
924 }
925
926 if c == b'-' {
927 if number_start_index != 0 {
928 return Err(InvalidTranslationTemplateSyntaxError::new("Invalid character \"-\" can not be used twice in a range inside a count range"));
929 }
930
931 let number_start_count = &raw_count_value[number_start_index..k];
932 number_start_index = k + 1;
933
934 let ret = i32::from_str(number_start_count);
935 match ret {
936 Ok(ret) => start_count = ret,
937 Err(e) => {
938 return Err(InvalidTranslationTemplateSyntaxError::new(format!(
939 "Invalid start count value: {e}",
940 )));
941 },
942 }
943 }else if c == b'+' {
944 if start_count != -2 || end_count != -2 || k < raw_count_value.len() - 1 {
945 return Err(InvalidTranslationTemplateSyntaxError::new(
946 "Invalid character \"+\" can not be used twice or with multiple values in count range",
947 ));
948 }
949
950 let number_start_count = &raw_count_value[number_start_index..k];
951 let ret = i32::from_str(number_start_count);
952 match ret {
953 Ok(ret) => start_count = ret,
954 Err(e) => {
955 return Err(InvalidTranslationTemplateSyntaxError::new(format!(
956 "Invalid start count value: {e}",
957 )));
958 },
959 }
960
961 end_count = -1;
962 }
963 }
964
965 if start_count == -2 || end_count == -2 {
966 return Err(InvalidTranslationTemplateSyntaxError::new("Empty count range sequence"));
967 }
968
969 count_values.push(CountRange::new(start_count, if end_count == -1 {
970 None
971 }else {
972 Some(end_count)
973 }));
974 }
975
976 templates.push(TranslationPluralizationTemplate::new(Box::from(count_values), Box::from(raw_translation_value)));
977 }
978
979 for template in templates {
980 for count_range in template.count_values() {
981 if count_range.is_count_in_range(count) {
982 let mut template_map = template_map;
983 template_map.insert(Box::from("count"), Box::from(&*count.to_string()));
984
985 return format_translation_template(template.raw_translation_value(), template_map);
986 }
987 }
988 }
989
990 Err(InvalidTranslationTemplateSyntaxError::new(format!("No pluralization for count \"{count}\" was defined")))
991}