gix_utils/
str.rs

1use std::{borrow::Cow, ffi::OsStr, path::Path};
2
3/// Assure that `s` is precomposed, i.e. `ä` is a single code-point, and not two i.e. `a` and `<umlaut>`.
4///
5/// At the expense of extra-compute, it does nothing if there is no work to be done, returning the original input without allocating.
6pub fn precompose(s: Cow<'_, str>) -> Cow<'_, str> {
7    use unicode_normalization::UnicodeNormalization;
8    if s.as_ref().nfc().cmp(s.as_ref().chars()).is_eq() {
9        s
10    } else {
11        Cow::Owned(s.as_ref().nfc().collect())
12    }
13}
14
15/// Assure that `s` is decomposed, i.e. `ä` turns into `a` and `<umlaut>`.
16///
17/// At the expense of extra-compute, it does nothing if there is no work to be done, returning the original input without allocating.
18pub fn decompose(s: Cow<'_, str>) -> Cow<'_, str> {
19    use unicode_normalization::UnicodeNormalization;
20    if s.as_ref().nfd().cmp(s.as_ref().chars()).is_eq() {
21        s
22    } else {
23        Cow::Owned(s.as_ref().nfd().collect())
24    }
25}
26
27/// Return the precomposed version of `path`, or `path` itself if it contained illformed unicode,
28/// or if the unicode version didn't contains decomposed unicode.
29/// Otherwise, similar to [`precompose()`]
30pub fn precompose_path(path: Cow<'_, Path>) -> Cow<'_, Path> {
31    match path.to_str() {
32        None => path,
33        Some(maybe_decomposed) => match precompose(maybe_decomposed.into()) {
34            Cow::Borrowed(_) => path,
35            Cow::Owned(precomposed) => Cow::Owned(precomposed.into()),
36        },
37    }
38}
39
40/// Return the precomposed version of `name`, or `name` itself if it contained illformed unicode,
41/// or if the unicode version didn't contains decomposed unicode.
42/// Otherwise, similar to [`precompose()`]
43pub fn precompose_os_string(name: Cow<'_, OsStr>) -> Cow<'_, OsStr> {
44    match name.to_str() {
45        None => name,
46        Some(maybe_decomposed) => match precompose(maybe_decomposed.into()) {
47            Cow::Borrowed(_) => name,
48            Cow::Owned(precomposed) => Cow::Owned(precomposed.into()),
49        },
50    }
51}
52
53/// Return the precomposed version of `s`, or `s` itself if it contained illformed unicode,
54/// or if the unicode version didn't contains decomposed unicode.
55/// Otherwise, similar to [`precompose()`]
56#[cfg(feature = "bstr")]
57pub fn precompose_bstr(s: Cow<'_, bstr::BStr>) -> Cow<'_, bstr::BStr> {
58    use bstr::ByteSlice;
59    match s.to_str().ok() {
60        None => s,
61        Some(maybe_decomposed) => match precompose(maybe_decomposed.into()) {
62            Cow::Borrowed(_) => s,
63            Cow::Owned(precomposed) => Cow::Owned(precomposed.into()),
64        },
65    }
66}