Skip to main content

runmat_runtime/builtins/strings/
common.rs

1//! Shared helpers for string builtins.
2use runmat_builtins::CharArray;
3
4/// Canonical display for missing string scalars in MATLAB-compatible output.
5const MISSING_SENTINEL: &str = "<missing>";
6
7/// Return `true` when the provided text represents a missing string scalar.
8#[inline]
9pub(crate) fn is_missing_string(text: &str) -> bool {
10    text.eq_ignore_ascii_case(MISSING_SENTINEL)
11}
12
13/// Convert text to lowercase while preserving MATLAB's `<missing>` sentinel.
14#[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/// Convert text to uppercase while preserving MATLAB's `<missing>` sentinel.
24#[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/// Collect a row from a [`CharArray`] into a `String`.
34#[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/// Collect a row from a character slice laid out in row-major order.
40#[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)]
48pub(crate) mod tests {
49    use super::*;
50
51    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
52    #[test]
53    fn detects_missing_strings_case_insensitively() {
54        assert!(is_missing_string("<missing>"));
55        assert!(is_missing_string("<Missing>"));
56        assert!(!is_missing_string("<missing value>"));
57    }
58
59    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
60    #[test]
61    fn lowercase_preserves_missing() {
62        assert_eq!(
63            lowercase_preserving_missing("<missing>".to_string()),
64            "<missing>"
65        );
66        assert_eq!(lowercase_preserving_missing("RunMat".to_string()), "runmat");
67    }
68
69    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
70    #[test]
71    fn uppercase_preserves_missing() {
72        assert_eq!(
73            uppercase_preserving_missing("<missing>".to_string()),
74            "<missing>"
75        );
76        assert_eq!(uppercase_preserving_missing("RunMat".to_string()), "RUNMAT");
77    }
78
79    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
80    #[test]
81    fn char_row_collection_supports_row_major_storage() {
82        let chars: Vec<char> = vec!['A', 'B', 'C', 'D', 'E', 'F'];
83        assert_eq!(char_row_to_string_slice(&chars, 3, 0), "ABC");
84        assert_eq!(char_row_to_string_slice(&chars, 3, 1), "DEF");
85    }
86}