o7 0.1.1

O7 workflow DSL runner
Documentation
use std::fmt;

/// A token with its type, value, and source location.
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
    pub ty: TokenType,
    pub value: String,
    pub line: usize,
    pub column: usize,
}

impl Token {
    /// Create a new token with the given type, value, and source location.
    pub fn new(ty: TokenType, value: impl Into<String>, line: usize, column: usize) -> Self {
        Token {
            ty,
            value: value.into(),
            line,
            column,
        }
    }

    /// Create a synthetic token with an empty value (used for Indent, Dedent, Newline, Eof).
    pub fn synthetic(ty: TokenType, line: usize, column: usize) -> Self {
        Token {
            ty,
            value: String::new(),
            line,
            column,
        }
    }
}

/// All token types for the O7 DSL.
///
/// INDENT, DEDENT, NEWLINE, and EOF are synthetic tokens injected
/// by the indent post-processor, not by the hand-written lexer.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TokenType {
    // Keywords
    Version,
    Workflow,
    Run,
    If,
    Not,
    While,
    ParAnd,
    Exec,
    Harness,
    Prompt,
    PromptFile,
    Args,
    FailPolicy,
    Match,
    Else,

    // Literals and identifiers
    Name,
    Number,
    Str,
    BareValue,

    // Punctuation
    Colon,
    Arrow,

    // Synthetic tokens (injected by indent processor)
    Indent,
    Dedent,
    Newline,
    Eof,
}

impl fmt::Display for TokenType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TokenType::Version => write!(f, "version"),
            TokenType::Workflow => write!(f, "workflow"),
            TokenType::Run => write!(f, "run"),
            TokenType::If => write!(f, "if"),
            TokenType::Not => write!(f, "not"),
            TokenType::While => write!(f, "while"),
            TokenType::ParAnd => write!(f, "par-and"),
            TokenType::Exec => write!(f, "exec"),
            TokenType::Harness => write!(f, "harness"),
            TokenType::Prompt => write!(f, "prompt"),
            TokenType::PromptFile => write!(f, "prompt_file"),
            TokenType::Args => write!(f, "args"),
            TokenType::FailPolicy => write!(f, "fail-policy"),
            TokenType::Match => write!(f, "match"),
            TokenType::Else => write!(f, "else"),
            TokenType::Name => write!(f, "name"),
            TokenType::Number => write!(f, "number"),
            TokenType::Str => write!(f, "string"),
            TokenType::BareValue => write!(f, "bare value"),
            TokenType::Colon => write!(f, ":"),
            TokenType::Arrow => write!(f, "->"),
            TokenType::Indent => write!(f, "INDENT"),
            TokenType::Dedent => write!(f, "DEDENT"),
            TokenType::Newline => write!(f, "NEWLINE"),
            TokenType::Eof => write!(f, "EOF"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_token_type_display() {
        assert_eq!(format!("{}", TokenType::Version), "version");
        assert_eq!(format!("{}", TokenType::ParAnd), "par-and");
        assert_eq!(format!("{}", TokenType::Eof), "EOF");
    }

    #[test]
    fn test_token_creation() {
        let tok = Token {
            ty: TokenType::Workflow,
            value: "workflow".to_string(),
            line: 1,
            column: 1,
        };
        assert_eq!(tok.ty, TokenType::Workflow);
        assert_eq!(tok.line, 1);
    }
}