Skip to main content

str_utils/
lib.rs

1/*!
2# str Utils
3
4This crate provides some traits to extend `[u8]`, `str` and `Cow<str>`.
5
6## Examples
7
8```rust
9# #[cfg(feature = "alloc")]
10# extern crate alloc;
11
12# #[cfg(feature = "alloc")]
13# use alloc::borrow::Cow;
14
15use str_utils::*;
16
17assert_eq!(true, "foobar".starts_with_ignore_ascii_case("FoO"));
18assert_eq!(Some(1), "photo.jpg".ends_with_ignore_ascii_case_multiple(&[".png", ".jpg", ".gif"]));
19assert_eq!(true, "foobar".starts_with_ignore_ascii_case("FoO"));
20
21# #[cfg(feature = "alloc")]
22assert_eq!("here is a ZERO_WIDTH_SPACE -> ​".len() - 3, "here is a ZERO_WIDTH_SPACE -> ​".remove_all_invisible_characters().len());
23
24# #[cfg(feature = "alloc")]
25assert_eq!(r"foo\% b\_r", r"foo% b_r".escape_ascii_characters(b'\\', b"%_"));
26
27assert!(!"AaBb\u{0130}Zz".is_lowercased());
28assert!(!"AaBbZz".is_ascii_lowercased());
29assert!(!"aabbßzz".is_uppercased());
30assert!(!"aabbzz".is_ascii_uppercased());
31
32# #[cfg(feature = "alloc")]
33assert_eq!("aabbi\u{0307}zz", "AaBb\u{0130}Zz".to_lowercase_cow());
34# #[cfg(feature = "alloc")]
35assert_eq!("aabb\u{0130}zz", "AaBb\u{0130}Zz".to_ascii_lowercase_cow());
36# #[cfg(feature = "alloc")]
37assert_eq!("AABBSSZZ", "aabbßzz".to_uppercase_cow());
38# #[cfg(feature = "alloc")]
39assert_eq!("AABBßZZ", "aabbßzz".to_ascii_uppercase_cow());
40
41# #[cfg(feature = "alloc")]
42assert_eq!("Line 1 Line 2 Line 2 Line 3 Line 4 Line 5", "Line 1\r\nLine 2\r\nLine 2\rLine 3\nLine 4\nLine 5".replace_newlines_with_space());
43
44# #[cfg(feature = "alloc")]
45assert_eq!("Line 1\nLine 2\nLine 3", "Line 1\r\nLine 2\u{2028}Line 3".normalize_newlines());
46
47# #[cfg(feature = "alloc")]
48assert_eq!("foo    bar", "foo\tbar".expand_tabs(4));
49
50# #[cfg(feature = "alloc")]
51assert_eq!("foo bar", "foo_baz".replace_cow("_baz", " bar"));
52
53assert_eq!("  foo  ", "\n\n  foo  \n\n".trim_line());
54
55# #[cfg(feature = "alloc")]
56assert_eq!("abc", Cow::from(" abc ").trim_cow()); // the `trim_cow` family of methods can be used on a `Cow<str>` to allow fluent method chaining.
57```
58
59## No Std
60
61Disable the default features to compile this crate without std.
62
63```toml
64[dependencies.str-utils]
65version = "*"
66default-features = false
67```
68*/
69
70#![cfg_attr(not(feature = "std"), no_std)]
71#![cfg_attr(docsrs, feature(doc_cfg))]
72
73// TODO eq_ignore_case_multiple
74// TODO starts_with_ignore_case_multiple
75// TODO ends_with_ignore_case_multiple
76
77#[cfg(feature = "alloc")]
78extern crate alloc;
79
80#[cfg(feature = "alloc")]
81use alloc::string::String;
82
83#[cfg(feature = "alloc")]
84#[doc(hidden)]
85pub mod __macro_private {
86    pub use alloc::borrow::Cow;
87}
88
89/// Converts a `Cow` produced from an existing owned value back into an owned value.
90///
91/// If the `Cow` is owned, this returns the owned value inside it. If the `Cow` is borrowed, this returns the original owned value.
92///
93/// This is a macro so the `Cow` expression can borrow from the original value before the macro moves that value in the borrowed branch.
94///
95/// # Examples
96///
97/// ```rust
98/// use str_utils::{cow_into_owned, ToLowercase};
99///
100/// let s = String::from("abc");
101/// let ptr = s.as_ptr();
102/// let s = cow_into_owned!(s, s.as_str().to_lowercase_cow());
103///
104/// assert_eq!("abc", s);
105/// assert_eq!(ptr, s.as_ptr());
106/// ```
107#[cfg(feature = "alloc")]
108#[macro_export]
109macro_rules! cow_into_owned {
110    ($owned:expr, $cow:expr $(,)?) => {{
111        match $cow {
112            $crate::__macro_private::Cow::Owned(s) => s,
113            $crate::__macro_private::Cow::Borrowed(_) => $owned,
114        }
115    }};
116}
117
118mod ends_with_ignore_ascii_case;
119mod ends_with_ignore_ascii_case_multiple;
120#[cfg(feature = "alloc")]
121mod ends_with_ignore_case;
122mod ends_with_multiple;
123mod eq_ignore_ascii_case;
124mod eq_ignore_ascii_case_multiple;
125#[cfg(feature = "unicase")]
126mod eq_ignore_case;
127mod eq_multiple;
128#[cfg(feature = "alloc")]
129mod escape_characters;
130#[cfg(feature = "alloc")]
131mod expand_tabs;
132mod is_ascii_lowercased;
133mod is_ascii_uppercased;
134mod is_lowercased;
135mod is_uppercased;
136#[cfg(feature = "alloc")]
137mod newlines;
138#[cfg(feature = "alloc")]
139mod normalize_newlines;
140#[cfg(feature = "alloc")]
141mod pattern;
142#[cfg(feature = "alloc")]
143mod remove_all_invisible_characters;
144#[cfg(feature = "alloc")]
145mod replace;
146#[cfg(feature = "alloc")]
147mod replace_newlines_with_space;
148mod starts_with_ignore_ascii_case;
149mod starts_with_ignore_ascii_case_multiple;
150#[cfg(feature = "alloc")]
151mod starts_with_ignore_case;
152mod starts_with_multiple;
153#[cfg(feature = "alloc")]
154mod to_lowercase;
155#[cfg(feature = "alloc")]
156mod to_uppercase;
157#[cfg(feature = "alloc")]
158mod trim;
159mod trim_line;
160
161pub use ends_with_ignore_ascii_case::*;
162pub use ends_with_ignore_ascii_case_multiple::*;
163#[cfg(feature = "alloc")]
164pub use ends_with_ignore_case::*;
165pub use ends_with_multiple::*;
166pub use eq_ignore_ascii_case::*;
167pub use eq_ignore_ascii_case_multiple::*;
168#[cfg(feature = "unicase")]
169pub use eq_ignore_case::*;
170pub use eq_multiple::*;
171#[cfg(feature = "alloc")]
172pub use escape_characters::*;
173#[cfg(feature = "alloc")]
174pub use expand_tabs::*;
175pub use is_ascii_lowercased::*;
176pub use is_ascii_uppercased::*;
177pub use is_lowercased::*;
178pub use is_uppercased::*;
179#[cfg(feature = "alloc")]
180pub use normalize_newlines::*;
181#[cfg(feature = "alloc")]
182pub use pattern::*;
183#[cfg(feature = "alloc")]
184pub use remove_all_invisible_characters::*;
185#[cfg(feature = "alloc")]
186pub use replace::*;
187#[cfg(feature = "alloc")]
188pub use replace_newlines_with_space::*;
189pub use starts_with_ignore_ascii_case::*;
190pub use starts_with_ignore_ascii_case_multiple::*;
191#[cfg(feature = "alloc")]
192pub use starts_with_ignore_case::*;
193pub use starts_with_multiple::*;
194#[cfg(feature = "alloc")]
195pub use to_lowercase::*;
196#[cfg(feature = "alloc")]
197pub use to_uppercase::*;
198#[cfg(feature = "alloc")]
199pub use trim::*;
200pub use trim_line::*;
201
202#[cfg(feature = "alloc")]
203pub(crate) unsafe fn find_substring_position(parent: &str, sub: &str) -> (usize, usize) {
204    let parent_start_address = parent.as_ptr() as usize;
205
206    let sub_start_address = sub.as_ptr() as usize;
207    let position_len = sub.len();
208
209    let position_start = sub_start_address - parent_start_address;
210
211    (position_start, position_len)
212}
213
214#[cfg(feature = "alloc")]
215pub(crate) unsafe fn into_substring_in_place(
216    mut s: String,
217    (start, len): (usize, usize),
218) -> String {
219    let bytes = s.as_mut_vec();
220
221    bytes.drain(..start);
222    bytes.truncate(len);
223
224    s
225}
226
227#[cfg(feature = "alloc")]
228macro_rules! to_substring_in_place {
229    ($parent:ident, $sub:ident) => {{
230        let position = $crate::find_substring_position($parent.as_str(), $sub);
231
232        $crate::into_substring_in_place($parent, position)
233    }};
234}
235
236#[cfg(feature = "alloc")]
237pub(crate) use to_substring_in_place;