use crate::syntax::{SyntaxKind, SyntaxNode, SyntaxToken};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value {
Struct(Struct),
Tuple(Tuple),
List(List),
Map(Map),
EnumVariant(EnumVariant),
Unit(Unit),
Literal(Literal),
Error(SyntaxNode),
}
impl Value {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
Some(match node.kind() {
SyntaxKind::Struct => Self::Struct(Struct(node)),
SyntaxKind::Tuple => Self::Tuple(Tuple(node)),
SyntaxKind::List => Self::List(List(node)),
SyntaxKind::Map => Self::Map(Map(node)),
SyntaxKind::EnumVariant => Self::EnumVariant(EnumVariant(node)),
SyntaxKind::Unit => Self::Unit(Unit(node)),
SyntaxKind::Literal => Self::Literal(Literal(node)),
SyntaxKind::Error => Self::Error(node),
_ => return None,
})
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
match self {
Self::Struct(n) => n.syntax(),
Self::Tuple(n) => n.syntax(),
Self::List(n) => n.syntax(),
Self::Map(n) => n.syntax(),
Self::EnumVariant(n) => n.syntax(),
Self::Unit(n) => n.syntax(),
Self::Literal(n) => n.syntax(),
Self::Error(n) => n,
}
}
}
fn first_value_child(parent: &SyntaxNode) -> Option<Value> {
parent.children().find_map(Value::cast)
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Document(SyntaxNode);
impl Document {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::Root).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
#[must_use]
pub fn value(&self) -> Option<Value> {
first_value_child(&self.0)
}
pub fn extension_attrs(&self) -> impl Iterator<Item = ExtensionAttr> + '_ {
self.0.children().filter_map(ExtensionAttr::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Struct(SyntaxNode);
impl Struct {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::Struct).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
#[must_use]
pub fn name(&self) -> Option<SyntaxToken> {
self.0.first_token_of(SyntaxKind::Ident)
}
#[must_use]
pub fn name_text(&self) -> Option<String> {
self.name().map(|t| t.text().to_string())
}
pub fn fields(&self) -> impl Iterator<Item = StructField> + '_ {
self.0.children().filter_map(StructField::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructField(SyntaxNode);
impl StructField {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::StructField).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
#[must_use]
pub fn name(&self) -> Option<SyntaxToken> {
self.0.first_token_of(SyntaxKind::Ident)
}
#[must_use]
pub fn name_text(&self) -> Option<String> {
self.name().map(|t| t.text().to_string())
}
#[must_use]
pub fn value(&self) -> Option<Value> {
first_value_child(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Tuple(SyntaxNode);
impl Tuple {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::Tuple).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
pub fn items(&self) -> impl Iterator<Item = Value> + '_ {
self.0.children().filter_map(Value::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct List(SyntaxNode);
impl List {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::List).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
pub fn items(&self) -> impl Iterator<Item = Value> + '_ {
self.0.children().filter_map(Value::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Map(SyntaxNode);
impl Map {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::Map).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
pub fn entries(&self) -> impl Iterator<Item = MapEntry> + '_ {
self.0.children().filter_map(MapEntry::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MapEntry(SyntaxNode);
impl MapEntry {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::MapEntry).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
#[must_use]
pub fn key(&self) -> Option<Value> {
self.0.children().filter_map(Value::cast).next()
}
#[must_use]
pub fn value(&self) -> Option<Value> {
self.0.children().filter_map(Value::cast).nth(1)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EnumVariant(SyntaxNode);
impl EnumVariant {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::EnumVariant).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
#[must_use]
pub fn name(&self) -> Option<SyntaxToken> {
self.0.first_token_of(SyntaxKind::Ident)
}
#[must_use]
pub fn name_text(&self) -> Option<String> {
self.name().map(|t| t.text().to_string())
}
pub fn entries(&self) -> impl Iterator<Item = MapEntry> + '_ {
self.0.children().filter_map(MapEntry::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Unit(SyntaxNode);
impl Unit {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::Unit).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Literal(SyntaxNode);
impl Literal {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::Literal).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
#[must_use]
pub fn token(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|el| el.as_token().cloned())
.find(|t| !t.is_trivia())
}
#[must_use]
pub fn token_kind(&self) -> Option<SyntaxKind> {
self.token().map(|t| t.kind())
}
#[must_use]
pub fn text(&self) -> Option<String> {
self.token().map(|t| t.text().to_string())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExtensionAttr(SyntaxNode);
impl ExtensionAttr {
#[must_use]
pub fn cast(node: SyntaxNode) -> Option<Self> {
(node.kind() == SyntaxKind::ExtensionAttr).then_some(Self(node))
}
#[must_use]
pub fn syntax(&self) -> &SyntaxNode {
&self.0
}
pub fn extensions(&self) -> impl Iterator<Item = SyntaxToken> + '_ {
self.0
.children_with_tokens()
.filter_map(|el| el.as_token().cloned())
.filter(|t| t.kind() == SyntaxKind::Ident)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse;
fn doc_of(src: &str) -> Document {
Document::cast(parse(src).root()).expect("root is always a Document")
}
#[test]
fn struct_fields_and_name() {
let d = doc_of("Point(x: 1, y: -2.0)");
let Some(Value::Struct(s)) = d.value() else {
panic!("expected a struct");
};
assert_eq!(s.name_text().as_deref(), Some("Point"));
let fields: Vec<_> = s.fields().collect();
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].name_text().as_deref(), Some("x"));
assert_eq!(fields[1].name_text().as_deref(), Some("y"));
let Some(Value::Literal(lit)) = fields[0].value() else {
panic!("x should be a literal");
};
assert_eq!(lit.text().as_deref(), Some("1"));
assert_eq!(lit.token_kind(), Some(SyntaxKind::Integer));
}
#[test]
fn anonymous_struct_has_no_name() {
let d = doc_of("(a: 1, b: 2)");
let Some(Value::Struct(s)) = d.value() else {
panic!("expected a struct");
};
assert_eq!(s.name_text(), None);
assert_eq!(s.fields().count(), 2);
}
#[test]
fn list_items() {
let d = doc_of("[1, 2, 3,]");
let Some(Value::List(list)) = d.value() else {
panic!("expected a list");
};
let items: Vec<_> = list.items().collect();
assert_eq!(items.len(), 3);
for it in &items {
assert!(matches!(it, Value::Literal(_)));
}
}
#[test]
fn tuple_items() {
let d = doc_of("(1, \"two\", 'c')");
let Some(Value::Tuple(t)) = d.value() else {
panic!("expected a tuple");
};
assert_eq!(t.items().count(), 3);
}
#[test]
fn map_entries_with_non_string_keys() {
let d = doc_of("{ 1: \"one\", 'c': true }");
let Some(Value::Map(m)) = d.value() else {
panic!("expected a map");
};
let entries: Vec<_> = m.entries().collect();
assert_eq!(entries.len(), 2);
let Some(Value::Literal(k0)) = entries[0].key() else {
panic!("key 0 should be a literal");
};
assert_eq!(k0.token_kind(), Some(SyntaxKind::Integer));
let Some(Value::Literal(v0)) = entries[0].value() else {
panic!("value 0 should be a literal");
};
assert_eq!(v0.token_kind(), Some(SyntaxKind::String));
}
#[test]
fn enum_variant_struct_like() {
let d = doc_of("Variant { field: 1 }");
let Some(Value::EnumVariant(v)) = d.value() else {
panic!("expected an enum variant");
};
assert_eq!(v.name_text().as_deref(), Some("Variant"));
assert_eq!(v.entries().count(), 1);
}
#[test]
fn bare_enum_variant() {
let d = doc_of("Unit");
let Some(Value::EnumVariant(v)) = d.value() else {
panic!("expected a bare variant");
};
assert_eq!(v.name_text().as_deref(), Some("Unit"));
assert_eq!(v.entries().count(), 0);
}
#[test]
fn unit_value() {
let d = doc_of("()");
assert!(matches!(d.value(), Some(Value::Unit(_))));
}
#[test]
fn literal_text_is_verbatim() {
let d = doc_of("r#\"raw \"q\" str\"#");
let Some(Value::Literal(lit)) = d.value() else {
panic!("expected a literal");
};
assert_eq!(lit.token_kind(), Some(SyntaxKind::RawString));
assert_eq!(lit.text().as_deref(), Some("r#\"raw \"q\" str\"#"));
}
#[test]
fn extension_attrs_and_value() {
let d = doc_of("#![enable(implicit_some)]\nSome(5)");
let attrs: Vec<_> = d.extension_attrs().collect();
assert_eq!(attrs.len(), 1);
let exts: Vec<_> = attrs[0]
.extensions()
.map(|t| t.text().to_string())
.collect();
assert!(exts.contains(&"implicit_some".to_string()));
assert!(d.value().is_some());
}
#[test]
fn error_value_is_reachable() {
let d = doc_of("@");
assert!(matches!(d.value(), Some(Value::Error(_))));
}
#[test]
fn value_syntax_round_trips_text() {
let src = "Foo(x: [1, 2], y: { 'a': 'b' })";
let d = doc_of(src);
let v = d.value().expect("has a value");
assert_eq!(v.syntax().text(), src);
}
}