tent_codegen 0.2.1

Procedural macros for tent.
Documentation
use std::str::FromStr;

use proc_macro::{TokenStream, TokenTree};

pub(super) enum Token {
    Ident(String),
    Punct(char),
    Literal(String),
    Group { delimited: String, inner: String },
    RawGroup(String),
}

impl Token {
    fn from_tree(tree: TokenTree) -> Self {
        match tree {
            TokenTree::Ident(ident) => Self::Ident(ident.to_string()),
            TokenTree::Punct(punct) => Self::Punct(punct.as_char()),
            TokenTree::Literal(literal) => Self::Literal(literal.to_string()),
            TokenTree::Group(group) => Self::Group {
                delimited: group.to_string(),
                inner: group.stream().to_string(),
            },
        }
    }

    pub(super) fn describe(&self) -> String {
        match self {
            Self::Ident(text) | Self::Literal(text) | Self::RawGroup(text) => text.clone(),
            Self::Group { delimited, .. } => delimited.clone(),
            Self::Punct(ch) => ch.to_string(),
        }
    }
}

pub(super) struct Line<'a> {
    pub(super) num: usize,
    pub(super) level: usize,
    pub(super) text: &'a str,
}

pub(super) struct Source;

impl Source {
    pub(super) fn lines(input: &str) -> impl Iterator<Item = Line<'_>> {
        input.lines().enumerate().filter_map(|(idx, raw)| {
            let trimmed = raw.trim();
            if trimmed.is_empty() || trimmed.starts_with('/') {
                return None;
            }
            Some(Line {
                num: idx + 1,
                level: raw.find(|c: char| !c.is_whitespace()).unwrap_or(0),
                text: raw,
            })
        })
    }

    pub(super) fn tokenize(text: &str) -> Result<Vec<Token>, String> {
        let stream = TokenStream::from_str(text).map_err(|e| format!("failed to tokenize: {e}"))?;
        Ok(stream.into_iter().map(Token::from_tree).collect())
    }
}