use crate::{
iter::{IntoIterKind, IsIteratorKind},
string::{self, str_from, str_up_to},
};
use konst_macro_rules::iterator_shared;
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
pub const fn split_once<'a>(this: &'a str, delim: &str) -> Option<(&'a str, &'a str)> {
if delim.is_empty() {
Some(string::split_at(this, 0))
} else {
crate::option::map! {
string::find(this, delim, 0),
|pos| (str_up_to(this, pos), str_from(this, pos + delim.len()))
}
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
pub const fn rsplit_once<'a>(this: &'a str, delim: &str) -> Option<(&'a str, &'a str)> {
if delim.is_empty() {
Some(string::split_at(this, this.len()))
} else {
crate::option::map! {
string::rfind(this, delim, this.len()),
|pos| (str_up_to(this, pos), str_from(this, pos + delim.len()))
}
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
pub const fn split<'a, 'b>(this: &'a str, delim: &'b str) -> Split<'a, 'b> {
Split {
this,
state: if delim.is_empty() {
State::Empty(EmptyState::Start)
} else {
State::Normal { delim }
},
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
pub const fn rsplit<'a, 'b>(this: &'a str, delim: &'b str) -> RSplit<'a, 'b> {
split(this, delim).rev()
}
#[derive(Copy, Clone)]
enum State<'a> {
Normal { delim: &'a str },
Empty(EmptyState),
Finished,
}
#[derive(Copy, Clone)]
enum EmptyState {
Start,
Continue,
}
macro_rules! split_shared {
(is_forward = $is_forward:ident) => {
const fn next_from_empty(mut self, es: EmptyState) -> Option<(&'a str, Self)> {
match es {
EmptyState::Start => {
self.state = State::Empty(EmptyState::Continue);
Some(("", self))
}
EmptyState::Continue => {
let this = self.this;
if this.is_empty() {
self.state = State::Finished;
}
let next_char = string::find_next_char_boundary(this.as_bytes(), 0);
let (next_char, rem) = string::split_at(this, next_char);
self.this = rem;
Some((next_char, self))
}
}
}
const fn next_back_from_empty(mut self, es: EmptyState) -> Option<(&'a str, Self)> {
match es {
EmptyState::Start => {
self.state = State::Empty(EmptyState::Continue);
Some(("", self))
}
EmptyState::Continue => {
let this = self.this;
if self.this.is_empty() {
self.state = State::Finished;
}
let next_char = string::find_prev_char_boundary(this.as_bytes(), this.len());
let (rem, next_char) = string::split_at(this, next_char);
self.this = rem;
Some((next_char, self))
}
}
}
iterator_shared! {
is_forward = $is_forward,
item = &'a str,
iter_forward = Split<'a, 'b>,
iter_reversed = RSplit<'a, 'b>,
next(self){
let Self {
this,
state,
} = self;
match state {
State::Normal{delim} => {
match string::find(this, delim, 0) {
Some(pos) => {
self.this = str_from(this, pos + delim.len());
Some((str_up_to(this, pos), self))
}
None => {
self.this = "";
self.state = State::Finished;
Some((this, self))
}
}
}
State::Empty(es) => self.next_from_empty(es),
State::Finished => None,
}
},
next_back{
let Self {
this,
state,
} = self;
match state {
State::Normal{delim} => {
match string::rfind(this, delim, this.len()) {
Some(pos) => {
self.this = str_up_to(this, pos);
Some((str_from(this, pos + delim.len()), self))
}
None => {
self.this = "";
self.state = State::Finished;
Some((this, self))
}
}
}
State::Empty(es) => self.next_back_from_empty(es),
State::Finished => None,
}
},
fields = {this, state},
}
};
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
pub struct Split<'a, 'b> {
this: &'a str,
state: State<'b>,
}
impl IntoIterKind for Split<'_, '_> {
type Kind = IsIteratorKind;
}
impl<'a, 'b> Split<'a, 'b> {
split_shared! {is_forward = true}
pub const fn remainder(&self) -> &'a str {
self.this
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
pub struct RSplit<'a, 'b> {
this: &'a str,
state: State<'b>,
}
impl IntoIterKind for RSplit<'_, '_> {
type Kind = IsIteratorKind;
}
impl<'a, 'b> RSplit<'a, 'b> {
split_shared! {is_forward = false}
pub const fn remainder(&self) -> &'a str {
self.this
}
}