use core::{fmt, hash, marker::PhantomData};
use constr::Constr;
use crate::traits::{Stringable, Stringy};
pub struct Spair<S: AsRef<str>, Sep: Constr = constr::Empty, const SPLIT_SEP: bool = false> {
buf: S,
split: usize,
phantom: PhantomData<Sep>,
}
impl<S: Copy + AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> Copy for Spair<S, Sep, SPLIT_SEP> {}
impl<S: Clone + AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> Clone for Spair<S, Sep, SPLIT_SEP> {
#[cfg_attr(any(coverage_nightly, feature = "nightly"), coverage(off))]
fn clone(&self) -> Self {
Spair {
buf: self.buf.clone(),
split: self.split,
phantom: PhantomData,
}
}
}
impl<S1: AsRef<str>, S2: AsRef<str>, Sep: Constr, const SPLIT_SEP: bool>
PartialEq<Spair<S2, Sep, SPLIT_SEP>> for Spair<S1, Sep, SPLIT_SEP>
{
#[cfg_attr(any(coverage_nightly, feature = "nightly"), coverage(off))]
fn eq(&self, other: &Spair<S2, Sep, SPLIT_SEP>) -> bool {
self.pair() == other.pair()
}
}
impl<S: AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> Eq for Spair<S, Sep, SPLIT_SEP> {}
impl<S: AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> hash::Hash for Spair<S, Sep, SPLIT_SEP> {
#[cfg_attr(any(coverage_nightly, feature = "nightly"), coverage(off))]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.pair().hash(state);
}
}
impl<S: AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> fmt::Display for Spair<S, Sep, SPLIT_SEP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(self.full())
}
}
impl<S: AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> fmt::Debug for Spair<S, Sep, SPLIT_SEP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Spair")
.field(&self.first())
.field(&self.second())
.finish()
}
}
impl<S: Stringable, Sep: Constr, const SPLIT_SEP: bool> Spair<S, Sep, SPLIT_SEP> {
pub fn new<S1: AsRef<str>, S2: AsRef<str>>(
first: S1,
second: S2,
) -> Result<Spair<S, Sep, SPLIT_SEP>, <S::Builder as Stringy>::Error> {
let first = first.as_ref();
let second = second.as_ref();
let buf = S::from_many(&[first, Sep::STR, second])?;
let split = first.len() + if SPLIT_SEP { Sep::STR.len() } else { 0 };
Ok(Spair {
buf,
split,
phantom: PhantomData,
})
}
}
impl<S: AsRef<str>, Sep: Constr, const SPLIT_SEP: bool> Spair<S, Sep, SPLIT_SEP> {
pub unsafe fn split_unchecked(buf: S, split: usize) -> Spair<S, Sep, SPLIT_SEP> {
debug_assert_eq!(
buf.as_ref().get(if SPLIT_SEP {
(split - Sep::STR.len())..split
} else {
split..(split + Sep::STR.len())
}),
Some(Sep::STR),
"split_unchecked({buf:?}, {split:?}) precondition failed",
buf = buf.as_ref(),
);
Spair {
buf,
split,
phantom: PhantomData,
}
}
pub fn split(buf: S) -> Option<Spair<S, Sep, SPLIT_SEP>> {
let split = buf.as_ref().find(Sep::STR)? + if SPLIT_SEP { Sep::STR.len() } else { 0 };
Some(Spair {
buf,
split,
phantom: PhantomData,
})
}
pub fn rsplit(buf: S) -> Option<Spair<S, Sep, SPLIT_SEP>> {
let split = buf.as_ref().rfind(Sep::STR)? + if SPLIT_SEP { Sep::STR.len() } else { 0 };
Some(Spair {
buf,
split,
phantom: PhantomData,
})
}
fn split_before(&self) -> usize {
self.split - if SPLIT_SEP { Sep::STR.len() } else { 0 }
}
fn split_after(&self) -> usize {
self.split + if SPLIT_SEP { 0 } else { Sep::STR.len() }
}
pub fn first(&self) -> &str {
unsafe { self.buf.as_ref().get_unchecked(..self.split_before()) }
}
pub fn second(&self) -> &str {
unsafe { self.buf.as_ref().get_unchecked(self.split_after()..) }
}
pub fn pair(&self) -> (&str, &str) {
(self.first(), self.second())
}
pub fn full(&self) -> &str {
self.buf.as_ref()
}
}