mod lex;
mod merge;
mod parse;
mod wrap;
use lex::Lex;
use merge::Merge;
use parse::Parse;
use wrap::{LineWrap, Sauce};
pub use wrap::{Guacamole, Salsa};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Newline {
LF,
CRLF,
}
impl Default for Newline {
fn default() -> Self {
Self::LF
}
}
impl Newline {
fn as_str(&self) -> &'static str {
match self {
Self::LF => "\n",
Self::CRLF => "\r\n",
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Token<'t> {
Space,
Tab,
Newline(Newline),
Word(&'t str),
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Whitespace {
Space(usize),
Tab(usize),
}
impl Whitespace {
fn count(&self) -> usize {
match self {
Self::Space(c) | Self::Tab(c) => *c,
}
}
fn as_str(&self) -> &'static str {
match self {
Self::Space(_) => " ",
Self::Tab(_) => "\t",
}
}
}
#[derive(Debug, Clone, PartialEq)]
struct Line<'t> {
indent: Whitespace,
comment: Option<&'t str>,
padding: Whitespace,
bullet: Option<&'t str>,
words: Vec<&'t str>,
newline: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Toppings {
tabs: usize,
width: usize,
newline: Newline,
}
impl Default for Toppings {
fn default() -> Self {
Self {
tabs: 4,
width: 80,
newline: Newline::default(),
}
}
}
impl Toppings {
pub fn width(self, width: usize) -> Self {
Self { width, ..self }
}
pub fn tabs(self, tabs: usize) -> Self {
Self { tabs, ..self }
}
pub fn newline(self, newline: Newline) -> Self {
Self { newline, ..self }
}
}
pub fn wrap<S: Sauce>(input: &str, toppings: Toppings) -> Wrap<'_, S> {
Wrap {
toppings,
lines: Merge::new(Parse::new(Lex::new(input))),
current: None,
}
}
pub struct Wrap<'t, S> {
toppings: Toppings,
lines: Merge<Parse<Lex<'t>>>,
current: Option<LineWrap<'t, S>>,
}
impl<'t, S: Sauce> Iterator for Wrap<'t, S> {
type Item = &'t str;
fn next(&mut self) -> Option<Self::Item> {
loop {
let inner = match &mut self.current {
Some(inner) => inner,
None => self
.current
.insert(LineWrap::new(self.lines.next()?, &self.toppings)),
};
match inner.next() {
Some(chunk) => return Some(chunk),
None => self.current = None,
}
}
}
}
#[cfg(test)]
#[macro_export]
macro_rules! token {
{ s } => { $crate::Token::Space };
{ t } => { $crate::Token::Tab };
{ lf } => { $crate::Token::Newline($crate::Newline::LF) };
{ crlf } => { $crate::Token::Newline($crate::Newline::CRLF) };
{ $word:expr } => { $crate::Token::Word($word) };
}
#[cfg(test)]
#[macro_export]
macro_rules! tokens {
[$($token:tt),*] => { vec![$($crate::token!{ $token }),*] };
}
#[cfg(test)]
#[macro_export]
macro_rules! line {
(
$indent:expr, $comment:expr,
$padding:expr, $bullet:expr
$(, $($word:expr),*)?
) => {
$crate::Line {
indent: $indent, comment: $comment,
padding: $padding, bullet: $bullet,
words: vec![$($($word),*)?], newline: false,
}
};
(
$indent:expr, $comment:expr,
$padding:expr, $bullet:expr
$(, $($word:expr),*)? ;
) => {
$crate::Line {
indent: $indent, comment: $comment,
padding: $padding, bullet: $bullet,
words: vec![$($($word),*)?], newline: true,
}
};
}