1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! This crate provides the [StrTools] trait which exposes a variety of helper functions for
//! handling strings for use cases like handling user input.
//!
//! # Examples
//! ```
//! # use strtools::StrTools;
//! # use std::error::Error;
//! # fn main() -> Result<(), Box<dyn Error>> {
//! // split a string by some separator but ignore escaped ones
//! let parts: Vec<_> = r"this string\ is split by\ spaces unless they are\ escaped"
//!     .split_non_escaped('\\', &[' '])?
//!     .collect();
//!
//! assert_eq!(
//!     parts,
//!     [
//!         "this",
//!         "string is",
//!         "split",
//!         "by spaces",
//!         "unless",
//!         "they",
//!         "are escaped"
//!     ]
//! );
//! # Ok(())
//! # }
//! ```
#![feature(cow_is_borrowed, let_chains)]
#![deny(missing_docs, clippy::missing_panics_doc)]

use escape::SplitNonEscaped;

/// Contains functions and types related to escaping strings.
pub mod escape;

mod sealed {
    pub trait Sealed {}
    impl Sealed for str {}
}

/// The main trait of this crate, providing various extension methods for [str].
/// See the individual function documentation for more info.
///
/// [crate_doc]: crate
pub trait StrTools: sealed::Sealed {
    /// Splits a [str] by the given delimiters unless they are precided by an escape.
    /// Escapes before significant chars are removed, significant chars are the delimters and the
    /// escape itself. Trailing escapes are ignored as if followed by a non-significant char.
    ///
    /// # Errors
    /// Returns an Error if `delims` contains `esc`
    ///
    /// # Complexity & Allocation
    /// This algorithm requires `O(n * m)` time where `n` is the length of the input string and `m`
    /// is the length of `delims` to split the full string. If no escapes are encountered in a
    /// part, no allocations are done and the part is borrowed, otherwise a [String] and all but
    /// the escape char are copied over.
    ///
    /// # Examples
    /// ```
    /// # use strtools::StrTools;
    /// # use std::error::Error;
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let value = r"Pa\rt0:Part1:Part2\:StillPart2";
    /// let parts: Vec<_> = value.split_non_escaped('\\', &[':'])?.collect();
    ///
    /// // notice that the escape char was removed in Part2 but not in Part1 as it's just used as
    /// // an indicator for escaping the delimiters or escapes themselves
    /// assert_eq!(parts, [r"Pa\rt0", "Part1", "Part2:StillPart2"]);
    /// # Ok(())
    /// # }
    /// ```
    fn split_non_escaped<'d>(
        &self,
        esc: char,
        delims: &'d [char],
    ) -> Result<SplitNonEscaped<'_, 'd>, escape::EscapeIsDelimiterError>;
}

impl StrTools for str {
    fn split_non_escaped<'d>(
        &self,
        esc: char,
        delims: &'d [char],
    ) -> Result<SplitNonEscaped<'_, 'd>, escape::EscapeIsDelimiterError> {
        SplitNonEscaped::new(self, esc, delims)
    }
}