use crate::{
syntax::{SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxNode, SyntaxToken},
util::{unescape, StringExt},
};
use indexmap::IndexMap;
use rowan::{TextRange, TextSize};
use std::{hash::Hash, iter::FromIterator, mem, rc::Rc};
#[macro_use]
mod macros;
pub trait Cast: Sized + private::Sealed {
fn cast(element: SyntaxElement) -> Option<Self>;
}
pub trait Common: core::fmt::Display + core::fmt::Debug + private::Sealed {
fn syntax(&self) -> SyntaxElement;
fn text_range(&self) -> TextRange;
fn is_valid(&self) -> bool {
true
}
}
mod private {
use super::*;
pub trait Sealed {}
dom_sealed!(
Node,
RootNode,
EntryNode,
KeyNode,
ValueNode,
ArrayNode,
TableNode,
IntegerNode,
StringNode,
BoolNode,
FloatNode,
DateNode
);
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Node {
Root(RootNode),
Table(TableNode),
Entry(EntryNode),
Key(KeyNode),
Value(ValueNode),
Array(ArrayNode),
}
dom_node_from!(
RootNode => Root,
TableNode => Table,
EntryNode => Entry,
KeyNode => Key,
ValueNode => Value,
ArrayNode => Array
);
impl core::fmt::Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Node::Root(v) => v.fmt(f),
Node::Table(v) => v.fmt(f),
Node::Entry(v) => v.fmt(f),
Node::Key(v) => v.fmt(f),
Node::Value(v) => v.fmt(f),
Node::Array(v) => v.fmt(f),
}
}
}
impl Common for Node {
fn syntax(&self) -> SyntaxElement {
match self {
Node::Root(v) => v.syntax(),
Node::Table(v) => v.syntax(),
Node::Entry(v) => v.syntax(),
Node::Key(v) => v.syntax(),
Node::Value(v) => v.syntax(),
Node::Array(v) => v.syntax(),
}
}
fn text_range(&self) -> TextRange {
match self {
Node::Root(v) => v.text_range(),
Node::Table(v) => v.text_range(),
Node::Entry(v) => v.text_range(),
Node::Key(v) => v.text_range(),
Node::Value(v) => v.text_range(),
Node::Array(v) => v.text_range(),
}
}
fn is_valid(&self) -> bool {
match self {
Node::Root(v) => v.is_valid(),
Node::Table(v) => v.is_valid(),
Node::Entry(v) => v.is_valid(),
Node::Key(v) => v.is_valid(),
Node::Value(v) => v.is_valid(),
Node::Array(v) => v.is_valid(),
}
}
}
impl Cast for Node {
fn cast(element: SyntaxElement) -> Option<Self> {
match element.kind() {
STRING
| MULTI_LINE_STRING
| STRING_LITERAL
| MULTI_LINE_STRING_LITERAL
| INTEGER
| INTEGER_HEX
| INTEGER_OCT
| INTEGER_BIN
| FLOAT
| BOOL
| DATE
| INLINE_TABLE => ValueNode::dom_inner(element).map(Node::Value),
KEY => KeyNode::cast(element).map(Node::Key),
VALUE => ValueNode::cast(element).map(Node::Value),
TABLE_HEADER | TABLE_ARRAY_HEADER => TableNode::cast(element).map(Node::Table),
ENTRY => EntryNode::cast(element).map(Node::Entry),
ARRAY => ArrayNode::cast(element).map(Node::Array),
ROOT => RootNode::cast(element).map(Node::Root),
_ => None,
}
}
}
impl Node {
pub fn text_range(&self) -> TextRange {
match self {
Node::Root(v) => v.text_range(),
Node::Table(v) => v.text_range(),
Node::Entry(v) => v.text_range(),
Node::Key(v) => v.text_range(),
Node::Value(v) => v.text_range(),
Node::Array(v) => v.text_range(),
}
}
pub fn kind(&self) -> SyntaxKind {
match self {
Node::Root(v) => v.syntax().kind(),
Node::Table(v) => v.syntax().kind(),
Node::Entry(v) => v.syntax().kind(),
Node::Key(v) => v.syntax().kind(),
Node::Value(v) => v.syntax().kind(),
Node::Array(v) => v.syntax().kind(),
}
}
}
dom_display!(
RootNode,
TableNode,
EntryNode,
ArrayNode,
IntegerNode,
StringNode
);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RootNode {
syntax: SyntaxNode,
errors: Vec<Error>,
entries: Entries,
}
impl RootNode {
pub fn entries(&self) -> &Entries {
&self.entries
}
pub fn into_entries(self) -> Entries {
self.entries
}
pub fn errors(&self) -> &[Error] {
&self.errors
}
}
impl Common for RootNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
self.syntax.text_range()
}
}
#[allow(clippy::cognitive_complexity)]
impl Cast for RootNode {
fn cast(syntax: SyntaxElement) -> Option<Self> {
if syntax.kind() != ROOT {
return None;
}
let syntax_node = syntax.into_node().unwrap();
let child_count = syntax_node.children_with_tokens().count();
let mut entries: IndexMap<KeyNode, EntryNode> = IndexMap::with_capacity(child_count);
let mut prefixes: Vec<Option<KeyNode>> = Vec::with_capacity(child_count);
let mut prefix: Option<KeyNode> = None;
let mut tables: Vec<Vec<KeyNode>> = Vec::with_capacity(child_count);
let mut errors = Vec::new();
for child in syntax_node.children_with_tokens() {
match child.kind() {
TABLE_HEADER | TABLE_ARRAY_HEADER => {
let t = match TableNode::cast(child) {
None => continue,
Some(t) => t,
};
let mut key = match t
.syntax
.first_child()
.and_then(|n| KeyNode::cast(rowan::NodeOrToken::Node(n)))
.ok_or(Error::Spanned {
range: t.text_range(),
message: "table has no key".into(),
}) {
Ok(k) => k,
Err(err) => {
errors.push(err);
continue;
}
};
let existing_table = entries.iter().rev().find(|(k, _)| k.eq_keys(&key));
if let Some((existing_key, existing)) = existing_table {
let existing_table_array = match &existing.value {
ValueNode::Table(t) => t.is_part_of_array(),
_ => false,
};
if existing_table_array && !t.is_part_of_array() {
errors.push(Error::ExpectedTableArray {
target: existing.key().clone(),
key: key.clone(),
});
} else if !existing_table_array && t.is_part_of_array() {
errors.push(Error::ExpectedTableArray {
target: key.clone(),
key: existing.key().clone(),
});
} else if !existing_table_array && !t.is_part_of_array() {
errors.push(Error::DuplicateKey {
first: existing.key().clone(),
second: key.clone(),
});
} else {
key = key.with_index(existing_key.index + 1);
entries.insert(
key.clone(),
EntryNode {
syntax: t.syntax.clone(),
key: key.clone(),
value: ValueNode::Table(t),
next_entry: None,
},
);
}
} else {
entries.insert(
key.clone(),
EntryNode {
syntax: t.syntax.clone(),
key: key.clone(),
value: ValueNode::Table(t),
next_entry: None,
},
);
}
for (i, (k, e)) in entries.iter().enumerate().rev().skip(1) {
let entry_prefix = prefixes.get(i).unwrap();
if let Some(p) = entry_prefix {
if k.contains(&key) && p.common_prefix_count(&key) < key.key_count() {
errors.push(Error::TopLevelTableDefined {
table: key.clone(),
key: e.key.clone(),
});
}
}
}
if tables.len() == key.index {
tables.push(vec![key.clone()]);
} else {
tables[key.index].push(key.clone());
}
prefixes.push(None);
prefix = Some(key);
}
ENTRY => {
let entry = match EntryNode::cast(child) {
None => continue,
Some(e) => e,
};
let insert_key = match &prefix {
None => entry.key().clone(),
Some(p) => entry.key().clone().with_prefix(p),
};
if let Some(p) = &prefix {
let table_containing_entry =
tables.get(insert_key.index).and_then(|same_index_tables| {
same_index_tables
.iter()
.find(|table| {
insert_key
.clone()
.without_prefix(p)
.contains(&(&**table).clone().without_prefix(p))
})
.and_then(|table| {
if table != same_index_tables.last().unwrap() {
Some(table)
} else {
None
}
})
});
if let Some(table_key) = table_containing_entry {
errors.push(Error::TopLevelTableDefined {
table: table_key.clone(),
key: entry.key().clone(),
});
continue;
}
}
if let Some(existing) = entries.get(&insert_key) {
errors.push(Error::DuplicateKey {
first: existing.key().clone(),
second: entry.key().clone(),
});
continue;
}
prefixes.push(prefix.clone());
entries.insert(insert_key, entry);
}
_ => {}
}
}
let grouped_by_index = entries.iter().fold(
Vec::<Vec<(&KeyNode, &EntryNode)>>::new(),
|mut all, (k, e)| {
if all.len() < k.index + 1 {
let mut v = Vec::with_capacity(entries.len());
v.push((k, e));
all.push(v);
} else {
all[k.index].push((k, e));
}
all
},
);
#[allow(clippy::never_loop)]
'outer: for (group_idx, group) in grouped_by_index.iter().enumerate() {
for (i, (k, e)) in group.iter().enumerate() {
if group_idx == 0 {
let is_table_array = match &e.value {
ValueNode::Table(t) => t.is_part_of_array(),
_ => false,
};
if !is_table_array {
let table_array = group.iter().skip(i).find(|(k2, e2)| match &e2.value {
ValueNode::Table(t) => k2.is_part_of(k) && t.is_part_of_array(),
_ => false,
});
if let Some((k2, _)) = table_array {
errors.push(Error::ExpectedTableArray {
target: (&**k2).clone(),
key: (&**k).clone(),
})
}
}
}
break 'outer;
}
}
let mut final_entries = Entries::from_map(entries);
if errors.is_empty() {
final_entries.merge(&mut errors);
final_entries.normalize();
}
final_entries.set_table_spans(
&syntax_node,
Some(syntax_node.text_range().end() + TextSize::from(1)),
);
Some(Self {
entries: final_entries,
errors,
syntax: syntax_node,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TableNode {
syntax: SyntaxNode,
array: bool,
pseudo: bool,
next_entry: Option<TextSize>,
entries: Entries,
}
impl TableNode {
pub fn into_entries(self) -> Entries {
self.entries
}
pub fn entries(&self) -> &Entries {
&self.entries
}
pub fn is_part_of_array(&self) -> bool {
self.array
}
pub fn is_inline(&self) -> bool {
match self.syntax.kind() {
INLINE_TABLE => true,
_ => false,
}
}
pub fn is_pseudo(&self) -> bool {
self.pseudo
}
}
impl Common for TableNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
let mut range = self.syntax.text_range();
if let Some(r) = self.entries().text_range() {
range = range.cover(r)
}
if let Some(r) = self.next_entry.as_ref() {
range = range.cover_offset(*r);
}
range
}
}
impl Cast for TableNode {
fn cast(syntax: SyntaxElement) -> Option<Self> {
match syntax.kind() {
TABLE_HEADER | TABLE_ARRAY_HEADER => {
let n = syntax.into_node().unwrap();
let key = n
.first_child()
.and_then(|e| KeyNode::cast(rowan::NodeOrToken::Node(e)));
key.as_ref()?;
Some(Self {
entries: Entries::default(),
next_entry: None,
pseudo: false,
array: n.kind() == TABLE_ARRAY_HEADER,
syntax: n,
})
}
INLINE_TABLE => Some(Self {
entries: syntax
.as_node()
.unwrap()
.children_with_tokens()
.filter_map(Cast::cast)
.collect(),
next_entry: None,
array: false,
pseudo: false,
syntax: syntax.into_node().unwrap(),
}),
_ => None,
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct Entries(Vec<EntryNode>);
impl Entries {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
pub fn iter(&self) -> impl Iterator<Item = &EntryNode> {
self.0.iter()
}
pub fn text_range(&self) -> Option<TextRange> {
let mut range = None;
for e in &self.0 {
match &mut range {
None => range = Some(e.key().text_range()),
Some(r) => *r = r.cover(e.value().text_range()),
}
}
range
}
fn set_table_spans(&mut self, root_syntax: &SyntaxNode, end: Option<TextSize>) {
for entry in self.0.iter_mut() {
if let TABLE_HEADER = entry.syntax.kind() {
let mut found = false;
let entry_header_string = entry.syntax.to_string();
for n in root_syntax.children() {
if n.text_range().start() < entry.syntax.text_range().end() {
continue;
}
let new_header_string = n.to_string();
if let TABLE_HEADER | TABLE_ARRAY_HEADER = n.kind() {
if !new_header_string
.trim_start_matches('[')
.trim_end_matches(']')
.starts_with(
entry_header_string
.trim_start_matches('[')
.trim_end_matches(']'),
)
{
entry.next_entry = Some(n.text_range().start());
found = true;
break;
}
}
}
if !found {
entry.next_entry = end;
}
}
if let TABLE_ARRAY_HEADER = entry.syntax.kind() {
let mut found = false;
let entry_header_string = entry.syntax.to_string();
for n in root_syntax.children() {
if n.text_range().start() < entry.syntax.text_range().end() {
continue;
}
let new_header_string = n.to_string();
if let TABLE_ARRAY_HEADER = n.kind() {
if !new_header_string
.trim_start_matches('[')
.trim_end_matches(']')
.starts_with(
entry_header_string
.trim_start_matches('[')
.trim_end_matches(']'),
)
{
entry.next_entry = Some(n.text_range().start());
found = true;
break;
}
}
}
if !found {
entry.next_entry = end;
}
}
match &mut entry.value {
ValueNode::Table(t) => {
t.next_entry = entry.next_entry;
t.entries.set_table_spans(root_syntax, end);
}
ValueNode::Array(arr) => {
if arr.is_array_of_tables() {
arr.set_table_spans(root_syntax, end)
}
}
_ => {}
}
}
}
fn from_map(map: IndexMap<KeyNode, EntryNode>) -> Self {
Entries(
map.into_iter()
.map(|(k, mut e)| {
e.key = k;
e
})
.collect(),
)
}
fn merge(&mut self, errors: &mut Vec<Error>) {
let mut new_entries: Vec<EntryNode> = Vec::with_capacity(self.0.len());
for mut entry in mem::take(&mut self.0) {
entry.key = entry.key.with_index(0);
let mut should_insert = true;
for existing_entry in &mut new_entries {
match Entries::merge_entry(existing_entry, &entry, errors) {
Ok(merged) => {
if merged {
should_insert = false;
break;
}
}
Err(err) => {
errors.push(err);
should_insert = false;
break;
}
}
}
if should_insert {
entry.value = match entry.value {
ValueNode::Table(mut t) => {
if t.array {
t.array = false;
ValueNode::Array(ArrayNode {
syntax: t.syntax.clone(),
items: vec![ValueNode::Table(t)],
next_header_start: None,
tables: true,
})
} else {
ValueNode::Table(t)
}
}
v => v,
};
new_entries.push(entry);
}
}
self.0 = new_entries;
}
fn normalize(&mut self) {
let mut entries_list = vec![&mut self.0];
while let Some(entries) = entries_list.pop() {
for entry in entries.iter_mut() {
entry.normalize();
match &mut entry.value {
ValueNode::Array(a) => {
let mut inner_arrs = vec![a];
while let Some(arr) = inner_arrs.pop() {
for item in arr.items.iter_mut() {
match item {
ValueNode::Array(a) => {
inner_arrs.push(a);
}
ValueNode::Table(t) => {
entries_list.push(&mut t.entries.0);
}
_ => {}
}
}
}
}
ValueNode::Table(t) => {
entries_list.push(&mut t.entries.0);
}
_ => {}
}
}
}
}
fn merge_entry(
old_entry: &mut EntryNode,
new_entry: &EntryNode,
errors: &mut Vec<Error>,
) -> Result<bool, Error> {
let old_key = old_entry.key.clone();
let new_key = new_entry.key.clone();
if old_key.is_part_of(&new_key) {
match &mut old_entry.value {
ValueNode::Table(t) => {
if t.is_inline() {
return Err(Error::InlineTable {
target: old_entry.key.clone(),
key: new_entry.key.clone(),
});
}
let mut to_insert = new_entry.clone();
to_insert.key = new_key.without_prefix(&old_key);
t.entries.0.push(to_insert);
t.entries.merge(errors);
Ok(true)
}
ValueNode::Array(old_arr) => {
if !old_arr.tables {
return Err(Error::ExpectedTableArray {
target: old_entry.key.clone(),
key: new_entry.key.clone(),
});
}
let mut final_entry = new_entry.clone();
match &mut final_entry.value {
ValueNode::Table(new_t) => {
if old_key.eq_keys(&new_key) && new_t.array {
new_t.array = false;
old_arr.items.push(final_entry.value);
Ok(true)
} else {
match old_arr.items.last_mut().unwrap() {
ValueNode::Table(arr_t) => {
let mut to_insert = new_entry.clone();
to_insert.key = new_key.without_prefix(&old_key);
arr_t.entries.0.push(to_insert);
arr_t.entries.merge(errors);
Ok(true)
}
_ => panic!("expected array of tables"),
}
}
}
ValueNode::Empty => panic!("empty value"),
_ => {
match old_arr.items.last_mut().unwrap() {
ValueNode::Table(arr_t) => {
let mut to_insert = new_entry.clone();
to_insert.key = new_key.without_prefix(&old_key);
arr_t.entries.0.push(to_insert);
arr_t.entries.merge(errors);
Ok(true)
}
_ => panic!("expected array of tables"),
}
}
}
}
ValueNode::Empty => panic!("empty value"),
_ => Err(Error::ExpectedTable {
target: old_entry.key.clone(),
key: new_entry.key.clone(),
}),
}
} else if new_key.is_part_of(&old_key) {
let mut new_old = new_entry.clone();
match Entries::merge_entry(&mut new_old, &old_entry, errors) {
Ok(merged) => {
if merged {
*old_entry = new_old;
Ok(true)
} else {
Ok(false)
}
}
Err(e) => Err(e),
}
} else {
let common_count = old_entry.key().common_prefix_count(new_entry.key());
if common_count > 0 {
let common_prefix = old_entry.key().clone().outer(common_count);
let mut a = old_entry.clone();
a.key = a.key.without_prefix(&common_prefix);
let mut b = new_entry.clone();
b.key = b.key.without_prefix(&common_prefix);
old_entry.key = common_prefix;
old_entry.value = ValueNode::Table(TableNode {
syntax: old_entry.syntax.clone(),
next_entry: None,
array: false,
pseudo: true,
entries: Entries(vec![a, b]),
});
Ok(true)
} else {
Ok(false)
}
}
}
}
impl IntoIterator for Entries {
type Item = EntryNode;
type IntoIter = std::vec::IntoIter<EntryNode>;
fn into_iter(self) -> std::vec::IntoIter<EntryNode> {
self.0.into_iter()
}
}
impl FromIterator<EntryNode> for Entries {
fn from_iter<T: IntoIterator<Item = EntryNode>>(iter: T) -> Self {
let i = iter.into_iter();
let hint = i.size_hint();
let len = match hint.1 {
None => hint.0,
Some(l) => l,
};
let mut entries = Vec::with_capacity(len);
for entry in i {
entries.push(entry);
}
Entries(entries)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArrayNode {
syntax: SyntaxNode,
tables: bool,
items: Vec<ValueNode>,
next_header_start: Option<TextSize>,
}
impl ArrayNode {
pub fn items(&self) -> &[ValueNode] {
&self.items
}
pub fn into_items(self) -> Vec<ValueNode> {
self.items
}
pub fn text_range(&self) -> TextRange {
let mut range = self.syntax.text_range();
for item in &self.items {
range = range.cover(item.text_range())
}
if let Some(r) = self.next_header_start.as_ref() {
range = range.cover_offset(*r);
}
range
}
pub fn is_array_of_tables(&self) -> bool {
self.tables
}
fn set_table_spans(&mut self, root_syntax: &SyntaxNode, end: Option<TextSize>) {
if !self.tables {
return;
}
for value in &mut self.items {
let table = match value {
ValueNode::Table(t) => t,
_ => panic!("expected table"),
};
let mut found = false;
let entry_header_string = table.syntax.to_string();
for n in root_syntax.children() {
if n.text_range().start() < table.syntax.text_range().end() {
continue;
}
let new_header_string = n.to_string();
if let TABLE_ARRAY_HEADER = n.kind() {
if !new_header_string
.trim_start_matches('[')
.trim_end_matches(']')
.starts_with(
entry_header_string
.trim_start_matches('[')
.trim_end_matches(']'),
)
|| new_header_string == entry_header_string
{
table.next_entry = Some(n.text_range().start());
found = true;
break;
}
}
}
if !found {
table.next_entry = end;
}
table.entries.set_table_spans(root_syntax, end);
}
}
}
impl Common for ArrayNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
let mut range = self.syntax.text_range();
for item in &self.items {
range = range.cover(item.text_range())
}
if let Some(r) = self.next_header_start.as_ref() {
range = range.cover_offset(*r);
}
range
}
}
impl Cast for ArrayNode {
fn cast(syntax: SyntaxElement) -> Option<Self> {
match syntax.kind() {
ARRAY => Some(Self {
items: syntax
.as_node()
.unwrap()
.children_with_tokens()
.filter_map(Cast::cast)
.collect(),
next_header_start: None,
tables: false,
syntax: syntax.into_node().unwrap(),
}),
TABLE_ARRAY_HEADER => Some(Self {
items: Vec::new(),
next_header_start: None,
tables: false,
syntax: syntax.into_node().unwrap(),
}),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EntryNode {
syntax: SyntaxNode,
key: KeyNode,
value: ValueNode,
next_entry: Option<TextSize>,
}
impl EntryNode {
pub fn key(&self) -> &KeyNode {
&self.key
}
pub fn value(&self) -> &ValueNode {
&self.value
}
pub fn into_value(self) -> ValueNode {
self.value
}
pub fn token_eq_text_range(&self) -> Option<TextRange> {
self.syntax.children_with_tokens().find_map(|t| {
if t.kind() == EQ {
Some(t.text_range())
} else {
None
}
})
}
fn normalize(&mut self) {
while self.key.key_count() > 1 {
let new_key = self.key.clone().prefix();
let inner_key = self.key.clone().last();
let value = mem::take(&mut self.value);
let is_array_table = match &value {
ValueNode::Table(t) => t.is_part_of_array(),
_ => false,
};
let inner_entry = EntryNode {
syntax: self.syntax.clone(),
key: inner_key.clone(),
next_entry: None,
value,
};
let mut entries = Entries(Vec::with_capacity(1));
entries.0.push(inner_entry);
self.value = ValueNode::Table(TableNode {
syntax: inner_key.syntax.clone(),
array: is_array_table,
next_entry: None,
pseudo: true,
entries,
});
self.key = new_key;
}
}
}
impl Common for EntryNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
let r = self.syntax.text_range();
match self.next_entry {
Some(next_entry) => r.cover_offset(next_entry),
None => r,
}
}
}
impl Cast for EntryNode {
fn cast(element: SyntaxElement) -> Option<Self> {
if element.kind() != ENTRY {
None
} else {
let key = element
.as_node()
.unwrap()
.first_child_or_token()
.and_then(Cast::cast);
key.as_ref()?;
let val = element
.as_node()
.unwrap()
.first_child()
.and_then(|k| k.next_sibling())
.map(rowan::NodeOrToken::Node)
.and_then(Cast::cast)
.unwrap_or(ValueNode::Invalid(None));
Some(Self {
key: key.unwrap(),
value: val,
next_entry: None,
syntax: element.into_node().unwrap(),
})
}
}
}
#[derive(Debug, Clone)]
pub struct KeyNode {
syntax: SyntaxNode,
mask_left: usize,
mask_right: usize,
mask_visible: usize,
idents: Rc<Vec<SyntaxToken>>,
index: usize,
}
impl KeyNode {
pub fn idents(&self) -> impl Iterator<Item = &SyntaxToken> {
self.idents[..self.idents.len() - self.mask_right]
.iter()
.skip(self.mask_left)
}
pub fn key_count(&self) -> usize {
self.mask_visible
}
pub fn keys_str(&self) -> impl Iterator<Item = &str> {
self.idents().map(|t| {
let mut s = t.text().as_str();
if s.starts_with('\"') || s.starts_with('\'') {
s = &s[1..s.len() - 1];
}
s
})
}
pub fn full_key_string(&self) -> String {
let s: Vec<String> = self.keys_str().map(|s| s.to_string()).collect();
s.join(".")
}
pub fn is_part_of(&self, other: &KeyNode) -> bool {
if other.mask_visible < self.mask_visible {
return false;
}
for (a, b) in self.keys_str().zip(other.keys_str()) {
if a != b {
return false;
}
}
true
}
pub fn contains(&self, other: &KeyNode) -> bool {
other.is_part_of(self)
}
pub fn outer(mut self, n: usize) -> Self {
let skip = usize::min(
self.mask_visible - 1,
self.mask_visible.checked_sub(n).unwrap_or_default(),
);
self.mask_right += skip;
self.mask_visible -= skip;
self
}
pub fn inner(mut self, n: usize) -> Self {
let skip = usize::min(self.mask_visible - 1, n);
self.mask_left += skip;
self.mask_visible -= skip;
self
}
pub fn common_prefix_count(&self, other: &KeyNode) -> usize {
let mut count = 0;
for (a, b) in self.keys_str().zip(other.keys_str()) {
if a != b {
break;
}
count += 1;
}
count
}
pub fn eq_keys(&self, other: &KeyNode) -> bool {
self.key_count() == other.key_count() && self.is_part_of(other)
}
fn with_prefix(mut self, other: &KeyNode) -> Self {
let mut new_idents = (*self.idents).clone();
new_idents.truncate(new_idents.len() - self.mask_right);
new_idents.drain(0..self.mask_left);
new_idents.splice(0..0, other.idents().cloned());
self.mask_visible = new_idents.len();
self.idents = Rc::new(new_idents);
self.mask_left = 0;
self.mask_right = 0;
self.index = other.index;
self
}
fn without_prefix(self, other: &KeyNode) -> Self {
let count = self.common_prefix_count(other);
if count > 0 {
self.inner(count)
} else {
self
}
}
fn with_index(mut self, index: usize) -> Self {
self.index = index;
self
}
fn prefix(self) -> Self {
let count = self.key_count();
self.outer(count - 1)
}
fn last(self) -> Self {
let count = self.key_count();
self.inner(count)
}
}
impl Common for KeyNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
self.idents()
.fold(self.idents().next().unwrap().text_range(), |r, t| {
r.cover(t.text_range())
})
}
}
impl PartialEq for KeyNode {
fn eq(&self, other: &Self) -> bool {
self.eq_keys(other) && self.index == other.index
}
}
impl Eq for KeyNode {}
impl Hash for KeyNode {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
for s in self.keys_str() {
s.hash(state)
}
self.index.hash(state)
}
}
impl core::fmt::Display for KeyNode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.full_key_string().fmt(f)
}
}
impl Cast for KeyNode {
fn cast(element: SyntaxElement) -> Option<Self> {
if element.kind() != KEY {
None
} else {
element.into_node().and_then(|n| {
let i: Vec<SyntaxToken> = n
.children_with_tokens()
.filter_map(|c| {
if let rowan::NodeOrToken::Token(t) = c {
match t.kind() {
IDENT => Some(t),
_ => None,
}
} else {
None
}
})
.collect();
if i.is_empty() {
return None;
}
Some(Self {
mask_left: 0,
mask_right: 0,
mask_visible: i.len(),
idents: Rc::new(i),
index: 0,
syntax: n,
})
})
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueNode {
Bool(BoolNode),
String(StringNode),
Integer(IntegerNode),
Float(FloatNode),
Array(ArrayNode),
Date(DateNode),
Table(TableNode),
Invalid(Option<SyntaxElement>),
Empty,
}
impl Default for ValueNode {
fn default() -> Self {
ValueNode::Empty
}
}
impl ValueNode {
fn dom_inner(element: SyntaxElement) -> Option<Self> {
match element.kind() {
INLINE_TABLE => Cast::cast(element).map(ValueNode::Table),
ARRAY => Cast::cast(element).map(ValueNode::Array),
BOOL => Cast::cast(element).map(ValueNode::Bool),
STRING | STRING_LITERAL | MULTI_LINE_STRING | MULTI_LINE_STRING_LITERAL => {
Cast::cast(element).map(ValueNode::String)
}
INTEGER | INTEGER_BIN | INTEGER_HEX | INTEGER_OCT => {
Cast::cast(element).map(ValueNode::Integer)
}
FLOAT => Cast::cast(element).map(ValueNode::Float),
DATE => Cast::cast(element).map(ValueNode::Date),
_ => None,
}
}
}
impl Common for ValueNode {
fn syntax(&self) -> SyntaxElement {
match self {
ValueNode::Bool(v) => v.syntax(),
ValueNode::String(v) => v.syntax(),
ValueNode::Integer(v) => v.syntax(),
ValueNode::Float(v) => v.syntax(),
ValueNode::Array(v) => v.syntax(),
ValueNode::Date(v) => v.syntax(),
ValueNode::Table(v) => v.syntax(),
_ => panic!("empty value"),
}
}
fn text_range(&self) -> TextRange {
match self {
ValueNode::Bool(v) => v.text_range(),
ValueNode::String(v) => v.text_range(),
ValueNode::Integer(v) => v.text_range(),
ValueNode::Float(v) => v.text_range(),
ValueNode::Array(v) => v.text_range(),
ValueNode::Date(v) => v.text_range(),
ValueNode::Table(v) => v.text_range(),
ValueNode::Invalid(n) => n.as_ref().map(|n| n.text_range()).unwrap_or_default(),
_ => panic!("empty value"),
}
}
fn is_valid(&self) -> bool {
match self {
ValueNode::Invalid(_) | ValueNode::Empty => false,
_ => true,
}
}
}
impl core::fmt::Display for ValueNode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ValueNode::Bool(v) => v.fmt(f),
ValueNode::String(v) => v.fmt(f),
ValueNode::Integer(v) => v.fmt(f),
ValueNode::Float(v) => v.fmt(f),
ValueNode::Array(v) => v.fmt(f),
ValueNode::Date(v) => v.fmt(f),
ValueNode::Table(v) => v.fmt(f),
_ => panic!("empty value"),
}
}
}
impl Cast for ValueNode {
fn cast(element: SyntaxElement) -> Option<Self> {
if element.kind() != VALUE {
return None;
}
element
.clone()
.into_node()
.and_then(|n| n.first_child_or_token())
.and_then(|c| match c.kind() {
INLINE_TABLE => Cast::cast(c).map(ValueNode::Table),
ARRAY => Cast::cast(c).map(ValueNode::Array),
BOOL => Cast::cast(c).map(ValueNode::Bool),
STRING | STRING_LITERAL | MULTI_LINE_STRING | MULTI_LINE_STRING_LITERAL => {
Cast::cast(c).map(ValueNode::String)
}
INTEGER | INTEGER_BIN | INTEGER_HEX | INTEGER_OCT => {
Cast::cast(c).map(ValueNode::Integer)
}
FLOAT => Cast::cast(c).map(ValueNode::Float),
DATE => Cast::cast(c).map(ValueNode::Date),
_ => None,
})
.or(Some(ValueNode::Invalid(Some(element))))
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum IntegerRepr {
Dec,
Bin,
Oct,
Hex,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IntegerNode {
syntax: SyntaxToken,
repr: IntegerRepr,
}
impl IntegerNode {
pub fn repr(&self) -> IntegerRepr {
self.repr
}
pub fn text_range(&self) -> TextRange {
self.syntax.text_range()
}
}
impl Common for IntegerNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
self.syntax.text_range()
}
}
impl Cast for IntegerNode {
fn cast(element: SyntaxElement) -> Option<Self> {
match element.kind() {
INTEGER => Some(IntegerNode {
syntax: element.into_token().unwrap(),
repr: IntegerRepr::Dec,
}),
INTEGER_BIN => Some(IntegerNode {
syntax: element.into_token().unwrap(),
repr: IntegerRepr::Bin,
}),
INTEGER_HEX => Some(IntegerNode {
syntax: element.into_token().unwrap(),
repr: IntegerRepr::Hex,
}),
INTEGER_OCT => Some(IntegerNode {
syntax: element.into_token().unwrap(),
repr: IntegerRepr::Oct,
}),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum StringKind {
Basic,
MultiLine,
Literal,
MultiLineLiteral,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StringNode {
syntax: SyntaxToken,
kind: StringKind,
content: String,
}
impl StringNode {
pub fn string_kind(&self) -> StringKind {
self.kind
}
pub fn content(&self) -> &str {
&self.content
}
pub fn into_content(self) -> String {
self.content
}
}
impl Common for StringNode {
fn syntax(&self) -> SyntaxElement {
self.syntax.clone().into()
}
fn text_range(&self) -> TextRange {
self.syntax.text_range()
}
}
impl Cast for StringNode {
fn cast(element: SyntaxElement) -> Option<Self> {
match element.kind() {
STRING => Some(StringNode {
kind: StringKind::Basic,
content: match unescape(
element
.as_token()
.unwrap()
.text()
.as_str()
.remove_prefix(r#"""#)
.remove_suffix(r#"""#),
) {
Ok(s) => s,
Err(_) => return None,
},
syntax: element.into_token().unwrap(),
}),
MULTI_LINE_STRING => Some(StringNode {
kind: StringKind::MultiLine,
content: match unescape(
element
.as_token()
.unwrap()
.text()
.as_str()
.remove_prefix(r#"""""#)
.remove_suffix(r#"""""#)
.remove_prefix("\n"),
) {
Ok(s) => s,
Err(_) => return None,
},
syntax: element.into_token().unwrap(),
}),
STRING_LITERAL => Some(StringNode {
kind: StringKind::Literal,
content: element
.as_token()
.unwrap()
.text()
.as_str()
.remove_prefix(r#"'"#)
.remove_suffix(r#"'"#)
.into(),
syntax: element.into_token().unwrap(),
}),
MULTI_LINE_STRING_LITERAL => Some(StringNode {
kind: StringKind::MultiLineLiteral,
content: element
.as_token()
.unwrap()
.text()
.as_str()
.remove_prefix(r#"'''"#)
.remove_suffix(r#"'''"#)
.remove_prefix("\n")
.into(),
syntax: element.into_token().unwrap(),
}),
_ => None,
}
}
}
dom_primitives!(
BOOL => BoolNode,
FLOAT => FloatNode,
DATE => DateNode
);
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Error {
DuplicateKey { first: KeyNode, second: KeyNode },
ExpectedTableArray { target: KeyNode, key: KeyNode },
ExpectedTable { target: KeyNode, key: KeyNode },
TopLevelTableDefined { table: KeyNode, key: KeyNode },
InlineTable { target: KeyNode, key: KeyNode },
Spanned { range: TextRange, message: String },
Generic(String),
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::DuplicateKey { first, second } => write!(
f,
"duplicate keys: \"{}\" ({:?}) and \"{}\" ({:?})",
&first.full_key_string(),
&first.text_range(),
&second.full_key_string(),
&second.text_range()
),
Error::ExpectedTable { target, key } => write!(
f,
"Expected \"{}\" ({:?}) to be a table, but it is not, required by \"{}\" ({:?})",
&target.full_key_string(),
&target.text_range(),
&key.full_key_string(),
&key.text_range()
),
Error::TopLevelTableDefined { table, key } => write!(
f,
"full table definition \"{}\" ({:?}) conflicts with dotted keys \"{}\" ({:?})",
&table.full_key_string(),
&table.text_range(),
&key.full_key_string(),
&key.text_range()
),
Error::ExpectedTableArray { target, key } => write!(
f,
"\"{}\" ({:?}) conflicts with array of tables: \"{}\" ({:?})",
&target.full_key_string(),
&target.text_range(),
&key.full_key_string(),
&key.text_range()
),
Error::InlineTable { target, key } => write!(
f,
"inline tables cannot be modified: \"{}\" ({:?}), modification attempted here: \"{}\" ({:?})",
&target.full_key_string(),
&target.text_range(),
&key.full_key_string(),
&key.text_range()
),
Error::Spanned { range, message } => write!(f, "{} ({:?})", message, range),
Error::Generic(s) => s.fmt(f),
}
}
}
impl std::error::Error for Error {}
#[test]
fn asd() {
let src = r#"
asd.bsd.csd.dsd.esd.fsd = 1
"#;
let _p = crate::parser::parse(src).into_dom();
}