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)
}
}