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}