mut_str/
replace.rs

1use core::{slice, str};
2
3use crate::errors::{
4    LenNotEqual, ReplaceWithPadCharError, ReplaceWithPadError, ReplacementTooLong,
5};
6
7/// Replace the [`prim@str`] with another of the same length.
8///
9/// ```
10/// use mut_str::replace;
11///
12/// let mut owned_s = Box::<str>::from("World!");
13///
14/// replace(&mut *owned_s, "🌍!!").unwrap();
15/// assert_eq!(&*owned_s, "🌍!!");
16///
17/// replace(&mut *owned_s, "aaaaaa").unwrap();
18/// assert_eq!(&*owned_s, "aaaaaa");
19/// ```
20///
21/// # Errors
22/// - If `s` and `r`, when utf8 encoded, do not have the same length, [`LenNotEqual`] will be returned.
23pub fn replace<'a>(s: &'a mut str, r: &str) -> Result<&'a mut str, LenNotEqual> {
24    if r.len() != s.len() {
25        return Err(LenNotEqual);
26    }
27
28    // SAFETY:
29    // This is valid as it copies one valid string to another of the same
30    // length.
31    unsafe { s.as_bytes_mut() }.copy_from_slice(r.as_bytes());
32
33    Ok(s)
34}
35
36/// Replace the [`prim@str`] with another of the same length or shorter.
37/// The remaining bytes will be filled with spaces.
38///
39/// ```
40/// use mut_str::replace_with_pad_space;
41///
42/// let mut owned_s = Box::<str>::from("World!");
43///
44/// replace_with_pad_space(&mut *owned_s, "🌍").unwrap();
45/// assert_eq!(&*owned_s, "🌍  ");
46///
47/// replace_with_pad_space(&mut *owned_s, "aaaa").unwrap();
48/// assert_eq!(&*owned_s, "aaaa  ");
49/// ```
50///
51/// # Errors
52/// - If `r`, when utf8 encoded, is longer than `s`, when utf8 encoded, [`ReplacementTooLong`] will be returned.
53pub fn replace_with_pad_space<'a>(
54    s: &'a mut str,
55    r: &str,
56) -> Result<&'a mut str, ReplacementTooLong> {
57    if r.len() > s.len() {
58        return Err(ReplacementTooLong);
59    }
60
61    // SAFETY:
62    // This is valid as it copies one valid string to another of at least the
63    // same length, replacing the remainder with one byte padding.
64    let (slice, remaining) = unsafe { s.as_bytes_mut() }.split_at_mut(r.len());
65
66    slice.copy_from_slice(r.as_bytes());
67    remaining.fill(b' ');
68
69    // SAFETY:
70    // This is valid as`slice` comes from a `str` split on a char boundary.
71    Ok(unsafe { str::from_utf8_unchecked_mut(slice) })
72}
73
74/// Replace the [`prim@str`] with another of the same length or shorter.
75/// The remaining bytes will be filled with `pad`.
76///
77/// ```
78/// use mut_str::replace_with_pad;
79///
80/// let mut owned_s = Box::<str>::from("World!");
81///
82/// replace_with_pad(&mut *owned_s, "🌍", b'!').unwrap();
83/// assert_eq!(&*owned_s, "🌍!!");
84///
85/// replace_with_pad(&mut *owned_s, "aaaa", b'b').unwrap();
86/// assert_eq!(&*owned_s, "aaaabb");
87/// ```
88///
89/// # Errors
90/// - If `pad` is not valid utf8, [`ReplaceWithPadError::InvalidPad`] will be returned.
91/// - If `r`, when utf8 encoded, is longer than `s`, when utf8 encoded, [`ReplaceWithPadError::ReplacementLen`] will be returned.
92pub fn replace_with_pad<'a>(
93    s: &'a mut str,
94    r: &str,
95    pad: u8,
96) -> Result<&'a mut str, ReplaceWithPadError> {
97    if r.len() > s.len() {
98        return Err(ReplaceWithPadError::CHAR_LEN);
99    }
100
101    str::from_utf8(&[pad])?;
102
103    // SAFETY:
104    // This is valid as it copies one valid string to another of at least the
105    // same length, replacing the remainder with one byte padding.
106    let (slice, remaining) = unsafe { s.as_bytes_mut() }.split_at_mut(r.len());
107
108    slice.copy_from_slice(r.as_bytes());
109    remaining.fill(pad);
110
111    // SAFETY:
112    // This is valid as`slice` comes from a `str` split on a char boundary.
113    Ok(unsafe { str::from_utf8_unchecked_mut(slice) })
114}
115
116/// Replace the [`prim@str`] with another of the same length or shorter.
117/// The remaining bytes will be filled with `pad`, which must be one byte long.
118///
119/// ```
120/// use mut_str::replace_with_pad_char;
121///
122/// let mut owned_s = Box::<str>::from("World!");
123///
124/// replace_with_pad_char(&mut *owned_s, "🌍", '!').unwrap();
125/// assert_eq!(&*owned_s, "🌍!!");
126///
127/// replace_with_pad_char(&mut *owned_s, "aaaa", 'b').unwrap();
128/// assert_eq!(&*owned_s, "aaaabb");
129/// ```
130///
131/// # Errors
132/// - If `pad_char`, when utf8 encoded, is longer than `Self`, [`ReplaceWithPadCharError::PadCharTooLong`] will be returned.
133/// - If `r`, when utf8 encoded, is longer than `s`, when utf8 encoded, [`ReplaceWithPadCharError::ReplacementLen`] will be returned.
134pub fn replace_with_pad_char<'a, C>(
135    s: &'a mut str,
136    r: &str,
137    pad_char: C,
138) -> Result<&'a mut str, ReplaceWithPadCharError>
139where
140    C: Into<char>,
141{
142    let pad_char = pad_char.into();
143
144    if r.len() > s.len() {
145        return Err(ReplaceWithPadCharError::CHAR_LEN);
146    }
147
148    if pad_char.len_utf8() != 1 {
149        return Err(ReplaceWithPadCharError::PadCharTooLong);
150    }
151
152    let mut pad: u8 = 0;
153    pad_char.encode_utf8(slice::from_mut(&mut pad));
154
155    // SAFETY:
156    // This is valid as it copies one valid string to another of at least the
157    // same length, replacing the remainder with one byte padding.
158    let (slice, remaining) = unsafe { s.as_bytes_mut() }.split_at_mut(r.len());
159
160    slice.copy_from_slice(r.as_bytes());
161    remaining.fill(pad);
162
163    // SAFETY:
164    // This is valid as`slice` comes from a `str` split on a char boundary.
165    Ok(unsafe { str::from_utf8_unchecked_mut(slice) })
166}
167
168/// Replace the [`prim@str`] with another of the same length or shorter, right aligned.
169/// The remaining bytes before the [`prim@str`] will be filled with spaces.
170///
171/// ```
172/// use mut_str::replace_with_pad_left_space;
173///
174/// let mut owned_s = Box::<str>::from("World!");
175///
176/// replace_with_pad_left_space(&mut *owned_s, "🌍").unwrap();
177/// assert_eq!(&*owned_s, "  🌍");
178///
179/// replace_with_pad_left_space(&mut *owned_s, "aaaa").unwrap();
180/// assert_eq!(&*owned_s, "  aaaa");
181/// ```
182///
183/// # Errors
184/// - If `r`, when utf8 encoded, is longer than `s`, when utf8 encoded, [`ReplacementTooLong`] will be returned.
185pub fn replace_with_pad_left_space<'a>(
186    s: &'a mut str,
187    r: &str,
188) -> Result<&'a mut str, ReplacementTooLong> {
189    if r.len() > s.len() {
190        return Err(ReplacementTooLong);
191    }
192
193    let mid = s.len() - r.len();
194
195    // SAFETY:
196    // This is valid as it copies one valid string to another of at least the
197    // same length, replacing the remainder with one byte padding.
198    let (remaining, slice) = unsafe { s.as_bytes_mut() }.split_at_mut(mid);
199
200    slice.copy_from_slice(r.as_bytes());
201    remaining.fill(b' ');
202
203    // SAFETY:
204    // This is valid as`slice` comes from a `str` split on a char boundary.
205    Ok(unsafe { str::from_utf8_unchecked_mut(slice) })
206}
207
208/// Replace the [`prim@str`] with another of the same length or shorter, right aligned.
209/// The remaining bytes before the character [`prim@str`] will be filled with `pad`.
210///
211/// ```
212/// use mut_str::replace_with_pad_left;
213///
214/// let mut owned_s = Box::<str>::from("World!");
215///
216/// replace_with_pad_left(&mut *owned_s, "🌍", b'!').unwrap();
217/// assert_eq!(&*owned_s, "!!🌍");
218///
219/// replace_with_pad_left(&mut *owned_s, "aaaa", b'b').unwrap();
220/// assert_eq!(&*owned_s, "bbaaaa");
221/// ```
222///
223/// # Errors
224/// - If `pad` is not valid utf8, [`ReplaceWithPadError::InvalidPad`] will be returned.
225/// - If `r`, when utf8 encoded, is longer than `s`, when utf8 encoded, [`ReplaceWithPadError::ReplacementLen`] will be returned.
226pub fn replace_with_pad_left<'a>(
227    s: &'a mut str,
228    r: &str,
229    pad: u8,
230) -> Result<&'a mut str, ReplaceWithPadError> {
231    if r.len() > s.len() {
232        return Err(ReplaceWithPadError::CHAR_LEN);
233    }
234
235    str::from_utf8(&[pad])?;
236
237    let mid = s.len() - r.len();
238
239    // SAFETY:
240    // This is valid as it copies one valid string to another of at least the
241    // same length, replacing the remainder with one byte padding.
242    let (remaining, slice) = unsafe { s.as_bytes_mut() }.split_at_mut(mid);
243
244    slice.copy_from_slice(r.as_bytes());
245    remaining.fill(pad);
246
247    // SAFETY:
248    // This is valid as`slice` comes from a `str` split on a char boundary.
249    Ok(unsafe { str::from_utf8_unchecked_mut(slice) })
250}
251
252/// Replace the [`prim@str`] with another of the same length or shorter, right aligned.
253/// The remaining bytes before the [`prim@str`] will be filled with `char`, which must be one byte long.
254///
255/// ```
256/// use mut_str::replace_with_pad_left_char;
257///
258/// let mut owned_s = Box::<str>::from("World!");
259///
260/// replace_with_pad_left_char(&mut *owned_s, "🌍", '!').unwrap();
261/// assert_eq!(&*owned_s, "!!🌍");
262///
263/// replace_with_pad_left_char(&mut *owned_s, "aaaa", 'b').unwrap();
264/// assert_eq!(&*owned_s, "bbaaaa");
265/// ```
266///
267/// # Errors
268/// - If `pad_char`, when utf8 encoded, is longer than `Self`, [`ReplaceWithPadCharError::PadCharTooLong`] will be returned.
269/// - If `r`, when utf8 encoded, is longer than `s`, when utf8 encoded, [`ReplaceWithPadCharError::ReplacementLen`] will be returned.
270pub fn replace_with_pad_left_char<'a, C>(
271    s: &'a mut str,
272    r: &str,
273    pad_char: C,
274) -> Result<&'a mut str, ReplaceWithPadCharError>
275where
276    C: Into<char>,
277{
278    let pad_char = pad_char.into();
279    if r.len() > s.len() {
280        return Err(ReplaceWithPadCharError::CHAR_LEN);
281    }
282
283    if pad_char.len_utf8() != 1 {
284        return Err(ReplaceWithPadCharError::PadCharTooLong);
285    }
286
287    let mut pad: u8 = 0;
288    pad_char.encode_utf8(slice::from_mut(&mut pad));
289
290    let mid = s.len() - r.len();
291
292    // SAFETY:
293    // This is valid as it copies one valid string to another of at least the
294    // same length, replacing the remainder with one byte padding.
295    let (remaining, slice) = unsafe { s.as_bytes_mut() }.split_at_mut(mid);
296
297    slice.copy_from_slice(r.as_bytes());
298    remaining.fill(pad);
299
300    // SAFETY:
301    // This is valid as`slice` comes from a `str` split on a char boundary.
302    Ok(unsafe { str::from_utf8_unchecked_mut(slice) })
303}