untex 0.4.0-beta

Understand and manipulate TeX files with ease.
Documentation
//! *WIP* Parsing [`Token`](crate::latex::token::Token) iterators into more usable structures.

/*
use crate::error::Result;
use crate::token::Token;
use itertools::Itertools;
use logos::Lexer;
use std::iter::Peekable;

type Span = std::ops::Range<usize>;

pub trait TryFromTokens {
    fn try_from_tokens<'source, I>(source: &'source str, iter: &mut Peekable<I>) -> Result<Self>
    where
        Self: Sized,
        I: Iterator<Item = (Token, Span)>;

    fn try_from_lexer<'source>(lexer: Lexer<'source, Token>) -> Result<Self>
    where
        Self: Sized,
    {
        let source = lexer.source();
        let mut iter = lexer.spanned().peekable();
        <Self as TryFromTokens>::try_from_tokens(source, &mut iter)
    }
}

pub struct LaTeXDocument {
    preamble: Preamble,
    document: Document,
}

impl TryFromTokens for LaTeXDocument {
    fn try_from_tokens<'source, I>(source: &'source str, iter: &mut Peekable<I>) -> Result<Self>
    where
        I: Iterator<Item = (Token, Span)>,
    {
        let preamble = Preamble::try_from_tokens(source, iter)?;
        let document = Document::try_from_tokens(source, iter)?;

        Ok(Self { preamble, document })
    }
}

struct Preamble {}

impl TryFromTokens for Preamble {
    fn try_from_tokens<'source, I>(source: &'source str, iter: &mut Peekable<I>) -> Result<Self>
    where
        I: Iterator<Item = (Token, Span)>,
    {
        match iter
            .skip_while(|(token, _)| {
                matches!(token, Token::Comment | Token::Newline | Token::TabsOrSpaces)
            })
            .next()
            .expect("Preamble should start with a \\documentclass, but nothing was found")
        {
            (Token::DocumentClass, _) => (),
            (token, span) => panic!(
                "Preamble should start with a \\documentclass, not with {:#?}: {}",
                token, &source[span]
            ),
        }

        while let Some((token, _span)) = iter.peek() {
            match token {
                // If \begin is found, we assumed it is \begin{document}
                Token::EnvironmentBegin => return Ok(Self {}),
                _ => (),
            }
            iter.next();
        }
        Ok(Self {})
    }
}

struct Document {}

impl TryFromTokens for Document {
    fn try_from_tokens<'source, I>(source: &'source str, iter: &mut Peekable<I>) -> Result<Self>
    where
        I: Iterator<Item = (Token, Span)>,
    {
        let mut iter = iter.skip_while(|(token, _)| {
            matches!(token, Token::Comment | Token::Newline | Token::TabsOrSpaces)
        });

        match iter
            .next_tuple()
            .expect("Document should start with a \\begin{{document}}, but nothing was found")
        {
            (
                (Token::EnvironmentBegin, _),
                (Token::BraceOpen, _),
                (Token::Word, word_span),
                (Token::BraceClose, _),
            ) if &source[word_span.clone()] == "document" => (),
            _ => panic!("Document should start with a \\begin{{document}}"),
        }

        while let Some((token, span)) = iter.next() {
            match token {
                Token::EnvironmentEnd => {
                    if let Some((token, span)) = iter
                        .skip_while(|(token, _)| {
                            matches!(token, Token::Comment | Token::Newline | Token::TabsOrSpaces)
                        })
                        .next()
                    {
                        panic!(
                            "Unexpected token found after \\end{{document}}: {}",
                            &source[span]
                        );
                    }
                    return Ok(Self {});
                }
                _ => (),
            }
        }
        panic!("Missing \\end{{document}}");
        Ok(Self {})
    }
}

struct Options<'source> {
    s: &'source str,
}

struct Arguments<'source> {
    s: &'source str,
}

struct Command<'source> {
    name: &'source str,
    opts: Options<'source>,
    args: Arguments<'source>,
    span: Span,
}

struct Environment<'source> {
    name: &'source str,
    opts: Option<Options<'source>>,
    args: Option<Arguments<'source>>,
    span: Span,
}
*/