lano_stre/
lib.rs

1// pub fn add(left: u64, right: u64) -> u64 {
2//     left + right
3// }
4
5// #[cfg(test)]
6// mod tests {
7//     use super::*;
8
9//     #[test]
10//     fn it_works() {
11//         let result = add(2, 2);
12//         assert_eq!(result, 4);
13//     }
14// }
15
16use regex::Regex;
17// use fancy_regex::Regex;
18
19/// mock js String.prototype.replace
20///
21/// ```rs
22/// let result = str_pattern_replace(input_string, pattern, replacement_function);
23/// ```
24fn str_pattern_replace<F>(input: &str, pattern: &str, replacement: F) -> String
25where
26    F: Fn(usize, &str) -> String,
27{
28    // Compile the regex pattern
29    let regex = Regex::new(pattern).expect("Invalid regex pattern");
30
31    // Create a mutable String to hold the modified result
32    let mut result = String::new();
33    let mut last_end = 0;
34
35    // Iterate over all matches
36    for (index, caps) in regex.captures_iter(input).enumerate() {
37        // Get the matched value
38        let matched_value = &caps[0];
39
40        // Get the start index of the match
41        let start_index = caps.get(0).unwrap().start();
42
43        // Append the part of the string before the match
44        result.push_str(&input[last_end..start_index]);
45
46        // Call the replacement function with the index and matched value
47        result.push_str(&replacement(index, matched_value));
48
49        // Update the last end position
50        last_end = caps.get(0).unwrap().end();
51    }
52
53    // Append the remaining part of the input string after the last match
54    result.push_str(&input[last_end..]);
55
56    result
57}
58
59/// Handle the first character to be uppercase and others to be lowercase
60///
61/// If `otherslower` is `Some(true)`, the other characters will be converted to lowercase.
62/// If `otherslower` is `Some(false)`, the other characters will retain their original case.
63/// If `otherslower` is `None`, the other characters will also be converted to lowercase.
64///
65/// ```rs
66/// let res = str_first_upper("hello world", Some(true));
67/// assert_eq!(res, "Hello world");
68/// ```
69fn str_first_upper(s: &str, otherslower: Option<bool>) -> String {
70    let res: String = s
71        .chars()
72        .enumerate()
73        .map(|(i, c)| {
74            if i == 0 {
75                c.to_uppercase().to_string() // Capitalize the first character
76            } else {
77                match otherslower {
78                    Some(false) => c.to_string(),      // Retain original case if false
79                    _ => c.to_lowercase().to_string(), // Convert to lowercase if true or None
80                }
81            }
82        })
83        .collect();
84
85    res
86}
87
88fn str_first_lower(s: &str) -> String {
89    let res: String = s
90        .chars()
91        .enumerate()
92        .map(|(i, c)| {
93            if i == 0 {
94                c.to_lowercase().to_string() // Capitalize the first character
95            } else {
96                c.to_string()
97            }
98        })
99        .collect();
100    res
101}
102
103/// add filled letters as prefix when enable and  pattern match
104///
105/// ```rs
106/// res = str_pattern_prefix(&res, r"(?:[A-Z]+)", index != 0);
107/// ```
108fn str_pattern_prefix(s: &str, pattern: &str, filled: &str, enable: bool) -> String {
109    if enable {
110        let upper_case_pattern = Regex::new(pattern).unwrap();
111        upper_case_pattern.replace_all(s, format!("{}{}", filled, "$0")).to_string()
112    } else {
113        s.to_string()
114    }
115}
116
117/// padd letters when enable and  pattern match
118///
119/// ```rs
120/// res = str_case_switch_when_enable(&res, index == 0);
121/// ```
122// fn str_case_switch_when_enable(s: &str, enable: bool) -> String {
123//     if enable {
124//         s.to_uppercase()
125//     } else {
126//         s.to_lowercase()
127//     }
128// }
129
130fn baseify(input_string: &str) -> String {
131    // Define a regex pattern for camelCase words
132    // let pattern = r"(?:^\w|[A-Z_-]|\b\w)";
133    // let separator_pattern = Regex::new(r"[-_]+").unwrap();
134
135    // let debug = false;
136    // // Define a replacement function that uses the index
137    // let replacement_function = |index: usize, match_str: &str| {
138    //     // info match key and val
139    //     if debug {
140    //         println!("{},{}", index, match_str);
141    //     }
142
143    //     // Replace multi - or _ with a space
144    //     let res = separator_pattern.replace_all(match_str, " ").to_string();
145
146    //     // Add space before uppercase letters that are not the first index
147    //     // res = str_pattern_prefix(&res, r"(?:[A-Z]+)", " ", index != 0);
148
149    //     // Handle the first character to be uppercase and others to be lowercase
150    //     // res = str_first_upper(&match_str, None);
151
152    //     // Capitalize the first character and lowercase the rest
153    //     // res = str_case_switch_when_enable(&res, index == 0);
154    //     res
155    //     // format!("REPLACED_{}: {} ", index, match_str.to_lowercase())
156    // };
157    // let result = str_pattern_replace(input_string, pattern, replacement_function);
158
159    // step 1 - Replace multi - or _ with a space
160    let result = str_pattern_replace(input_string, r"[-_]+", |_index: usize, _match_str: &str| {
161        " ".to_string()
162    });
163    // println!("[replace multi - or _ with a space]: {}", result);
164
165    // optinal
166    // let filled as prefix to sequence of two or more uppercase letters
167    // let result = str_pattern_prefix(&result, r"(?:[A-Z]{2,})", " ", true);
168
169    // let frst char upper for sequence of two or more uppercase letters
170    let result =
171        str_pattern_replace(&result, r"(?:[A-Z]{2,})", |_index: usize, match_str: &str| {
172            str_first_upper(&match_str, None)
173        });
174    // println!("[NAME to Name]: {}", result);
175
176    // let filled as prefix to a uppercase letter
177    let result = str_pattern_prefix(&result, r"[A-Z]+", " ", true);
178    let result =
179        str_pattern_replace(&result, r" +", |_index: usize, _match_str: &str| " ".to_string());
180    // println!("[prefix space to uppercase letter]: {}", result);
181
182    // let word first char upper for each word
183    // step 2 - Capitalize the first character and lowercase the rest for each word
184    let words_pattern = r"(?:[\w]+)";
185    let words_first_upper = |_index: usize, match_str: &str| -> String {
186        // str_case_switch_when_enable(&match_str, index == 0)
187        str_first_upper(&match_str, None)
188    };
189    let result = str_pattern_replace(&result, words_pattern, words_first_upper);
190
191    // println!("[word first upper]: {}", result);
192
193    // step 3 - let str first char upper for sentence
194    // let result = str_first_upper(&result, None);
195    // println!("[sentence first upper]: {}", result);
196
197    result
198}
199
200///
201///
202/// ```rs
203/// assert_eq!(humanize("hello-world"), "Hello World");
204/// assert_eq!(humanize("hello_world"), "Hello World");
205/// assert_eq!(humanize("helloWorld"), "Hello World");
206/// assert_eq!(humanize("HelloWorld"), "Hello World");
207/// assert_eq!(humanize("helloWORLD"), "Hello World");
208/// assert_eq!(humanize(""), "");
209/// assert_eq!(humanize("--"), "");
210/// assert_eq!(humanize("__"), "");
211/// assert_eq!(humanize("hello--world"), "Hello World");
212/// assert_eq!(humanize("hello__world"), "Hello World");
213/// assert_eq!(humanize("helloWORLD Test"), "Hello World Test");
214/// ```
215pub fn humanize(s: &str) -> String {
216    // use regexp to get words in rust: "\w+"
217    // let words support containts - or _ "[\w-]+"
218    // (?:^\w|[A-Z_-]|\b\w)
219    // r"(?:^\w|[A-Z-a-z]|\b\w)"
220
221    // (?:^\w|[A-Z_-]|\b\w)
222    // (?:(?m)^\w|[A-Z_-]|\b\w)
223    // Regex to match the patterns similar to the TypeScript regex
224    let result = baseify(s);
225    // Trim any trailing whitespace and return
226    result.trim().to_string().replace("\n", " ") // Replace newlines with spaces
227}
228
229///
230///  ```rs
231///  assert_eq!(slugify("hello-world"), "hello-world");
232///  assert_eq!(slugify("hello_world"), "hello-world");
233///  assert_eq!(slugify("helloWorld"), "hello-world");
234///  assert_eq!(slugify("HelloWorld"), "hello-world");
235///  assert_eq!(slugify("helloWORLD"), "hello-world");
236///  assert_eq!(slugify(""), "");
237///  assert_eq!(slugify("--"), "");
238///  assert_eq!(slugify("__"), "");
239///  assert_eq!(slugify("hello--world"), "hello-world");
240///  assert_eq!(slugify("hello__world"), "hello-world");
241///  assert_eq!(slugify("helloWORLD Test"), "hello-world-test");
242/// ```
243pub fn slugify(s: &str) -> String {
244    let humanized = humanize(s);
245    humanized.to_lowercase().replace(" ", "-")
246}
247
248///
249///
250///  ```rs
251///  assert_eq!(camelize("hello-world"), "helloWorld");
252///  assert_eq!(camelize("hello_world"), "helloWorld");
253///  assert_eq!(camelize("helloWorld"), "helloWorld");
254///  assert_eq!(camelize("HelloWorld"), "helloWorld");
255///  assert_eq!(camelize("helloWORLD"), "helloWorld");
256///  assert_eq!(camelize(""), "");
257///  assert_eq!(camelize("--"), "");
258///  assert_eq!(camelize("__"), "");
259///  assert_eq!(camelize("hello--world"), "helloWorld");
260///  assert_eq!(camelize("hello__world"), "helloWorld");
261///  assert_eq!(camelize("helloWORLD Test"), "helloWorldTest");
262/// ```
263pub fn camelize(s: &str) -> String {
264    let humanized = humanize(s);
265
266    let result = str_pattern_replace(
267        &str_first_lower(&humanized),
268        r" +",
269        |_index: usize, _match_str: &str| "".to_string(),
270    );
271
272    // let mut result = String::new();
273    // let mut first_char = true;
274    // for c in humanized.chars() {
275    //     if c == ' ' {
276    //         continue;
277    //     }
278    //     if first_char {
279    //         result.push(c.to_ascii_lowercase());
280    //         first_char = false;
281    //     } else {
282    //         result.push(c.to_ascii_uppercase());
283    //     }
284    // }
285    result
286}
287
288///
289///
290///  ```rs
291///  assert_eq!(underscoped("hello-world"), "hello_world");
292///  assert_eq!(underscoped("hello_world"), "hello_world");
293///  assert_eq!(underscoped("helloWorld"), "hello_world");
294///  assert_eq!(underscoped("HelloWorld"), "hello_world");
295///  assert_eq!(underscoped("helloWORLD"), "hello_world");
296///  assert_eq!(underscoped(""), "");
297///  assert_eq!(underscoped("--"), "");
298///  assert_eq!(underscoped("__"), "");
299///  assert_eq!(underscoped("hello--world"), "hello_world");
300///  assert_eq!(underscoped("hello__world"), "hello_world");
301///  assert_eq!(underscoped("helloWORLD Test"), "hello_world_test");
302///  ``````
303pub fn underscoped(s: &str) -> String {
304    let humanized = humanize(s);
305    humanized.to_lowercase().replace(" ", "_")
306}
307
308/// transform string to class style
309///
310/// ```rs
311///  assert_eq!(classify("hello-world"), "HelloWorld");
312///  assert_eq!(classify("hello_world"), "HelloWorld");
313///  assert_eq!(classify("helloWorld"), "HelloWorld");
314///  assert_eq!(classify("HelloWorld"), "HelloWorld");
315///  assert_eq!(classify("helloWORLD"), "HelloWorld"); //different for zero's stre in ts
316///  assert_eq!(classify(""), "");
317///  assert_eq!(classify("--"), "");
318///  assert_eq!(classify("__"), "");
319///  assert_eq!(classify("hello--world"), "HelloWorld");
320///  assert_eq!(classify("hello__world"), "HelloWorld");
321///  assert_eq!(classify("helloWORLD Test"), "HelloWorldTest");
322/// ```
323pub fn classify(s: &str) -> String {
324    let humanized = humanize(s);
325    let result = str_pattern_replace(
326        &str_first_lower(&humanized),
327        r"\w+",
328        |_index: usize, _match_str: &str| str_first_upper(_match_str, Some(true)),
329    );
330    result.replace(" ", "")
331}
332
333///
334///
335/// ```rs
336///  assert_eq!(swap_case("hello"), "HELLO");
337///  assert_eq!(swap_case("HELLO"), "hello");
338///  assert_eq!(swap_case("Hello"), "hELLO");
339///  assert_eq!(swap_case(""), "");
340///  assert_eq!(swap_case("HeLlO"), "hElLo");
341/// ```
342pub fn swap_case(s: &str) -> String {
343    let mut result = String::new();
344    for c in s.chars() {
345        if c.is_uppercase() {
346            result.push(c.to_ascii_lowercase());
347        } else {
348            result.push(c.to_ascii_uppercase());
349        }
350    }
351    result
352}
353
354/// ttransform the first char to upper case (only the first word), other noop
355///
356/// ```rs
357///  assert_eq!(capitalize("hello"), "Hello");
358///  assert_eq!(capitalize("HELLO"), "HELLO");
359///  assert_eq!(capitalize("Hello"), "Hello");
360///  assert_eq!(capitalize(""), "");
361///  assert_eq!(capitalize("hello world"), "Hello world");
362/// ```
363pub fn capitalize(s: &str) -> String {
364    let mut result = String::new();
365    let mut first_char = true;
366    for c in s.chars() {
367        if first_char {
368            result.push(c.to_ascii_uppercase());
369            first_char = false;
370        } else {
371            result.push(c);
372        }
373    }
374    result
375}
376
377/// transform the firt char to upper (only the first word) , other lower
378///
379/// ```rs
380///  assert_eq!(sentence("hello"), "Hello");
381///  assert_eq!(sentence("HELLO"), "Hello");
382///  assert_eq!(sentence("Hello"), "Hello");
383///  assert_eq!(sentence(""), "");
384///  assert_eq!(sentence("hello world"), "Hello world");
385///  assert_eq!(sentence("HELLO WORLD"), "Hello world");
386/// ```
387pub fn sentence(s: &str) -> String {
388    let mut result = String::new();
389    let mut first_char = true;
390    for c in s.chars() {
391        if first_char {
392            result.push(c.to_ascii_uppercase());
393            first_char = false;
394        } else {
395            result.push(c.to_ascii_lowercase());
396        }
397    }
398    result
399}
400
401/// transform the firt char to upper(for each word), other lower
402///
403/// ```rs
404///  assert_eq!(titleize("hello"), "Hello");
405///  assert_eq!(titleize("HELLO"), "Hello");
406///  assert_eq!(titleize("Hello"), "Hello");
407///  assert_eq!(titleize("hello world"), "Hello World");
408///  assert_eq!(titleize("HELLO WORLD"), "Hello World");
409///  assert_eq!(titleize(""), "");
410///  assert_eq!(titleize("hello--world"), "Hello World");
411///  assert_eq!(titleize("hello__world"), "Hello World");
412/// ```
413pub fn titleize(s: &str) -> String {
414    let humanized = humanize(s);
415    let mut result = String::new();
416    let words = humanized.split_whitespace();
417    for word in words {
418        let capitalized_word = word.chars().next().unwrap().to_ascii_uppercase().to_string()
419            + &word[1..].to_ascii_lowercase();
420        result.push_str(&capitalized_word);
421        result.push(' ');
422    }
423    result.trim_end().to_string()
424}
425
426pub fn pad_start(s: &str, len: usize, filled: char) -> String {
427    let mut result = s.to_string();
428    while result.len() < len {
429        result.insert(0, filled);
430    }
431    result
432}
433
434pub fn pad_end(s: &str, len: usize, filled: char) -> String {
435    let mut result = s.to_string();
436    while result.len() < len {
437        result.push(filled);
438    }
439    result
440}
441
442#[cfg(test)]
443mod lib_test;