mudrs-milk 0.0.1

WIP Mud Client
Documentation
use std::{
    borrow::Cow,
    convert::TryFrom,
    mem,
    ops::{
        Deref,
        DerefMut,
    },
};

use thiserror::Error;
use tui::{
    style::Style,
    text::{
        Span,
        Spans,
    },
};

#[derive(Clone, Debug, Default)]
pub struct Text(Spans<'static>);

#[derive(Clone, Debug)]
pub struct Line(Text);

#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
#[error("string contains a newline")]
pub struct NewlineError;

impl Text {
    pub fn with_style(style: Style) -> Text {
        let mut text = Text::new();
        text.new_span(style);
        text
    }

    pub fn new() -> Text {
        Text::default()
    }

    pub fn push_str(&mut self, s: impl AsRef<str>) -> Result<(), NewlineError> {
        no_newlines(&s)?;
        self.current_span_mut()
            .content
            .to_mut()
            .push_str(s.as_ref());
        Ok(())
    }

    pub fn push_string(&mut self, s: String) -> Result<(), NewlineError> {
        let current = self.current_span_mut();
        if current.content.len() == 0 {
            no_newlines(&s)?;
            current.content = Cow::Owned(s);
            return Ok(());
        }
        self.push_str(s)
    }

    pub fn current_span_mut(&mut self) -> &mut Span<'static> {
        if self.spans().len() == 0 {
            self.new_span(Default::default());
        }
        self.spans_mut().last_mut().unwrap()
    }

    pub fn set_style(&mut self, style: Style) {
        let current = self.current_span_mut();
        if current.content.is_empty() {
            current.style = style;
        } else {
            self.new_span(style);
        }
    }

    fn spans_mut(&mut self) -> &mut Vec<Span<'static>> {
        &mut self.0 .0
    }

    pub fn new_span(&mut self, style: Style) {
        self.spans_mut().push(Span {
            content: Cow::Owned(String::new()),
            style,
        })
    }

    /// Extend self with more text.
    /// Styles are patched along the way.
    pub fn extend(&mut self, other: &mut Text) {
        let mut style = self.style();
        self.spans_mut()
            .extend(other.spans_mut().drain(..).map(|mut span| {
                span.style = style.patch(span.style);
                style = span.style;
                span
            }));
    }

    pub fn spans(&self) -> &Vec<Span<'static>> {
        &self.0 .0
    }

    pub fn complete(mut self, mut line: Line) -> Line {
        self.extend(&mut *line);
        self.into()
    }

    /// Replace `self` with a new `Text` which picks up where the provided line
    /// appended to the current text leaves off stylistically.
    pub fn swap_complete(&mut self, line: Line) -> Line {
        let text = mem::replace(self, Text::new());
        let line = text.complete(line);
        self.set_style(line.style());
        line
    }

    pub fn empty(&self) -> bool {
        self.spans().len() == 0
    }

    pub fn style(&self) -> Style {
        let spans = self.spans();
        if spans.len() == 0 {
            Style::default()
        } else {
            spans.last().unwrap().style
        }
    }
}

fn no_newlines(s: impl AsRef<str>) -> Result<(), NewlineError> {
    if s.as_ref()
        .as_bytes()
        .iter()
        .any(|c| matches!(c, b'\r' | b'\n'))
    {
        return Err(NewlineError);
    }
    Ok(())
}

impl<'a> TryFrom<&'a str> for Text {
    type Error = NewlineError;

    fn try_from(other: &'a str) -> Result<Self, Self::Error> {
        let mut text = Text::new();
        text.push_str(other)?;
        Ok(text)
    }
}
impl TryFrom<String> for Text {
    type Error = NewlineError;
    fn try_from(other: String) -> Result<Self, Self::Error> {
        let mut text = Text::new();
        text.push_string(other)?;
        Ok(text)
    }
}

impl From<Text> for Line {
    fn from(other: Text) -> Self {
        Line(other)
    }
}

impl Deref for Line {
    type Target = Text;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Line {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl Line {
    pub fn new() -> Self {
        Line(Text::new())
    }
}