use crate::Position;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QuoteStyle {
Plain,
Single,
Double,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token {
pub token_type: TokenType,
pub start_position: Position,
pub end_position: Position,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenType {
StreamStart,
StreamEnd,
DocumentStart,
DocumentEnd,
YamlDirective(u8, u8), TagDirective(String, String),
BlockSequenceStart,
BlockMappingStart,
BlockEnd,
FlowSequenceStart,
FlowSequenceEnd,
FlowMappingStart,
FlowMappingEnd,
BlockEntry,
FlowEntry,
Key,
Value,
Scalar(String, QuoteStyle),
BlockScalarLiteral(String),
BlockScalarFolded(String),
Alias(String),
Anchor(String),
Tag(String),
Comment(String),
}
impl Token {
pub const fn new(
token_type: TokenType,
start_position: Position,
end_position: Position,
) -> Self {
Self {
token_type,
start_position,
end_position,
}
}
pub const fn simple(token_type: TokenType, position: Position) -> Self {
Self::new(token_type, position, position)
}
pub const fn token_type(&self) -> &TokenType {
&self.token_type
}
pub const fn start_position(&self) -> Position {
self.start_position
}
pub const fn end_position(&self) -> Position {
self.end_position
}
pub const fn is_scalar(&self) -> bool {
matches!(
self.token_type,
TokenType::Scalar(_, _)
| TokenType::BlockScalarLiteral(_)
| TokenType::BlockScalarFolded(_)
)
}
pub fn as_scalar(&self) -> Option<&str> {
match &self.token_type {
TokenType::Scalar(s, _)
| TokenType::BlockScalarLiteral(s)
| TokenType::BlockScalarFolded(s) => Some(s),
_ => None,
}
}
pub fn as_scalar_with_style(&self) -> Option<(&str, QuoteStyle)> {
match &self.token_type {
TokenType::Scalar(s, style) => Some((s, style.clone())),
TokenType::BlockScalarLiteral(s) => Some((s, QuoteStyle::Plain)), TokenType::BlockScalarFolded(s) => Some((s, QuoteStyle::Plain)), _ => None,
}
}
pub const fn is_flow_collection_start(&self) -> bool {
matches!(
self.token_type,
TokenType::FlowSequenceStart | TokenType::FlowMappingStart
)
}
pub const fn is_flow_collection_end(&self) -> bool {
matches!(
self.token_type,
TokenType::FlowSequenceEnd | TokenType::FlowMappingEnd
)
}
pub const fn is_block_collection_start(&self) -> bool {
matches!(
self.token_type,
TokenType::BlockSequenceStart | TokenType::BlockMappingStart
)
}
pub const fn is_document_boundary(&self) -> bool {
matches!(
self.token_type,
TokenType::DocumentStart | TokenType::DocumentEnd
)
}
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.token_type {
TokenType::StreamStart => write!(f, "STREAM-START"),
TokenType::StreamEnd => write!(f, "STREAM-END"),
TokenType::DocumentStart => write!(f, "DOCUMENT-START"),
TokenType::DocumentEnd => write!(f, "DOCUMENT-END"),
TokenType::BlockSequenceStart => write!(f, "BLOCK-SEQUENCE-START"),
TokenType::BlockMappingStart => write!(f, "BLOCK-MAPPING-START"),
TokenType::BlockEnd => write!(f, "BLOCK-END"),
TokenType::FlowSequenceStart => write!(f, "FLOW-SEQUENCE-START"),
TokenType::FlowSequenceEnd => write!(f, "FLOW-SEQUENCE-END"),
TokenType::FlowMappingStart => write!(f, "FLOW-MAPPING-START"),
TokenType::FlowMappingEnd => write!(f, "FLOW-MAPPING-END"),
TokenType::BlockEntry => write!(f, "BLOCK-ENTRY"),
TokenType::FlowEntry => write!(f, "FLOW-ENTRY"),
TokenType::Key => write!(f, "KEY"),
TokenType::Value => write!(f, "VALUE"),
TokenType::Scalar(s, style) => write!(f, "SCALAR({}, {:?})", s, style),
TokenType::BlockScalarLiteral(s) => write!(f, "LITERAL({})", s),
TokenType::BlockScalarFolded(s) => write!(f, "FOLDED({})", s),
TokenType::Alias(name) => write!(f, "ALIAS({})", name),
TokenType::Anchor(name) => write!(f, "ANCHOR({})", name),
TokenType::Tag(tag) => write!(f, "TAG({})", tag),
TokenType::Comment(text) => write!(f, "COMMENT({})", text),
TokenType::YamlDirective(major, minor) => {
write!(f, "YAML-DIRECTIVE({}.{})", major, minor)
}
TokenType::TagDirective(handle, prefix) => {
write!(f, "TAG-DIRECTIVE({}, {})", handle, prefix)
}
}
}
}
impl fmt::Display for TokenType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::StreamStart => write!(f, "StreamStart"),
Self::StreamEnd => write!(f, "StreamEnd"),
Self::DocumentStart => write!(f, "DocumentStart"),
Self::DocumentEnd => write!(f, "DocumentEnd"),
Self::BlockSequenceStart => write!(f, "BlockSequenceStart"),
Self::BlockMappingStart => write!(f, "BlockMappingStart"),
Self::BlockEnd => write!(f, "BlockEnd"),
Self::FlowSequenceStart => write!(f, "FlowSequenceStart"),
Self::FlowSequenceEnd => write!(f, "FlowSequenceEnd"),
Self::FlowMappingStart => write!(f, "FlowMappingStart"),
Self::FlowMappingEnd => write!(f, "FlowMappingEnd"),
Self::BlockEntry => write!(f, "BlockEntry"),
Self::FlowEntry => write!(f, "FlowEntry"),
Self::Key => write!(f, "Key"),
Self::Value => write!(f, "Value"),
Self::Scalar(s, style) => write!(f, "Scalar({}, {:?})", s, style),
Self::BlockScalarLiteral(s) => write!(f, "BlockScalarLiteral({})", s),
Self::BlockScalarFolded(s) => write!(f, "BlockScalarFolded({})", s),
Self::Alias(name) => write!(f, "Alias({})", name),
Self::Anchor(name) => write!(f, "Anchor({})", name),
Self::Tag(tag) => write!(f, "Tag({})", tag),
Self::Comment(text) => write!(f, "Comment({})", text),
Self::YamlDirective(major, minor) => write!(f, "YamlDirective({}.{})", major, minor),
Self::TagDirective(handle, prefix) => write!(f, "TagDirective({}, {})", handle, prefix),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_creation() {
let pos1 = Position::at(1, 1, 0);
let pos2 = Position::at(1, 5, 4);
let token = Token::new(
TokenType::Scalar("hello".to_string(), QuoteStyle::Plain),
pos1,
pos2,
);
assert_eq!(token.start_position(), pos1);
assert_eq!(token.end_position(), pos2);
assert!(token.is_scalar());
assert_eq!(token.as_scalar(), Some("hello"));
}
#[test]
fn test_token_type_checks() {
let scalar_token = Token::simple(
TokenType::Scalar("test".to_string(), QuoteStyle::Plain),
Position::start(),
);
let flow_start = Token::simple(TokenType::FlowSequenceStart, Position::start());
let doc_start = Token::simple(TokenType::DocumentStart, Position::start());
assert!(scalar_token.is_scalar());
assert!(!scalar_token.is_flow_collection_start());
assert!(flow_start.is_flow_collection_start());
assert!(!flow_start.is_scalar());
assert!(doc_start.is_document_boundary());
assert!(!doc_start.is_scalar());
}
#[test]
fn test_token_display() {
let scalar = Token::simple(
TokenType::Scalar("hello".to_string(), QuoteStyle::Plain),
Position::start(),
);
assert_eq!(format!("{}", scalar), "SCALAR(hello, Plain)");
let flow_start = Token::simple(TokenType::FlowSequenceStart, Position::start());
assert_eq!(format!("{}", flow_start), "FLOW-SEQUENCE-START");
}
#[test]
fn test_token_type_display() {
assert_eq!(format!("{}", TokenType::StreamStart), "StreamStart");
assert_eq!(
format!(
"{}",
TokenType::Scalar("test".to_string(), QuoteStyle::Plain)
),
"Scalar(test, Plain)"
);
assert_eq!(
format!("{}", TokenType::FlowSequenceStart),
"FlowSequenceStart"
);
}
}