use std::marker::PhantomData;
use crate::{
Buffer,
Conciliator,
Inline,
Pushable
};
use crate::style::{
Color,
Paint,
TAGS
};
use super::{
Formatter,
PushableFmt,
WrapFmt,
Print
};
pub struct List<H, I, F, P> {
header: H,
iter: I,
formatter: F,
prefixer: P
}
pub trait Prefixer {
fn prefix(&mut self, buf: &mut Buffer);
}
pub struct NoPrefix;
pub struct ArrowPrefix;
pub struct Indexer(usize);
pub struct NoHeader;
pub struct PushableHdr<M, T>(PhantomData<M>, Header<T>);
enum Header<T> {
Pushable(T),
WithCount(usize, T)
}
impl<I, T, MT, H, MH> List<PushableHdr<MH, H>, I, PushableFmt<MT>, ArrowPrefix>
where I: ExactSizeIterator<Item = T>
{
pub fn new(header: H, iter: I) -> Self {
Self {
header: PushableHdr::with_count(iter.len(), header),
iter,
formatter: PushableFmt(PhantomData),
prefixer: ArrowPrefix
}
}
}
impl<I, MT, H, MH> List<PushableHdr<MH, H>, I, PushableFmt<MT>, ArrowPrefix> {
pub fn uncounted(header: H, iter: I) -> Self {
Self {
header: PushableHdr::new(header),
iter,
formatter: PushableFmt(PhantomData),
prefixer: ArrowPrefix
}
}
}
impl<I, M> List<NoHeader, I, PushableFmt<M>, ArrowPrefix> {
pub fn headless(iter: I) -> Self {
Self {
header: NoHeader,
iter,
formatter: PushableFmt(PhantomData),
prefixer: ArrowPrefix
}
}
}
impl<I, M> List<NoHeader, I, PushableFmt<M>, Indexer> {
pub fn indexed(iter: I) -> Self {
Self {
header: NoHeader,
iter,
formatter: PushableFmt(PhantomData),
prefixer: Indexer(1)
}
}
}
impl<H, I, P> List<H, I, PushableFmt<()>, P> {
pub fn with_formatter<F>(self, formatter: F) -> List<H, I, F, P> {
let Self {header, iter, prefixer, ..} = self;
List {header, iter, formatter, prefixer}
}
}
impl<H, I: Iterator<Item = T>, T, P> List<H, I, PushableFmt<()>, P> {
pub fn with_wrap<F, W>(self, wrap_fn: F) -> List<H, I, WrapFmt<F>, P>
where F: FnMut(T) -> W, W: Inline
{
let Self {header, iter, prefixer, ..} = self;
let formatter = WrapFmt(wrap_fn);
List {header, iter, formatter, prefixer}
}
}
impl<I, F, P> List<NoHeader, I, F, P> {
pub fn with_header<M, T>(self, header: T)
-> List<PushableHdr<M, T>, I, F, P>
{
let Self {iter, prefixer, formatter, ..} = self;
let header = PushableHdr::new(header);
List {header, iter, formatter, prefixer}
}
}
impl<I: ExactSizeIterator<Item = T>, T, F, P> List<NoHeader, I, F, P> {
pub fn with_count<M, H>(self, header: H)
-> List<PushableHdr<M, H>, I, F, P>
{
let Self {iter, prefixer, formatter, ..} = self;
let header = PushableHdr::with_count(iter.len(), header);
List {header, iter, formatter, prefixer}
}
}
impl<H, I: ExactSizeIterator<Item = T>, T, F, P> List<H, I, F, P> {
pub(crate) fn len(&self) -> usize {
self.iter.len()
}
}
impl<H, I, T, P, F> List<H, I, F, P>
where
H: Print,
I: Iterator<Item = T>,
P: Prefixer,
F: Formatter<T>
{
#[must_use]
pub fn print_and_count<C>(mut self, con: &C) -> usize
where C: Conciliator + ?Sized
{
match self.iter.next() {
Some(item) => {
let mut count = 1;
self.header.print(con);
let mut line = con.get_line();
self.prefixer.prefix(&mut line);
self.formatter.format(&mut line, item);
drop(line);
for item in self.iter {
let mut line = con.get_line();
self.prefixer.prefix(&mut line);
self.formatter.format(&mut line, item);
count += 1;
}
count
},
None => 0
}
}
pub fn print_to<C: Conciliator + ?Sized>(self, con: &C) {
let _ = self.print_and_count(con);
}
}
impl<H, I, T, P, F> Print for List<H, I, F, P>
where
H: Print,
I: Iterator<Item = T>,
P: Prefixer,
F: Formatter<T>
{
fn print<C: Conciliator + ?Sized>(self, con: &C) {
self.print_to(con);
}
}
impl Prefixer for NoPrefix {
fn prefix(&mut self, _buf: &mut Buffer) {}
}
impl Prefixer for ArrowPrefix {
fn prefix(&mut self, buf: &mut Buffer) {
buf.push_bold("\t→ ");
}
}
impl Prefixer for Indexer {
fn prefix(&mut self, buf: &mut Buffer) {
buf
.push_zeta(&format_args!("\t[{}]", self.0))
.push_plain(" - ");
self.0 += 1;
}
}
impl<M, T> PushableHdr<M, T> {
pub(crate) fn new(thing: T) -> Self {
Self(PhantomData, Header::Pushable(thing))
}
pub(crate) fn with_count(count: usize, thing: T) -> Self {
Self(PhantomData, Header::WithCount(count, thing))
}
}
impl Print for NoHeader {
fn print<C: Conciliator + ?Sized>(self, _con: &C) {}
}
impl<HM, HT: Pushable<HM>> Print for PushableHdr<HM, HT> {
fn print<C: Conciliator + ?Sized>(self, con: &C) {
let mut line = con.get_line();
line.tag(Color::Alpha, TAGS.status);
match self.1 {
Header::Pushable(header) => line
.push::<HM, _>(header),
Header::WithCount(count, header) => line
.push(count)
.push(' ')
.push::<HM, _>(header),
};
line.push(':');
}
}
#[test]
fn print_nothing() {
struct DontPush;
impl Inline for DontPush {
fn inline(&self, _buffer: &mut Buffer) {panic!("Don't push!")}
}
let con = crate::init();
let iter = std::iter::empty::<DontPush>;
List::new(DontPush, iter()).print_to(&con);
let zero = List::new(DontPush, iter()).print_and_count(&con);
assert_eq!(zero, 0);
}