slash_formatter/
slash.rs

1use alloc::{borrow::Cow, string::String};
2
3/// Delete an ending slash in a string except for '/'.
4///
5/// ```
6/// assert_eq!("path", slash_formatter::delete_end_slash("path/"));
7/// ```
8#[inline]
9pub fn delete_end_slash<S: ?Sized + AsRef<str>>(s: &S) -> &str {
10    let s = s.as_ref();
11
12    let length = s.len();
13
14    if length > 1 && s.ends_with('/') {
15        unsafe { s.get_unchecked(..length - 1) }
16    } else {
17        s
18    }
19}
20
21/// Delete an ending slash in a string except for '/'.
22///
23/// ```
24/// let mut s = String::from("path/");
25///
26/// slash_formatter::delete_end_slash_in_place(&mut s);
27///
28/// assert_eq!("path", s);
29/// ```
30#[inline]
31pub fn delete_end_slash_in_place(s: &mut String) {
32    let length = s.len();
33
34    if length > 1 && s.ends_with('/') {
35        unsafe {
36            s.as_mut_vec().set_len(length - 1);
37        }
38    }
39}
40
41/// Delete a starting slash in a string except for '/'.
42///
43/// ```
44/// assert_eq!("path", slash_formatter::delete_start_slash("/path"));
45/// ```
46#[inline]
47pub fn delete_start_slash<S: ?Sized + AsRef<str>>(s: &S) -> &str {
48    let s = s.as_ref();
49
50    let length = s.len();
51
52    if length > 1 && s.starts_with('/') {
53        unsafe { s.get_unchecked(1..) }
54    } else {
55        s
56    }
57}
58
59/// Delete a starting slash in a string except for '/'.
60///
61/// ```
62/// let mut s = String::from("/path");
63///
64/// slash_formatter::delete_start_slash_in_place(&mut s);
65///
66/// assert_eq!("path", s);
67/// ```
68#[inline]
69pub fn delete_start_slash_in_place(s: &mut String) {
70    let length = s.len();
71
72    if length > 1 && s.starts_with('/') {
73        s.remove(0);
74    }
75}
76
77/// Add a starting slash into a string.
78///
79/// ```
80/// assert_eq!("/path", slash_formatter::add_start_slash("path"));
81/// ```
82#[inline]
83pub fn add_start_slash<S: ?Sized + AsRef<str>>(s: &S) -> Cow<str> {
84    let s = s.as_ref();
85
86    if s.starts_with('/') {
87        Cow::from(s)
88    } else {
89        Cow::from(format!("/{}", s))
90    }
91}
92
93/// Add a starting slash into a string.
94///
95/// ```
96/// let mut s = String::from("path");
97///
98/// slash_formatter::add_start_slash_in_place(&mut s);
99///
100/// assert_eq!("/path", s);
101/// ```
102#[inline]
103pub fn add_start_slash_in_place(s: &mut String) {
104    if !s.starts_with('/') {
105        s.insert(0, '/');
106    }
107}
108
109/// Add an ending slash into a string.
110///
111/// ```
112/// assert_eq!("path/", slash_formatter::add_end_slash("path"));
113/// ```
114#[inline]
115pub fn add_end_slash<S: ?Sized + AsRef<str>>(s: &S) -> Cow<str> {
116    let s = s.as_ref();
117
118    if s.ends_with('/') {
119        Cow::from(s)
120    } else {
121        Cow::from(format!("{}/", s))
122    }
123}
124
125/// Add an ending slash into a string.
126///
127/// ```
128/// let mut s = String::from("path");
129///
130/// slash_formatter::add_end_slash_in_place(&mut s);
131///
132/// assert_eq!("path/", s);
133/// ```
134#[inline]
135pub fn add_end_slash_in_place(s: &mut String) {
136    if !s.ends_with('/') {
137        s.push('/');
138    }
139}
140
141/// Concatenate two strings with a slash.
142///
143/// ```
144/// assert_eq!("path/to", slash_formatter::concat_with_slash("path", "to/"));
145/// ```
146#[inline]
147pub fn concat_with_slash<S1: Into<String>, S2: AsRef<str>>(s1: S1, s2: S2) -> String {
148    let mut s1 = s1.into();
149
150    concat_with_slash_in_place(&mut s1, s2);
151
152    s1
153}
154
155/// Concatenate two strings with a slash.
156///
157/// ```
158/// let mut s = String::from("path");
159///
160/// slash_formatter::concat_with_slash_in_place(&mut s, "to/");
161///
162/// assert_eq!("path/to", s);
163/// ```
164#[inline]
165pub fn concat_with_slash_in_place<S2: AsRef<str>>(s1: &mut String, s2: S2) {
166    add_end_slash_in_place(s1);
167    s1.push_str(delete_start_slash(s2.as_ref()));
168    delete_end_slash_in_place(s1);
169}
170
171/**
172Concatenate multiple strings with slashes. It can also be used to get the literal `'/'`.
173
174```
175assert_eq!("path/to/file", slash_formatter::slash!("path", "to/", "/file/"));
176
177let s = String::from("path");
178
179let s = slash_formatter::slash!(s, "to/", "/file/");
180
181assert_eq!("path/to/file", s);
182```
183*/
184#[macro_export]
185macro_rules! slash {
186    () => {
187        '/'
188    };
189    ($s:expr $(, $sc:expr)* $(,)*) => {
190        {
191            let mut s = $s.to_owned();
192
193            $(
194                $crate::concat_with_slash_in_place(&mut s, $sc);
195            )*
196
197            s
198        }
199    };
200}
201
202/**
203Concatenate multiple strings with slashes. It can also be used to get the literal `'/'`.
204
205```
206let mut s = String::from("path");
207
208slash_formatter::slash_in_place!(&mut s, "to/", "/file/");
209
210assert_eq!("path/to/file", s);
211```
212*/
213#[macro_export]
214macro_rules! slash_in_place {
215    () => {
216        '/'
217    };
218    ($s:expr $(, $sc:expr)* $(,)*) => {
219        $(
220            $crate::concat_with_slash_in_place($s, $sc);
221        )*
222    };
223}
224
225concat_with::concat_impl! {
226    #[macro_export]
227    /// Concatenates literals into a static string slice separated by a slash. Prefixes and suffixes can also be added.
228    ///
229    /// ```rust
230    /// assert_eq!("test/10/b/true", slash_formatter::concat_with_slash!("test", 10, 'b', true));
231    /// ```
232    concat_with_slash => "/"
233}