#![allow(clippy::module_name_repetitions)]
use core::{str, iter::Map, cmp::Ordering, hash::{Hash, Hasher}};
use crate::{SourceIterItem, SourcePosition};
use crate::parser::AllocError;
pub mod iter;
pub mod premade {
mod datum_list;
pub use datum_list::TextDatumList;
}
pub trait TextBase
where Self: Sized,
{
type Pos: SourcePosition;
fn empty() -> Self;
fn is_empty(&self) -> bool;
}
pub mod chunk {
use crate::SourceIterItem;
use super::TextChunk;
pub mod premade {
mod pos_str;
pub use pos_str::*;
}
pub trait SourceStream<C>: Iterator<Item = SourceIterItem<C::Pos>>
where C: TextChunk,
{
fn peek(&mut self) -> Option<&<Self as Iterator>::Item>;
fn next_accum(&mut self) -> Option<<Self as Iterator>::Item>;
fn accum_done(&mut self) -> C;
}
}
pub trait TextChunk: TextBase {
type CharsSrcStrm: chunk::SourceStream<Self>;
fn src_strm(&self) -> Self::CharsSrcStrm;
}
#[inline]
fn sii_ch<P>(SourceIterItem{ch, ..}: SourceIterItem<P>) -> char {
ch
}
pub type Chars<'text, TextType> =
Map<iter::Iter<'text, TextType>,
fn(SourceIterItem<<TextType as TextBase>::Pos>) -> char>;
pub trait Text: TextBase
where Self: From<<Self as Text>::Chunk>,
{
type Chunk: TextChunk<Pos = Self::Pos>;
type IterChunksState: iter::chunks::State<Chunk = Self::Chunk> + ?Sized;
#[inline]
fn from_chunkish<T>(v: T) -> Self
where T: Into<Self::Chunk>
{
Self::from(v.into())
}
#[inline]
fn from_str<'s>(s: &'s str) -> Self
where Self::Chunk: From<&'s str>
{
Self::from_chunkish(s)
}
fn eq<O: Text>(&self, other: &O) -> bool {
self.iter().map(sii_ch).eq(other.iter().map(sii_ch))
}
fn cmp<O: Text>(&self, other: &O) -> Ordering {
self.iter().map(sii_ch).cmp(other.iter().map(sii_ch))
}
fn hash<H: Hasher>(&self, state: &mut H) {
for ch in self.iter().map(sii_ch) {
ch.hash(state);
}
}
#[inline]
fn chars(&self) -> Chars<'_, Self> {
self.iter().map(sii_ch)
}
fn encode_utf8<'b>(&self, buf: &'b mut [u8]) -> Result<&'b str, &'b str> {
let mut pos = 0;
macro_rules! as_str {
() => { str::from_utf8(&buf[..pos]).unwrap() }
}
for ch in self.iter().map(sii_ch) {
if pos + ch.len_utf8() <= buf.len() {
let s = ch.encode_utf8(&mut buf[pos..]);
pos += s.len();
} else {
return Err(as_str!())
}
}
Ok(as_str!())
}
fn iter_chunks_state(&self) -> Option<&Self::IterChunksState>;
#[inline]
fn iter_chunks(&self) -> iter::chunks::Iter<'_, Self> {
iter::chunks::Iter::new(self)
}
#[inline]
fn iter(&self) -> iter::Iter<'_, Self> {
iter::Iter::new(self)
}
}
pub trait TextConcat<DA>: Text {
fn concat(self, other: Self, datum_alloc: &mut DA) -> Result<Self, AllocError>;
}
#[cfg(test)]
mod tests {
use super::{*, premade::TextDatumList, chunk::premade::PosStr};
type TT<'d> = TextDatumList<'d, PosStr<'static>, ()>;
#[test]
fn encode_utf8() {
assert_eq!(TT::from_str("").encode_utf8(&mut []), Ok(""));
assert_eq!(TT::from_str("").encode_utf8(&mut [0; 1]), Ok(""));
assert_eq!(TT::from_str("").encode_utf8(&mut [0; 123]), Ok(""));
assert_eq!(TT::from_str("a").encode_utf8(&mut [0; 1]), Ok("a"));
assert_eq!(TT::from_str("a").encode_utf8(&mut [0; 2]), Ok("a"));
assert_eq!(TT::from_str("a").encode_utf8(&mut [0; 3]), Ok("a"));
assert_eq!(TT::from_str("a").encode_utf8(&mut []), Err(""));
assert_eq!(TT::from_str("raboof").encode_utf8(&mut [0; 6]), Ok("raboof"));
assert_eq!(TT::from_str("raboof").encode_utf8(&mut [0; 512]), Ok("raboof"));
assert_eq!(TT::from_str("raboof").encode_utf8(&mut [0; 5]), Err("raboo"));
assert_eq!(TT::from_str("raboof").encode_utf8(&mut [0; 2]), Err("ra"));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 6]), Ok("▷ λ"));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 8]), Ok("▷ λ"));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 99]), Ok("▷ λ"));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 5]), Err("▷ "));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 4]), Err("▷ "));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 3]), Err("▷"));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 2]), Err(""));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut [0; 1]), Err(""));
assert_eq!(TT::from_str("▷ λ").encode_utf8(&mut []), Err(""));
}
}