acick 0.0.9-alpha.1

Tools for programming contests
Documentation
use std::cmp::max;
use std::fmt;

use getset::{CopyGetters, Getters};
use itertools::{EitherOrBoth, Itertools as _};
use serde::{Deserialize, Serialize};

use crate::model::Compare;

#[derive(Serialize, Deserialize, Getters, CopyGetters, Debug, Clone, PartialEq, Eq, Hash)]
pub struct TextDiff {
    #[get = "pub"]
    l_title: String,
    #[get = "pub"]
    r_title: String,
    #[get_copy = "pub"]
    l_width: usize,
    #[get_copy = "pub"]
    r_width: usize,
    #[get_copy = "pub"]
    is_any: bool,
    left: String,
    right: String,
    cmp: Compare,
}

impl TextDiff {
    pub fn new(
        l_title: impl Into<String>,
        r_title: impl Into<String>,
        left: String,
        right: String,
        cmp: Compare,
    ) -> Self {
        let (l_title, r_title) = (l_title.into(), r_title.into());
        let (l_width, r_width, is_any) = Self::iter_lines(&left, &right).fold(
            (l_title.len(), r_title.len(), false),
            |(l_width, r_width, is_any), line| {
                (
                    max(l_width, line.0.len()),
                    max(r_width, line.1.len()),
                    is_any || !cmp.compare(line.0, line.1),
                )
            },
        );

        Self {
            l_title,
            r_title,
            l_width,
            r_width,
            is_any,
            left,
            right,
            cmp,
        }
    }

    fn iter_lines<'a>(left: &'a str, right: &'a str) -> impl Iterator<Item = (&'a str, &'a str)> {
        let (l_iter, r_iter) = (left.lines(), right.lines());
        l_iter.zip_longest(r_iter).map(|pair| match pair {
            EitherOrBoth::Both(l, r) => (l, r),
            EitherOrBoth::Left(l) => (l, ""),
            EitherOrBoth::Right(r) => ("", r),
        })
    }

    fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
        Self::iter_lines(&self.left, &self.right)
    }
}

impl fmt::Display for TextDiff {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "  | {:l_width$} | {:r_width$} ",
            self.l_title,
            self.r_title,
            l_width = self.l_width,
            r_width = self.r_width
        )?;
        writeln!(
            f,
            "--+-{:-<l_width$}-+-{:-<r_width$}-",
            "",
            "",
            l_width = self.l_width,
            r_width = self.r_width
        )?;
        for line in self.iter() {
            writeln!(
                f,
                "{} | {:l_width$} | {:r_width$} ",
                if self.cmp.compare(line.0, line.1) {
                    " "
                } else {
                    ">"
                },
                line.0,
                line.1,
                l_width = self.l_width,
                r_width = self.r_width
            )?;
        }
        Ok(())
    }
}