runmat_runtime/builtins/strings/
common.rs1use runmat_builtins::CharArray;
3
4const MISSING_SENTINEL: &str = "<missing>";
6
7#[inline]
9pub(crate) fn is_missing_string(text: &str) -> bool {
10 text.eq_ignore_ascii_case(MISSING_SENTINEL)
11}
12
13#[inline]
15pub(crate) fn lowercase_preserving_missing(text: String) -> String {
16 if is_missing_string(&text) {
17 MISSING_SENTINEL.to_string()
18 } else {
19 text.to_lowercase()
20 }
21}
22
23#[inline]
25pub(crate) fn uppercase_preserving_missing(text: String) -> String {
26 if is_missing_string(&text) {
27 MISSING_SENTINEL.to_string()
28 } else {
29 text.to_uppercase()
30 }
31}
32
33#[inline]
35pub(crate) fn char_row_to_string(array: &CharArray, row: usize) -> String {
36 char_row_to_string_slice(&array.data, array.cols, row)
37}
38
39#[inline]
41pub(crate) fn char_row_to_string_slice(data: &[char], cols: usize, row: usize) -> String {
42 let start = row * cols;
43 let end = start + cols;
44 data[start..end].iter().collect()
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn detects_missing_strings_case_insensitively() {
53 assert!(is_missing_string("<missing>"));
54 assert!(is_missing_string("<Missing>"));
55 assert!(!is_missing_string("<missing value>"));
56 }
57
58 #[test]
59 fn lowercase_preserves_missing() {
60 assert_eq!(
61 lowercase_preserving_missing("<missing>".to_string()),
62 "<missing>"
63 );
64 assert_eq!(lowercase_preserving_missing("RunMat".to_string()), "runmat");
65 }
66
67 #[test]
68 fn uppercase_preserves_missing() {
69 assert_eq!(
70 uppercase_preserving_missing("<missing>".to_string()),
71 "<missing>"
72 );
73 assert_eq!(uppercase_preserving_missing("RunMat".to_string()), "RUNMAT");
74 }
75
76 #[test]
77 fn char_row_collection_supports_row_major_storage() {
78 let chars: Vec<char> = vec!['A', 'B', 'C', 'D', 'E', 'F'];
79 assert_eq!(char_row_to_string_slice(&chars, 3, 0), "ABC");
80 assert_eq!(char_row_to_string_slice(&chars, 3, 1), "DEF");
81 }
82}