pub mod ast;
pub mod grammar;
pub mod lexer;
pub use ast::{AstNode, AstValue, Comment, Comments, Document, Key, Span, TableEntry};
pub use grammar::{parse_file, parse_string, NomlParser};
pub use lexer::{Lexer, Token, TokenKind};
use crate::error::Result;
pub fn parse(source: &str) -> Result<Document> {
parse_string(source, None)
}
pub fn parse_from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Document> {
parse_file(path.as_ref())
}
pub fn validate(source: &str) -> Result<()> {
parse(source).map(|_| ())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_parsing() {
let source = r#"
# Simple config
name = "test"
version = 1.0
debug = true
[database]
url = "sqlite://test.db"
"#;
let doc = parse(source).expect("Should parse successfully");
let value = doc.to_value().expect("Should convert to value");
assert_eq!(value.get("name").unwrap().as_string().unwrap(), "test");
assert_eq!(value.get("version").unwrap().as_float().unwrap(), 1.0);
assert!(value.get("debug").unwrap().as_bool().unwrap());
assert_eq!(
value.get("database.url").unwrap().as_string().unwrap(),
"sqlite://test.db"
);
}
#[test]
fn validation_success() {
let valid_noml = r#"
[server]
host = "localhost"
port = 8080
"#;
assert!(validate(valid_noml).is_ok());
}
#[test]
fn validation_failure() {
let invalid_noml = r#"
[server
host = "localhost"
"#;
assert!(validate(invalid_noml).is_err());
}
#[test]
fn comment_preservation() {
let source = r#"
# Main configuration
name = "test" # App name
# Database section
[database]
url = "sqlite://test.db"
"#;
let doc = parse(source).expect("Should parse successfully");
let comments = doc.all_comments();
assert!(!comments.is_empty());
assert!(comments
.iter()
.any(|c| c.text.contains("Main configuration")));
assert!(comments.iter().any(|c| c.text.contains("App name")));
assert!(comments.iter().any(|c| c.text.contains("Database section")));
}
#[test]
fn nested_key_paths() {
let source = r#"
[server.database]
url = "postgres://localhost/test"
[server.cache.redis]
host = "localhost"
port = 6379
"#;
let doc = parse(source).expect("Should parse successfully");
let value = doc.to_value().expect("Should convert to value");
assert_eq!(
value
.get("server.database.url")
.unwrap()
.as_string()
.unwrap(),
"postgres://localhost/test"
);
assert_eq!(
value
.get("server.cache.redis.host")
.unwrap()
.as_string()
.unwrap(),
"localhost"
);
assert_eq!(
value
.get("server.cache.redis.port")
.unwrap()
.as_integer()
.unwrap(),
6379
);
}
#[test]
fn array_operations() {
let source = r#"
ports = [8080, 8081, 8082]
servers = [
"server1.example.com",
"server2.example.com",
]
"#;
let doc = parse(source).expect("Should parse successfully");
let value = doc.to_value().expect("Should convert to value");
let ports = value.get("ports").unwrap().as_array().unwrap();
assert_eq!(ports.len(), 3);
assert_eq!(ports[0].as_integer().unwrap(), 8080);
let servers = value.get("servers").unwrap().as_array().unwrap();
assert_eq!(servers.len(), 2);
assert_eq!(servers[0].as_string().unwrap(), "server1.example.com");
}
}