osstrtools_fix/
lib.rs

1//! # OsStrTools
2//!
3//! `OsStrTools` adds some useful methods to `OsStr` and `OsString`
4//! that are missing in the standard library, like
5//! [`split()`](trait.OsStrTools.html#tymethod.split),
6//! [`replace()`](trait.OsStrTools.html#tymethod.replace), or
7//! [`splice()`](trait.OsStrTools.html#tymethod.splice). It is mostly
8//! useful for dealing dealing with things like file names, command
9//! line arguments, `PathBuf`, and the like.
10//!
11//! Windows support is experimental, but shoud hopefully mostly work,
12//! although it is not well tested and likely somewhat slower due to
13//! some overhead since it requires checking the strings for
14//! correctness. The checking is done by
15//! [`os_str_bytes`](https://crates.io/crates/os_str_bytes).
16//!
17//! This crate is still maintained, but mostly has been deprecated in
18//! favor of [`bstr`](https://crates.io/crates/bstr). If any bugs
19//! remain it's likely they will be fixed as they come up, though.
20
21use itertools::{Itertools, Position};
22use std::ffi::{OsStr, OsString};
23#[cfg(not(target_os = "windows"))]
24use std::os::unix::ffi::{OsStrExt, OsStringExt};
25
26
27/// Temporary storage for the string to be spliced. Can assemble the
28/// final [`OsString`] in different ways, depending on the use-case.
29/// Created by [`splice()`](trait.OsStrTools.html#tymethod.splice).
30pub struct StringSplicer<'a, T: Bytes> {
31    string: Vec<&'a OsStr>,
32    to: &'a [T],
33}
34
35impl<'a, T: Bytes> StringSplicer<'a, T> {
36    fn new(string: Vec<&'a OsStr>, to: &'a [T]) -> Self {
37        Self {
38            string,
39            to
40        }
41    }
42
43    /// Assembles the final string by appending individual items
44    /// without any other processing.
45    pub fn assemble(self) -> OsString
46    {
47        self.assemble_with_sep_and_wrap("", "")
48    }
49
50    /// Places a separator between all the elements that are spliced
51    /// into the string.
52    pub fn assemble_with_sep(self, sep: impl Bytes) -> OsString
53    {
54        self.assemble_with_sep_and_wrap(sep, "")
55    }
56
57    /// Wraps all items with the provided value before inserting them
58    /// into the string.
59    pub fn assemble_with_wrap(self, wrap: impl Bytes) -> OsString
60    {
61        self.assemble_with_sep_and_wrap("", wrap)
62    }
63
64    /// Wraps all items with the provided value and places the
65    /// separator item between the individual elements.
66    /// # Examples
67    ///
68    /// ```
69    /// use std::ffi::OsStr;
70    /// use osstrtools::{OsStrTools, StringSplicer};
71    ///
72    /// let strings: &[&OsStr] = &[OsStr::new("/foo/bar"),
73    ///                            OsStr::new("/baz/")];
74    /// let s = OsStr::new("rm #");
75    ///
76    /// let mut splicer = s.splice("#", strings);
77    /// let result = splicer.assemble_with_sep_and_wrap(" ", "'");
78    ///
79    /// assert_eq!(result, "rm '/foo/bar' '/baz/'");
80    /// ```
81    pub fn assemble_with_sep_and_wrap(self,
82                                      sep: impl Bytes,
83                                      wrap: impl Bytes) -> OsString
84    {
85        // If len is 0, string was empty
86        if self.string.len() == 0 {
87            return OsString::new();
88        }
89
90        // No splits means no pattern was found
91        if self.string.len() == 1 {
92            return self.string[0].to_os_string();
93        }
94
95        let sep = sep.as_byte_slice();
96        let wrap = wrap.as_byte_slice();
97        let part_count = self.string.len();
98        let to_count = self.to.len();
99
100        let splice_items = self.to.into_iter()
101            .enumerate()
102            .fold(OsString::new(), |mut splice_items, (i, item)| {
103                splice_items.push(wrap.bytes_as_os_str());
104                splice_items.push(item.bytes_as_os_str());
105                splice_items.push(wrap.bytes_as_os_str());
106
107                // Don't add separator for last item
108                if i+1 < to_count {
109                    splice_items.push(sep.bytes_as_os_str());
110                }
111                splice_items
112            });
113
114
115        self.string
116            .into_iter()
117            .take(part_count-1)
118            .fold(OsString::new(), |mut parts, part| {
119                parts.push(part);
120                parts.push(&splice_items);
121
122                parts
123            })
124    }
125}
126
127
128
129/// Conversion Trait implemented for many byte sources that can be used as
130/// arguments for search patterns and replacement strings. The methods
131/// probably shouldn't be called anywhere outside this crate.
132pub trait Bytes: std::fmt::Debug
133{
134    /// Returns the underlying buffer as a `&[u8]` slice.
135    ///
136    /// # WARNING
137    ///
138    /// Windows support requires transmuting the [`OsStr`] into a
139    /// slice and back, since there is no other way to directly access
140    /// the underlying bytes. This should be fine for what this
141    /// library does, since all inputs are valid UTF8/WTF8 anyway and
142    /// all this library does is combine or split bytes at valid
143    /// edges. One exception is the possibility to supply arbitrarily
144    /// prepared slices as inputs, but even then, all strings are
145    /// checked for validity before being transmuted back into `OsStr`
146    /// so there is no chance to end up with a broken string.
147    fn as_byte_slice(&self) -> &[u8];
148
149    /// Don't use this outside of this crate.
150    ///
151    /// # WARNING
152    ///
153    /// Transmutes arbitrary byte slices into `OsStr` on Windows which
154    /// could cause bad things to happen. To prevent this, the strings
155    /// are checked for validity before the conversion and if they
156    /// contain invalid bytes a panic will ensure.
157    ///
158    /// # Panics
159    ///
160    /// On Windows this can panic when called on a malformed
161    /// collection of bytes.
162    fn bytes_as_os_str(&self) -> &OsStr {
163        let bytes = self.as_byte_slice();
164        OsStr::from_bytes(bytes)
165    }
166}
167
168
169impl Bytes for &[u8] {
170    fn as_byte_slice(&self) -> &[u8]  {
171        self
172    }
173}
174
175impl Bytes for Vec<u8> {
176    fn as_byte_slice(&self) -> &[u8]  {
177        self.as_slice()
178    }
179}
180
181impl Bytes for &Vec<u8> {
182    fn as_byte_slice(&self) -> &[u8]  {
183        self.as_slice()
184    }
185}
186
187impl Bytes for OsStr {
188    fn as_byte_slice(&self) -> &[u8] {
189        self.as_bytes()
190    }
191}
192
193impl Bytes for &OsStr {
194    fn as_byte_slice(&self) -> &[u8] {
195        self.as_bytes()
196    }
197}
198
199impl Bytes for &&OsStr {
200    fn as_byte_slice(&self) -> &[u8] {
201        self.as_bytes()
202    }
203}
204
205impl Bytes for OsString {
206    fn as_byte_slice(&self) -> &[u8] {
207        self.as_os_str().as_byte_slice()
208    }
209}
210
211impl Bytes for &OsString {
212    fn as_byte_slice(&self) -> &[u8] {
213        self.as_os_str().as_byte_slice()
214    }
215}
216
217impl Bytes for str {
218    fn as_byte_slice(&self) -> &[u8] {
219        self.as_bytes()
220    }
221}
222
223impl Bytes for &str {
224    fn as_byte_slice(&self) -> &[u8] {
225        self.as_bytes()
226    }
227}
228
229
230
231/// Concatenation of things that can be turned into an [`Iterator`] of
232/// elements that implement [`Bytes`].
233pub trait OsStrConcat {
234    /// Concatenates all the strings and places the separator between
235    /// all elements. The separator can be an empty string.
236    /// # Examples
237    ///
238    /// ```
239    /// use std::ffi::OsStr;
240    /// use osstrtools::OsStrConcat;
241    ///
242    /// let strings: &[&OsStr] = &[OsStr::new("a"),
243    ///                            OsStr::new("b"),
244    ///                            OsStr::new("c")];
245    /// let result = strings.concat(" ");
246    ///
247    /// assert_eq!(result, "a b c");
248    /// ```
249    ///
250    fn concat(self, sep: impl Bytes) -> OsString;
251}
252
253impl<I> OsStrConcat for I
254where
255    I: IntoIterator,
256    I::Item: Bytes + Clone,
257{
258    fn concat(self, sep: impl Bytes) -> OsString {
259        let sep = sep.as_byte_slice();
260
261        let mut string = self.into_iter()
262            .fold(Vec::new(), |mut string,  part| {
263                string.extend_from_slice(part.as_byte_slice());
264                string.extend_from_slice(sep);
265
266                string
267            });
268
269        // Remove last separator
270        string.truncate(string.len() - sep.len());
271
272        OsString::from_vec(string.to_vec())
273    }
274}
275
276
277
278/// Includes some of the methods of [`OsStrTools`] but they take the
279/// [`OsString`] by value and return it without modification if the
280/// specific search pattern isn't found.
281///
282/// Allocation is also avoided by reusing parts of the old string when
283/// possible. As such, the methods here can be slightly faster and
284/// might be preferable in some cases.
285pub trait OsStringTools {
286    /// Replace all matches with something else.
287    ///
288    ///If no matches are found the returned string contains the whole
289    /// original string.
290    fn replace(self, pat: impl Bytes, to: impl Bytes) -> OsString;
291
292    /// Replace double quote characters in the string with `"\\\""` for
293    /// safe handling over to a shell.
294    fn escape_double_quote(self) -> OsString;
295
296    /// Replace single quote characters in the string with `"\\''"` for
297    /// safe handling over to a shell.
298    fn escape_single_quote(self) -> OsString;
299
300    /// Remove all matches of the pattern until a different character
301    /// is found.
302    ///
303    /// Returns the rest.
304    fn trim_start(self, pat: impl Bytes) -> OsString;
305
306    /// Remove all matches of the pattern from the end until a
307    /// different character is found.
308    ///
309    /// Returns the rest.
310    fn trim_end(self, pat: impl Bytes) -> OsString;
311}
312
313impl OsStringTools for OsString {
314    #[inline]
315    fn replace(self, pat: impl Bytes, to: impl Bytes) -> OsString {
316        let pat = pat.as_byte_slice();
317        if self.contains(pat) {
318            self.as_os_str().replace(pat, to)
319        } else {
320            self
321        }
322    }
323
324    #[inline]
325    fn escape_double_quote(self) -> OsString {
326        if self.contains("\"") {
327            self.as_os_str().escape_double_quote()
328        } else {
329            self
330        }
331    }
332
333    #[inline]
334    fn escape_single_quote(self) -> OsString {
335        if self.contains("'") {
336            self.as_os_str().escape_single_quote()
337        } else {
338            self
339        }
340    }
341
342    #[inline]
343    fn trim_start(self, pat: impl Bytes) -> OsString {
344        let pat = pat.as_byte_slice();
345
346        let skip_n = *self.as_bytes()
347            .chunks(pat.len())
348            .enumerate()
349            .take_while(|(_, part)| part == &pat)
350            .map(|(i,_)| i)
351            .collect_vec()
352            .last()
353            .unwrap_or(&0);
354
355        // First byte is something else already
356        if skip_n == 0 {
357            return self;
358        } else {
359            let mut string = self.into_vec();
360            // Move bytes around and remove the matched bytes
361            string.rotate_left(skip_n+1);
362            string.truncate((string.len() - skip_n) - 1);
363
364            return OsString::from_vec(string);
365        }
366    }
367
368    #[inline]
369    fn trim_end(self, pat: impl Bytes) -> OsString {
370        let pat = pat.as_byte_slice();
371        let mut string = self.into_vec();
372
373        while string.ends_with(pat) {
374            string.truncate(string.len() - pat.len())
375        }
376
377        OsString::from_vec(string)
378    }
379}
380
381/// Extension Trait for OsStr to make working OsStr them more
382/// ergonomic. It contains many methods that work similarly to those
383/// of [`String`].
384pub trait OsStrTools
385{
386    /// Split the string by looking for pat.
387    ///
388    /// Splits don't include the pattern itself. Like the stdlib
389    /// multiple consecutive matches result in empty strings placed in
390    /// between.
391    ///
392    /// If the string starts with the pattern the first element will
393    /// be an empty string. If the string ends with the pattern, there
394    /// will be an empty string at the end.
395    fn split(&self, pat: impl Bytes) -> Vec<&OsStr>;
396
397    /// Splits lines by looking for `"\n"`.
398    ///
399    /// Same as `.split("\n")`.
400    fn split_lines(&self) -> Vec<&OsStr>;
401
402    /// Replace all matches with something else.
403    ///
404    ///If no matches are found the returned string contains the whole
405    /// original string.
406    fn replace(&self, pat: impl Bytes, to: impl Bytes) -> OsString;
407
408    /// Remove all matches of the pattern until a different character
409    /// is found.
410    ///
411    /// Returns the rest.
412    fn trim_start(&self, pat: impl Bytes) -> &OsStr;
413
414    /// Remove all matches of the pattern from the end until a
415    /// different character is found.
416    ///
417    /// Returns the rest.
418    fn trim_end(&self, pat: impl Bytes) -> &OsStr;
419
420    /// Check if the string contains the pattern.
421    fn contains(&self, pat: impl Bytes) -> bool;
422
423    /// Looks through the string for the pattern.
424    ///
425    /// The position of the first match is returned. If no matches are
426    /// found it returns `None`.
427    fn position(&self, pat: impl Bytes) -> Option<usize>;
428
429    /// Similar to replace, but instead of inserting a single
430    /// replacement string this allows inserting multiple strings at
431    /// the match position.
432    ///
433    /// Returns a [`StringSplicer`] which allows to specify how the
434    /// strings should be inserted.
435    ///
436    /// # Examples
437    ///
438    /// ```
439    /// use std::ffi::OsStr;
440    /// use osstrtools::{OsStrTools, StringSplicer};
441    ///
442    /// let strings: &[&OsStr] = &[OsStr::new("is"),
443    ///                            OsStr::new("an"),
444    ///                            OsStr::new("example")];
445    /// let s = OsStr::new("this #");
446    ///
447    /// let mut splicer: StringSplicer<'_, _> = s.splice("#", strings);
448    /// let result = splicer.assemble_with_sep(" ");
449    ///
450    /// assert_eq!(result, "this is an example");
451    /// ```
452    fn splice<'a, B: Bytes>(&'a self,
453                            from: impl Bytes,
454                            to: &'a [B]) -> StringSplicer<'a, B>;
455
456    /// Wraps the string with double quote characters.
457    fn quote_double(&self) -> OsString;
458
459    /// Wraps the string with single quote characters.
460    fn quote_single(&self) -> OsString;
461
462    /// Replace double quote characters in the string with `"\\\""` for
463    /// safe handling over to a shell.
464    fn escape_double_quote(&self) -> OsString;
465
466    /// Replace single quote characters in the string with `"\\''"` for
467    /// safe handling over to a shell.
468    fn escape_single_quote(&self) -> OsString;
469}
470
471
472
473impl OsStrTools for OsStr
474{
475    fn split(&self, pat: impl Bytes) -> Vec<&OsStr>
476    {
477        let orig_string = self.as_byte_slice();
478        let pat = pat.as_byte_slice();
479        let pat_len = pat.len();
480        let mut last_pos = 0;
481
482        let split_string = orig_string
483            .windows(pat_len)
484            .enumerate()
485            .scan(0, |prev_split_end, (i, chars)| {
486                if chars == pat {
487                    let split_at = *prev_split_end;
488                    *prev_split_end = i+pat_len;
489                    Some(Some((split_at, i)))
490                } else {
491                    Some(None)
492                }
493            })
494            .filter_map(|s| s)
495            .map(|(start, end)| {
496                let split = &orig_string[start..end];
497                (OsStr::from_bytes(split), end)
498            })
499            .with_position()
500            .batching(move |it| {
501                match it.next() {
502                    Some(item) => {
503                        let string = match item {
504                            Position::First((string, _))  |
505                            Position::Middle((string, _)) => string,
506                            Position::Last((string, pos)) |
507                            Position::Only((string, pos)) => {
508                                last_pos = pos ;
509                                string
510                            }
511                        };
512
513                        return Some(string)
514                    }
515                    None => {
516                        // No position was updated. Either NO or ONLY pattern in string.
517                        if last_pos == 0 {
518                            last_pos = self.len();
519                            // If they're the same it means there was only a pattern
520                            if self.as_byte_slice() == pat.as_byte_slice() {
521                                return Some(OsStr::new(""));
522                            } else if self.len() == 0 {
523                                return None;
524                            } else {
525                                return Some(self);
526                            }
527                        } else if last_pos+pat_len <= self.len() {
528                            // There is still some of the string left
529                            let last = last_pos;
530                            let remaining = self.len() - last_pos;
531                            last_pos = self.len();
532                            let split = &orig_string[last+pat_len..last+remaining];
533                            return Some(OsStr::from_bytes(split));
534                        } else { return None; }
535                    }
536                }
537            })
538            .collect();
539
540        split_string
541    }
542
543    fn replace(&self, pat: impl Bytes, with: impl Bytes) -> OsString
544    {
545        if self.len() == 0 {
546            return OsString::new()
547        }
548
549        let string_len = self.len();
550        let replace_len = with.as_byte_slice().len();
551        let replace_iter = std::iter::repeat(OsStr::from_bytes(with.as_byte_slice()));
552
553        let splits = self.split(pat);
554        let split_len = splits.len();
555
556        let splits = splits
557            .into_iter()
558            .interleave_shortest(replace_iter);
559
560        let mut new_string = OsString::with_capacity(string_len + replace_len);
561
562        for split in splits.take(split_len*2-1) {
563            new_string.push(split);
564        }
565
566        new_string
567    }
568
569    fn split_lines(&self) -> Vec<&OsStr> {
570        let newline = "\n";
571        self.split(newline)
572    }
573
574    fn quote_double(&self) -> OsString {
575        let string_len = self.len();
576        let quote = "\"";
577
578        let mut new_string = OsString::with_capacity(string_len + 2);
579        new_string.push(quote);
580        new_string.push(self);
581        new_string.push(quote);
582
583        new_string
584    }
585
586    fn quote_single(&self) -> OsString
587    {
588        let string = self;
589        let string_len = string.len();
590        let quote = OsStr::new("\'");
591
592        let mut new_string = OsString::with_capacity(string_len + 2);
593        new_string.push(quote);
594        new_string.push(string);
595        new_string.push(quote);
596
597        new_string
598    }
599
600    fn splice<'a, B: Bytes>(&'a self,
601                            pat: impl Bytes,
602                            to: &'a [B]) -> StringSplicer<'a, B> {
603        let string = self.split(pat);
604        StringSplicer::new(string, to)
605    }
606
607
608    fn trim_start(&self, pat: impl Bytes) -> &OsStr {
609        let mut string = self.as_bytes();
610        let pat = pat.as_byte_slice();
611        let pat_len = pat.len();
612
613        while string.starts_with(pat) {
614            string = &string[pat_len..];
615        }
616
617        OsStr::from_bytes(string)
618    }
619
620    fn trim_end(&self, pat: impl Bytes) -> &OsStr {
621        let mut string = self.as_bytes();
622        let pat = pat.as_byte_slice();
623        let pat_len = pat.len();
624
625        while string.ends_with(pat) {
626            string = &string[..string.len()-pat_len];
627        }
628
629        OsStr::from_bytes(string)
630    }
631
632    fn contains(&self, pat: impl Bytes) -> bool {
633        self.position(pat).is_some()
634    }
635
636    fn position(&self, pat: impl Bytes) -> Option<usize> {
637        let string = self.as_bytes();
638        let pat = pat.as_byte_slice();
639        let pat_len = pat.len();
640
641        string.windows(pat_len)
642            .position(|chars| chars == pat)
643    }
644
645    fn escape_double_quote(&self) -> OsString {
646        let quote = "\"";
647        let quote_escaped = "\\\"";
648
649        self.replace(quote, quote_escaped)
650    }
651
652    fn escape_single_quote(&self) -> OsString {
653        let single_quote = "'";
654        let single_quote_escaped = "'\\''";
655
656        self.replace(single_quote, single_quote_escaped)
657    }
658}
659
660
661
662// These traits are used on Windows instead of OsStrExt/OsStringExt
663// from the stdlib. They provide the same functionality as on *nix
664// systems by using a combination of os_str_bytes' functionality and
665// some use of transmutation to borrow the byte slice of OsStr and to
666// turn a byte slice into an &OsStr. Strings are always checked before
667// transmutation and invalid strings will cause a panic.
668
669
670#[cfg(target_os = "windows")]
671trait WinOsString {
672    fn from_vec(s: Vec<u8>) -> OsString;
673    fn into_vec(self) -> Vec<u8>;
674}
675
676#[cfg(target_os = "windows")]
677impl WinOsString for OsString {
678    fn from_vec(s: Vec<u8>) -> OsString {
679        use os_str_bytes::OsStringBytes;
680        // Verify the string. Panics if it's not valid WTF8.
681        OsStringBytes::from_vec(s.clone())
682            .expect("INVALID STRING: Tried to convert invalid WTF8 to OsString!")
683    }
684
685    fn into_vec(self) -> Vec<u8> {
686        use os_str_bytes::OsStringBytes;
687        OsStringBytes::into_vec(self)
688    }
689}
690
691#[cfg(target_os = "windows")]
692#[doc(cfg(windows))]
693#[doc(cfg(features = "windows"))]
694#[cfg(target_os = "windows")]
695#[cfg(features = "windows")]
696/// This uses unsafe transmutation. However, the lifetimes of passed
697/// in values are respected and all byte slices are checked before
698/// being turned into an `OsStr`. For example, this fails to compile:
699///
700/// ```compile_fail
701/// use std::ffi::{OsString, OsStr};
702/// use crate::osstrtools::{Bytes, WinOsStringExt, WinOsStrExt};
703/// fn test_lifetime() {
704///     let s: &[u8] = {
705///         let string = OsString::new();
706///         test_lifetime1(string)
707///     };
708///
709///     s.len();
710/// }
711///
712/// fn test_lifetime1(s: OsString) -> &'static [u8] {
713///     let byte_vec = s.into_vec();
714///     let byte_slice = byte_vec.as_byte_slice();
715///     let osstr = OsStr::from_bytes(byte_slice);
716///
717///     let bytes = osstr.as_bytes();
718///     bytes
719/// }
720/// ```
721///
722/// Similarly, this will panic (on Windows):
723///
724/// ```should_panic
725/// #[cfg(target_os = "windows")]
726/// #[cfg(features = "windows")]
727/// fn will_panic() {
728///     use std::ffi::{OsString, OsStr};
729///     use crate::osstrtools::{Bytes, WinOsStringExt, WinOsStrExt};
730///     let bytes: &[u8] = b"\xF1foo\xF1\x80bar\xF1\x80\x80baz";
731///     OsStr::from_bytes(bytes);
732/// }
733/// ```
734trait WinOsStr {
735    fn from_bytes(bytes: &[u8]) -> &OsStr;
736    fn as_bytes(&self) -> &[u8];
737}
738
739#[cfg(target_os = "windows")]
740impl WinOsStr for OsStr {
741    fn from_bytes(bytes: &[u8]) -> &OsStr {
742        use os_str_bytes::OsStrBytes;
743
744        // Verify the string. Panics if it's not valid WTF8.
745        let _str: std::borrow::Cow<'_, OsStr> = OsStrBytes::from_bytes(bytes)
746            .expect("INVALID STRING: Tried to convert invalid WTF8 to OsStr!");
747
748        // Since it's a valid OsStr this should be fine...
749        unsafe { (bytes as *const _).cast() }
750    }
751
752    fn as_bytes<'s>(&'s self) -> &'s [u8] {
753        // This should be fine in any case, as OsStr is just a &[u8]
754        unsafe { (bytes as *const _).cast() }
755    }
756}
757
758
759
760#[test]
761fn testsplit() {
762    let s = OsStr::new("a,d,g");
763    assert_eq!(s.split(","), vec!["a", "d", "g"]);
764    let s = OsStr::new("abcd defe geh");
765    assert_eq!(s.split(" "), vec!["abcd", "defe", "geh"]);
766    let s = OsStr::new("abcd $s");
767    assert_eq!(s.split("$s"), vec!["abcd ", ""]);
768    let s = OsStr::new("abcd $s$s");
769    assert_eq!(s.split("$s"), vec!["abcd ", "", ""]);
770    let s = OsStr::new("");
771    assert_eq!(s.split("$s"), Vec::<&OsStr>::new());
772    let s = OsStr::new("$s");
773    assert_eq!(s.split(",,"), &["$s"]);
774
775}
776
777#[test]
778fn testreplace() {
779    let s = OsStr::new("a,d,g");
780    assert_eq!(s.replace(",", "WUT"), "aWUTdWUTg");
781    let s = OsStr::new("a,d,g");
782    assert_eq!(s.replace(".", "WUT"), "a,d,g");
783    let s = OsStr::new("a,,d,,g");
784    assert_eq!(s.replace(",,", "WUT"), "aWUTdWUTg");
785    let s = OsStr::new("");
786    assert_eq!(s.replace(",,", "WUT"), "");
787    let s = OsStr::new("$s");
788    assert_eq!(s.replace(",,", "WUT"), "$s");
789    let s = OsString::from("a,d,g");
790    assert_eq!(s.replace(",", "WUT"), "aWUTdWUTg");
791    let s = OsString::from("a,d,g");
792    assert_eq!(s.replace(".", "WUT"), "a,d,g");
793    let s = OsString::from("a,,d,,g");
794    assert_eq!(s.replace(",,", "WUT"), "aWUTdWUTg");
795    let s = OsString::from("");
796    assert_eq!(s.replace(",,", "WUT"), "");
797    let s = OsString::from("$s");
798    assert_eq!(s.replace(",,", "WUT"), "$s");
799}
800
801#[test]
802fn testtrimstart() {
803    let s = OsStr::new(",,,abcd,defe,geh,,,,");
804    assert_eq!(s.trim_start(","), "abcd,defe,geh,,,,");
805    let s = OsString::from(",,,abcd,defe,geh");
806    assert_eq!(s.trim_start(","), "abcd,defe,geh");
807}
808
809#[test]
810fn testtrimend() {
811    let s = OsStr::new(",,,abcd,defe,geh,,,,");
812    assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
813    let s = OsStr::new(",,,abcd,defe,geh,,,,");
814    assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
815    let s = OsStr::new(",,,abcd");
816    assert_eq!(s.trim_end(","), ",,,abcd");
817    let s = OsStr::new(",,,abcd,,");
818    assert_eq!(s.trim_end(","), ",,,abcd");
819    let s = OsStr::new(",,,");
820    assert_eq!(s.trim_end(","), "");
821    let s = OsStr::new(",,,");
822    assert_eq!(s.trim_end(",,"), ",");
823    let s = OsStr::new(",,,");
824    assert_eq!(s.trim_end(",,,"), "");
825    let s = OsStr::new("");
826    assert_eq!(s.trim_end(",,,"), "");
827
828    let s = OsString::from(",,,abcd,defe,geh,,,,");
829    assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
830    let s = OsString::from(",,,abcd,defe,geh,,,,");
831    assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
832    let s = OsString::from(",,,abcd");
833    assert_eq!(s.trim_end(","), ",,,abcd");
834    let s = OsString::from(",,,abcd,,");
835    assert_eq!(s.trim_end(","), ",,,abcd");
836    let s = OsString::from(",,,");
837    assert_eq!(s.trim_end(","), "");
838    let s = OsString::from(",,,");
839    assert_eq!(s.trim_end(",,"), ",");
840    let s = OsString::from(",,,");
841    assert_eq!(s.trim_end(",,,"), "");
842    let s = OsString::from("");
843    assert_eq!(s.trim_end(",,,"), "");
844}
845
846
847#[test]
848fn testescape() {
849    let s = OsStr::new("te'st");
850    assert_eq!(s.escape_single_quote(), "te'\\''st");
851    let s = OsStr::new("'te'st'");
852    assert_eq!(s.escape_single_quote(), "'\\''te'\\''st'\\''");
853    let s = OsString::from("te'st");
854    assert_eq!(s.escape_single_quote(), "te'\\''st");
855    let s = OsString::from("'te'st'");
856    assert_eq!(s.escape_single_quote(), "'\\''te'\\''st'\\''");
857}
858
859#[test]
860fn test_escape_double() {
861    let s = OsStr::new("te\"st");
862    assert_eq!(s.escape_double_quote(), "te\\\"st");
863    let s = OsStr::new("\"te\"st\"");
864    assert_eq!(s.escape_double_quote(), "\\\"te\\\"st\\\"");
865    let s = OsString::from("te\"st");
866    assert_eq!(s.escape_double_quote(), "te\\\"st");
867    let s = OsString::from("\"te\"st\"");
868    assert_eq!(s.escape_double_quote(), "\\\"te\\\"st\\\"");
869}
870
871#[test]
872fn testsplice() {
873    let s = OsStr::new("ls $s");
874    assert_eq!(s.splice("$s", &["TEST"]).assemble_with_sep(" "),
875               "ls TEST");
876    let s = OsStr::new("ls $s");
877    assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep(" "),
878               "ls TEST TEST");
879    let s = OsStr::new("ls $s$s");
880    assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep(" "),
881               "ls TEST TESTTEST TEST");
882    let s = OsStr::new("");
883    assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep(" "), "");
884    let s = OsStr::new("ls $s");
885    assert_eq!(s.splice("$s", &[""]).assemble_with_wrap("'"), "ls ''");
886    let s = OsStr::new("ls $s");
887    assert_eq!(s.splice("$s", &["TEST"]).assemble_with_wrap("'"),
888               "ls 'TEST'");
889    let s = OsStr::new("ls $s");
890    assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep_and_wrap(" ", "'"),
891               "ls 'TEST' 'TEST'");
892    let s = OsStr::new("ls $s$s");
893    assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep_and_wrap(" ", "'"),
894               "ls 'TEST' 'TEST''TEST' 'TEST'");
895}
896
897#[test]
898fn testconcat() {
899    let s = &[OsStr::new("1"), OsStr::new("2"), OsStr::new("3")];
900    assert_eq!(s.concat(" "), "1 2 3");
901    assert_eq!(s.concat(""), "123");
902    assert_eq!(s.concat("' '"), "1' '2' '3");
903    let s = &[OsStr::new("")];
904    assert_eq!(s.concat(""), "");
905    assert_eq!(s.concat("' '"), "");
906    let s: &[&OsStr] = &[];
907    assert_eq!(s.concat(""), "");
908}