sub-strs 0.29.2

For finding sub strings...
Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

sub-strs

Copyright (C) 2019-2023  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2023".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # `SubStrIter`

use {
    alloc::string::String,
    core::ops::{Deref, Range},
};

/// # A sub string
///
/// This struct is used by [`SubStrIter`][::SubStrIter], which can be made via [`sub_strs()`][::sub_strs()].
///
/// You can dereference this struct to `&str`. It can also give you start and end indexes of the original string.
///
/// [::SubStrIter]: struct.SubStrIter.html
/// [::sub_strs()]: fn.sub_strs.html
#[derive(Debug)]
pub struct SubStr<'a> {
    target: &'a str,

    start: usize,
    end: usize,

    inner: &'a str,
    inner_start: usize,
    inner_end: usize,
}

impl<'a> SubStr<'a> {

    /// # Start index within original string (inclusive)
    pub fn start(&self) -> usize {
        self.start
    }

    /// # End index within original string (exclusive)
    pub fn end(&self) -> usize {
        self.end
    }

    /// # Range within original string
    pub fn range(&self) -> Range<usize> {
        self.start..self.end
    }

    /// # Inner string between start and end phrases
    ///
    /// # Examples
    ///
    /// ```
    /// use core::str::FromStr;
    ///
    /// let sample = "some_id: 99,";
    /// let some_id = sub_strs::sub_strs(sample, "some_id:", ",").next().unwrap();
    /// assert_eq!(u8::from_str(some_id.inner().trim()).unwrap(), 99);
    /// ```
    pub fn inner(&self) -> &'a str {
        self.inner
    }

    /// # Start index of the inner string within original string (inclusive)
    pub fn inner_start(&self) -> usize {
        self.inner_start
    }

    /// # End index of the inner string within original string (exclusive)
    pub fn inner_end(&self) -> usize {
        self.inner_end
    }

    /// # Range of the inner string within original string
    pub fn inner_range(&self) -> Range<usize> {
        self.inner_start..self.inner_end
    }

}

impl Deref for SubStr<'_> {

    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.target
    }

}

impl PartialEq<str> for SubStr<'_> {

    fn eq(&self, other: &str) -> bool {
        self.target == other
    }

}

impl PartialEq<SubStr<'_>> for &str {

    fn eq(&self, other: &SubStr) -> bool {
        self == &other.target
    }

}

impl PartialEq<String> for SubStr<'_> {

    fn eq(&self, other: &String) -> bool {
        self.target == other
    }

}

impl PartialEq<SubStr<'_>> for String {

    fn eq(&self, other: &SubStr) -> bool {
        self == other.target
    }

}

/// # An iterator of [`SubStr`][::SubStr]
///
/// You can make an instance of this struct by [`sub_strs()`][::sub_strs()].
///
/// [::SubStr]: struct.SubStr.html
/// [::sub_strs()]: fn.sub_strs.html
#[derive(Debug)]
pub struct SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
    src: &'a str,
    src_index: usize,
    start: S,
    end: T,
}

impl<'a, S, T> SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {

    /// # Makes new instance
    fn new(source: &'a str, start: S, end: T) -> Self {
        Self {
            src: source,
            src_index: 0,
            start,
            end,
        }
    }

}

impl<'a, S, T> Iterator for SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {

    type Item = SubStr<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        let start = self.start.as_ref();
        let start_len = start.len();

        let src = &self.src[self.src_index..];
        if let Some(mut start_idx) = src.find(start) {
            let end = self.end.as_ref();
            let delta = start_idx + start_len;
            if let Some(end_idx) = src[delta..].find(end) {
                if start_len > 0 && end_idx > 0 {
                    if let Some(next_char_bytes) = src[start_idx..].chars().next().map(|c| c.len_utf8()) {
                        if let Some(next_start_idx) = src[start_idx + next_char_bytes .. delta + end_idx].rfind(start) {
                            start_idx += next_char_bytes + next_start_idx;
                        }
                    }
                }

                let end_len = end.len();
                let end_idx = delta + end_idx + end_len;
                let sub_start = self.src_index + start_idx;
                self.src_index += end_idx;

                let target = &src[start_idx..end_idx];
                let inner = &target[start_len .. target.len() - end_len];

                return {
                    let start_idx = sub_start;
                    let end_idx = self.src_index;
                    let inner_start = start_idx + start.len();
                    let inner_end = end_idx - end.len();
                    Some(SubStr { target, start: start_idx, end: end_idx, inner, inner_start, inner_end })
                };
            }
        }

        None
    }

}

/// # Finds sub strings
///
/// ## Examples
///
/// ```
/// use core::str::FromStr;
///
/// let sample = "test(some(test(0)     test(1)---test(2) some test(3)";
/// let start = "test(";
/// let end = ")";
/// for (i, s) in sub_strs::sub_strs(sample, start, end).enumerate() {
///     let expected = format!("{}{}{}", start, i, end);
///     assert_eq!(s, expected);
///     assert_eq!(&sample[s.start()..s.end()], expected);
///     assert_eq!(usize::from_str(s.inner()).unwrap(), i);
/// }
/// ```
pub fn sub_strs<'a, S, T>(source: &'a str, start: S, end: T) -> SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
    SubStrIter::new(source, start, end)
}