quicklatex 0.1.0

A program to help me write LaTeX quickly
Documentation
use std::{
    fmt::{self, Display, Formatter},
    mem::take,
};

use anyhow::{bail, Context, Error};

enum Type
{
    Bracket,
    Paren,
    Square,
    Absolute,
    Int,
}

enum Piece
{
    String(Vec<char>),
    ShortBracket(Type, Text),
}

struct Text
{
    pieces: Vec<Piece>,
}

impl Display for Text
{
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error>
    {
        for piece in &self.pieces
        {
            match piece
            {
                Piece::String(s) => write!(f, "{}", s.iter().collect::<String>())?,
                Piece::ShortBracket(Type::Bracket, text) => write!(f, "{{{text}}}")?,
                Piece::ShortBracket(Type::Paren, text) => write!(f, "ł({text}¶)")?,
                Piece::ShortBracket(Type::Square, text) => write!(f, "ł[{text}¶]")?,
                Piece::ShortBracket(Type::Absolute, text) => write!(f, "ł|{text}¶|")?,
                Piece::ShortBracket(Type::Int, text) => write!(f, "ł.{text}¶|")?,
            }
        }

        Ok(())
    }
}

fn parse_text(s: &[char]) -> Result<Text, Error>
{
    let mut i = 0;

    let mut current_string = vec![];

    let mut pieces = vec![];

    while i < s.len()
    {
        if "§¹²³¼½".contains(s[i])
        {
            if !current_string.is_empty()
            {
                pieces.push(Piece::String(take(&mut current_string)));
            }

            let length = match s[i]
            {
                '§' =>
                {
                    i += 1;
                    s[i].to_digit(36).context("Not a digit for short bracket")? as usize
                }
                '¹' => 1,
                '²' => 2,
                '³' => 3,
                '¼' => 4,
                '½' => 5,
                _ => unreachable!(),
            };

            i += 1;

            let typ = match s[i]
            {
                ' ' | '{' | '}' | 'c' => Type::Bracket,
                '(' | ')' | 'p' => Type::Paren,
                '[' | ']' | 'q' => Type::Square,
                '|' | 'a' => Type::Absolute,
                '.' | 'i' => Type::Int,
                c => bail!("{c:?} isn't supported as a short bracket type"),
            };

            i += 1;

            let inner_text = &s[i..(i + length)];

            i += length;

            pieces.push(Piece::ShortBracket(typ, parse_text(inner_text)?));
        }
        else
        {
            current_string.push(s[i]);
            i += 1;
        }
    }

    if !current_string.is_empty()
    {
        pieces.push(Piece::String(current_string));
    }

    Ok(Text { pieces })
}

pub fn add_short_brackets(s: &str) -> Result<String, Error>
{
    Ok(format!(
        "{}",
        parse_text(&s.chars().collect::<Vec<_>>()).context("Couldn't resolve short bracketing")?
    ))
}

#[cfg(test)]
mod tests
{
    use crate::shortbrackets::add_short_brackets;

    #[test]
    fn shortbrackets_integration_test()
    {
        let tests = vec![
            ("abcdefg", "abcdefg"),
            ("¹ abcdefg", "{a}bcdefg"),
            ("² abcdefg", "{ab}cdefg"),
            ("³ abcdefg", "{abc}defg"),
            ("¼ abcdefg", "{abcd}efg"),
            ("½ abcdefg", "{abcde}fg"),
            ("¬ abcdefg", "¬ abcdefg"),
            ("a¹ bcdefg", "a{b}cdefg"),
            ("a² bcdefg", "a{bc}defg"),
            ("a³ bcdefg", "a{bcd}efg"),
            ("a¼ bcdefg", "a{bcde}fg"),
            ("a½ bcdefg", "a{bcdef}g"),
            ("a¬ bcdefg", "a¬ bcdefg"),
            ("³pabcdefg", "ł(abc¶)defg"),
            ("a§9qbcdefghijklmnop", "ał[bcdefghij¶]klmnop"),
            ("abcdefgh²ijklmnop", "abcdefghł.jk¶|lmnop"),
        ];

        for (input, output) in tests
        {
            assert_eq!(add_short_brackets(input).unwrap(), output);
        }
    }
}