use std::{
cmp::Ordering,
fmt::Display,
ops::{Index, IndexMut},
slice::{Iter, IterMut},
str::FromStr,
};
#[cfg(feature = "span")]
use miette::SourceSpan;
use crate::{
v2_parser, FormatConfig, KdlDocument, KdlDocumentFormat, KdlEntry, KdlError, KdlIdentifier,
KdlValue,
};
#[derive(Debug, Clone, Eq)]
pub struct KdlNode {
pub(crate) ty: Option<KdlIdentifier>,
pub(crate) name: KdlIdentifier,
pub(crate) entries: Vec<KdlEntry>,
pub(crate) children: Option<KdlDocument>,
pub(crate) format: Option<KdlNodeFormat>,
#[cfg(feature = "span")]
pub(crate) span: SourceSpan,
}
impl PartialEq for KdlNode {
fn eq(&self, other: &Self) -> bool {
self.ty == other.ty
&& self.name == other.name
&& self.entries == other.entries
&& self.children == other.children
&& self.format == other.format
}
}
impl std::hash::Hash for KdlNode {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.ty.hash(state);
self.name.hash(state);
self.entries.hash(state);
self.children.hash(state);
self.format.hash(state);
}
}
impl KdlNode {
pub fn new(name: impl Into<KdlIdentifier>) -> Self {
Self {
name: name.into(),
ty: None,
entries: Vec::new(),
children: None,
format: Some(KdlNodeFormat {
trailing: "\n".into(),
..Default::default()
}),
#[cfg(feature = "span")]
span: SourceSpan::from(0..0),
}
}
pub fn name(&self) -> &KdlIdentifier {
&self.name
}
pub fn name_mut(&mut self) -> &mut KdlIdentifier {
&mut self.name
}
pub fn set_name(&mut self, name: impl Into<KdlIdentifier>) {
self.name = name.into();
}
#[cfg(feature = "span")]
pub fn span(&self) -> SourceSpan {
self.span
}
#[cfg(feature = "span")]
pub fn set_span(&mut self, span: impl Into<SourceSpan>) {
self.span = span.into();
}
pub fn ty(&self) -> Option<&KdlIdentifier> {
self.ty.as_ref()
}
pub fn ty_mut(&mut self) -> &mut Option<KdlIdentifier> {
&mut self.ty
}
pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
self.ty = Some(ty.into());
}
pub fn entries(&self) -> &[KdlEntry] {
&self.entries
}
pub fn entries_mut(&mut self) -> &mut Vec<KdlEntry> {
&mut self.entries
}
pub fn clear_format(&mut self) {
self.format = None;
}
pub fn clear_format_recursive(&mut self) {
self.clear_format();
self.name.clear_format();
if let Some(children) = &mut self.children {
children.clear_format_recursive();
}
for entry in self.entries.iter_mut() {
entry.clear_format();
}
}
pub fn entry(&self, key: impl Into<NodeKey>) -> Option<&KdlEntry> {
self.entry_impl(key.into())
}
fn entry_impl(&self, key: NodeKey) -> Option<&KdlEntry> {
match key {
NodeKey::Key(key) => {
let mut current = None;
for entry in &self.entries {
if entry.name.is_some()
&& entry.name.as_ref().map(|i| i.value()) == Some(key.value())
{
current = Some(entry);
}
}
current
}
NodeKey::Index(idx) => {
let mut current_idx = 0;
for entry in &self.entries {
if entry.name.is_none() {
if current_idx == idx {
return Some(entry);
}
current_idx += 1;
}
}
None
}
}
}
pub fn entry_mut(&mut self, key: impl Into<NodeKey>) -> Option<&mut KdlEntry> {
self.entry_mut_impl(key.into())
}
fn entry_mut_impl(&mut self, key: NodeKey) -> Option<&mut KdlEntry> {
match key {
NodeKey::Key(key) => {
let mut current = None;
for entry in &mut self.entries {
if entry.name.is_some()
&& entry.name.as_ref().map(|i| i.value()) == Some(key.value())
{
current = Some(entry);
}
}
current
}
NodeKey::Index(idx) => {
let mut current_idx = 0;
for entry in &mut self.entries {
if entry.name.is_none() {
if current_idx == idx {
return Some(entry);
}
current_idx += 1;
}
}
None
}
}
}
pub fn children(&self) -> Option<&KdlDocument> {
self.children.as_ref()
}
pub fn children_mut(&mut self) -> &mut Option<KdlDocument> {
&mut self.children
}
pub fn set_children(&mut self, children: KdlDocument) {
self.children = Some(children);
}
pub fn clear_children(&mut self) {
self.children = None;
}
pub fn ensure_children(&mut self) -> &mut KdlDocument {
if self.children.is_none() {
self.children = Some(KdlDocument::new());
}
self.children_mut().as_mut().unwrap()
}
pub fn format(&self) -> Option<&KdlNodeFormat> {
self.format.as_ref()
}
pub fn format_mut(&mut self) -> Option<&mut KdlNodeFormat> {
self.format.as_mut()
}
pub fn set_format(&mut self, format: KdlNodeFormat) {
self.format = Some(format);
}
pub fn autoformat(&mut self) {
self.autoformat_config(&FormatConfig::default());
}
pub fn autoformat_no_comments(&mut self) {
self.autoformat_config(&FormatConfig {
no_comments: true,
..Default::default()
});
}
pub fn autoformat_config(&mut self, config: &FormatConfig<'_>) {
if let Some(KdlNodeFormat {
leading,
before_terminator,
terminator,
trailing,
before_children,
..
}) = self.format_mut()
{
crate::fmt::autoformat_leading(leading, config);
crate::fmt::autoformat_trailing(before_terminator, config.no_comments);
crate::fmt::autoformat_trailing(trailing, config.no_comments);
*trailing = trailing.trim().into();
if !terminator.starts_with('\n') {
*terminator = "\n".into();
}
if let Some(c) = trailing.chars().next() {
if !c.is_whitespace() {
trailing.insert(0, ' ');
}
}
*before_children = " ".into();
} else {
self.set_format(KdlNodeFormat {
terminator: "\n".into(),
..Default::default()
})
}
self.name.clear_format();
if let Some(ty) = self.ty.as_mut() {
ty.clear_format()
}
for entry in &mut self.entries {
if config.entry_autoformate_keep {
entry.keep_format();
}
entry.autoformat();
}
if let Some(children) = self.children.as_mut() {
children.autoformat_config(&FormatConfig {
indent_level: config.indent_level + 1,
..*config
});
if let Some(KdlDocumentFormat { leading, trailing }) = children.format_mut() {
*leading = leading.trim().into();
leading.push('\n');
for _ in 0..config.indent_level {
trailing.push_str(config.indent);
}
}
}
}
pub fn parse(s: &str) -> Result<Self, KdlError> {
#[cfg(not(feature = "v1-fallback"))]
{
v2_parser::try_parse(v2_parser::padded_node, s)
}
#[cfg(feature = "v1-fallback")]
{
v2_parser::try_parse(v2_parser::padded_node, s)
.or_else(|e| KdlNode::parse_v1(s).map_err(|_| e))
}
}
#[cfg(feature = "v1")]
pub fn parse_v1(s: &str) -> Result<Self, KdlError> {
let ret: Result<kdlv1::KdlNode, kdlv1::KdlError> = s.parse();
ret.map(|x| x.into()).map_err(|e| e.into())
}
pub fn ensure_v2(&mut self) {
self.ty = self.ty.take().map(|ty| ty.value().into());
let v2_name: KdlIdentifier = self.name.value().into();
self.name = v2_name;
for entry in self.iter_mut() {
entry.ensure_v2();
}
self.children = self.children.take().map(|mut doc| {
doc.ensure_v2();
doc
});
}
#[cfg(feature = "v1")]
pub fn ensure_v1(&mut self) {
self.ty = self.ty.take().map(|ty| {
let v1_name: kdlv1::KdlIdentifier = ty.value().into();
v1_name.into()
});
let v1_name: kdlv1::KdlIdentifier = self.name.value().into();
self.name = v1_name.into();
for entry in self.iter_mut() {
entry.ensure_v1();
}
self.children = self.children.take().map(|mut children| {
children.ensure_v1();
children
});
}
}
#[cfg(feature = "v1")]
impl From<kdlv1::KdlNode> for KdlNode {
fn from(value: kdlv1::KdlNode) -> Self {
let terminator = value
.trailing()
.map(|t| if t.contains(';') { ";" } else { "\n" })
.unwrap_or("\n");
let trailing = value.trailing().map(|t| {
if t.contains(';') {
t.replace(';', "")
} else {
let t = t.replace("\r\n", "\n");
let t = t
.chars()
.map(|c| {
if v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)) {
'\n'
} else {
c
}
})
.collect::<String>();
if terminator == ";" {
t
} else {
t.replacen('\n', "", 1)
}
}
});
KdlNode {
ty: value.ty().map(|x| x.clone().into()),
name: value.name().clone().into(),
entries: value.entries().iter().map(|x| x.clone().into()).collect(),
children: value.children().map(|x| x.clone().into()),
format: Some(KdlNodeFormat {
leading: value.leading().unwrap_or("").into(),
before_ty_name: "".into(),
after_ty_name: "".into(),
after_ty: "".into(),
before_children: value.before_children().unwrap_or("").into(),
before_terminator: "".into(),
terminator: terminator.into(),
trailing: trailing.unwrap_or_else(|| "".into()),
}),
#[cfg(feature = "span")]
span: SourceSpan::new(value.span().offset().into(), value.span().len()),
}
}
}
#[derive(Debug)]
pub struct EntryIter<'a>(Iter<'a, KdlEntry>);
impl<'a> Iterator for EntryIter<'a> {
type Item = &'a KdlEntry;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[derive(Debug)]
pub struct EntryIterMut<'a>(IterMut<'a, KdlEntry>);
impl<'a> Iterator for EntryIterMut<'a> {
type Item = &'a mut KdlEntry;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[derive(Debug)]
pub struct ChildrenIter<'a>(Option<Iter<'a, KdlNode>>);
impl<'a> Iterator for ChildrenIter<'a> {
type Item = &'a KdlNode;
fn next(&mut self) -> Option<Self::Item> {
self.0.as_mut().and_then(|x| x.next())
}
}
#[derive(Debug)]
pub struct ChildrenIterMut<'a>(Option<IterMut<'a, KdlNode>>);
impl<'a> Iterator for ChildrenIterMut<'a> {
type Item = &'a mut KdlNode;
fn next(&mut self) -> Option<Self::Item> {
self.0.as_mut().and_then(|x| x.next())
}
}
impl KdlNode {
pub fn iter(&self) -> EntryIter<'_> {
EntryIter(self.entries.iter())
}
pub fn iter_mut(&mut self) -> EntryIterMut<'_> {
EntryIterMut(self.entries.iter_mut())
}
pub fn iter_children(&self) -> ChildrenIter<'_> {
ChildrenIter(self.children.as_ref().map(|x| x.nodes.iter()))
}
pub fn iter_children_mut(&mut self) -> ChildrenIterMut<'_> {
ChildrenIterMut(self.children.as_mut().map(|x| x.nodes.iter_mut()))
}
pub fn get(&self, key: impl Into<NodeKey>) -> Option<&KdlValue> {
self.entry_impl(key.into()).map(|e| &e.value)
}
pub fn get_mut(&mut self, key: impl Into<NodeKey>) -> Option<&mut KdlValue> {
self.entry_mut_impl(key.into()).map(|e| &mut e.value)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn push(&mut self, entry: impl Into<KdlEntry>) {
self.entries.push(entry.into());
}
pub fn insert(
&mut self,
key: impl Into<NodeKey>,
entry: impl Into<KdlEntry>,
) -> Option<KdlEntry> {
self.insert_impl(key.into(), entry.into())
}
fn insert_impl(&mut self, key: NodeKey, mut entry: KdlEntry) -> Option<KdlEntry> {
match key {
NodeKey::Key(ref key_val) => {
if entry.name.is_none() {
entry.name = Some(key_val.clone());
}
if entry.name.as_ref().map(|i| i.value()) != Some(key_val.value()) {
panic!("Property name mismatch");
}
if let Some(existing) = self.entry_mut(key) {
std::mem::swap(existing, &mut entry);
Some(entry)
} else {
self.entries.push(entry);
None
}
}
NodeKey::Index(idx) => {
if entry.name.is_some() {
panic!("Cannot insert property with name under a numerical key");
}
let mut current_idx = 0;
for (idx_existing, existing) in self.entries.iter().enumerate() {
if existing.name.is_none() {
if current_idx == idx {
self.entries.insert(idx_existing, entry);
return None;
}
current_idx += 1;
}
}
if idx > current_idx {
panic!("Insertion index (is {idx}) should be <= len (is {current_idx})");
} else {
self.entries.push(entry);
None
}
}
}
}
pub fn remove(&mut self, key: impl Into<NodeKey>) -> Option<KdlEntry> {
self.remove_impl(key.into())
}
fn remove_impl(&mut self, key: NodeKey) -> Option<KdlEntry> {
match key {
NodeKey::Key(key) => {
for (idx, entry) in self.entries.iter().enumerate() {
if entry.name.is_some() && entry.name.as_ref() == Some(&key) {
return Some(self.entries.remove(idx));
}
}
None
}
NodeKey::Index(idx) => {
let mut current_idx = 0;
for (idx_entry, entry) in self.entries.iter().enumerate() {
if entry.name.is_none() {
if current_idx == idx {
return Some(self.entries.remove(idx_entry));
}
current_idx += 1;
}
}
panic!(
"removal index (is {idx}) should be < number of index entries (is {current_idx})"
);
}
}
}
pub fn retain<F>(&mut self, keep: F)
where
F: FnMut(&KdlEntry) -> bool,
{
self.entries.retain(keep)
}
pub fn sort_by<F>(&mut self, compare: F)
where
F: FnMut(&KdlEntry, &KdlEntry) -> Ordering,
{
self.entries.sort_by(compare)
}
pub fn sort_by_key<K, F>(&mut self, f: F)
where
F: FnMut(&KdlEntry) -> K,
K: Ord,
{
self.entries.sort_by_key(f)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NodeKey {
Key(KdlIdentifier),
Index(usize),
}
impl From<&str> for NodeKey {
fn from(key: &str) -> Self {
Self::Key(key.into())
}
}
impl From<String> for NodeKey {
fn from(key: String) -> Self {
Self::Key(key.into())
}
}
impl From<usize> for NodeKey {
fn from(key: usize) -> Self {
Self::Index(key)
}
}
impl Index<usize> for KdlNode {
type Output = KdlValue;
fn index(&self, index: usize) -> &Self::Output {
self.get(index).expect("Argument out of range.")
}
}
impl IndexMut<usize> for KdlNode {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.get_mut(index).expect("Argument out of range.")
}
}
impl Index<&str> for KdlNode {
type Output = KdlValue;
fn index(&self, key: &str) -> &Self::Output {
self.get(key).expect("No such property.")
}
}
impl IndexMut<&str> for KdlNode {
fn index_mut(&mut self, key: &str) -> &mut Self::Output {
if self.get(key).is_none() {
self.push((key, KdlValue::Null));
}
self.get_mut(key).expect("Something went wrong.")
}
}
impl FromStr for KdlNode {
type Err = KdlError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
v2_parser::try_parse(v2_parser::padded_node, input)
}
}
impl Display for KdlNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.stringify(f, 0)
}
}
impl KdlNode {
pub(crate) fn stringify(
&self,
f: &mut std::fmt::Formatter<'_>,
indent: usize,
) -> std::fmt::Result {
if let Some(KdlNodeFormat { leading, .. }) = self.format() {
write!(f, "{leading}")?;
} else {
write!(f, "{:indent$}", "", indent = indent)?;
}
if let Some(ty) = &self.ty {
write!(f, "({ty})")?;
}
write!(f, "{}", self.name)?;
let mut space_before_children = true;
for entry in &self.entries {
if entry.format().is_none() {
write!(f, " ")?;
}
write!(f, "{entry}")?;
space_before_children = entry.format().is_none();
}
if let Some(children) = &self.children {
if let Some(KdlNodeFormat {
before_children, ..
}) = self.format()
{
write!(f, "{before_children}")?;
} else if space_before_children {
write!(f, " ")?;
}
write!(f, "{{")?;
if children.format().is_none() {
writeln!(f)?;
}
children.stringify(f, indent + 4)?;
if children.format().is_none() {
write!(f, "{:indent$}", "", indent = indent)?;
}
write!(f, "}}")?;
}
if let Some(KdlNodeFormat {
before_terminator,
terminator,
trailing,
..
}) = self.format()
{
write!(f, "{before_terminator}{terminator}{trailing}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Default, Hash, Eq, PartialEq)]
pub struct KdlNodeFormat {
pub leading: String,
pub before_ty_name: String,
pub after_ty_name: String,
pub after_ty: String,
pub before_children: String,
pub before_terminator: String,
pub terminator: String,
pub trailing: String,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn canonical_clear_fmt() -> miette::Result<()> {
let mut left_node: KdlNode = r#"node /-"commented" param_name=103.000 {
// This is a nested node
nested 1 2 3
}"#
.parse()?;
let mut right_node: KdlNode = "node param_name=103.0 { nested 1 2 3; }".parse()?;
assert_ne!(left_node, right_node);
left_node.clear_format_recursive();
right_node.clear_format_recursive();
assert_eq!(left_node.to_string(), right_node.to_string());
Ok(())
}
#[test]
fn parsing() -> miette::Result<()> {
let node: KdlNode = "\n\t (\"ty\")\"node\" 0xDEADbeef;\n".parse()?;
assert_eq!(node.ty(), Some(&"\"ty\"".parse()?));
assert_eq!(node.name(), &"\"node\"".parse()?);
assert_eq!(node.entry(0), Some(&" 0xDEADbeef".parse()?));
assert_eq!(
node.format(),
Some(&KdlNodeFormat {
leading: "\n\t ".into(),
before_terminator: "".into(),
terminator: ";".into(),
trailing: "\n".into(),
before_ty_name: "".into(),
after_ty_name: "".into(),
after_ty: "".into(),
before_children: "".into(),
})
);
let node: KdlNode = r#"node test {
link "blah" anything=self
}
"#
.parse::<KdlNode>()?;
assert_eq!(node.entry(0), Some(&" test".parse()?));
assert_eq!(node.children().unwrap().nodes().len(), 1);
Ok(())
}
#[test]
fn indexing() {
let mut node = KdlNode::new("foo");
node.push("bar");
node["foo"] = 1.into();
assert_eq!(node[0], "bar".into());
assert_eq!(node["foo"], 1.into());
node[0] = false.into();
node["foo"] = KdlValue::Null;
assert_eq!(node[0], false.into());
assert_eq!(node["foo"], KdlValue::Null);
node.entries_mut().push(KdlEntry::new_prop("x", 1));
node.entries_mut().push(KdlEntry::new_prop("x", 2));
assert_eq!(&node["x"], &2.into())
}
#[test]
fn insertion() {
let mut node = KdlNode::new("foo");
node.push("pos0");
node.insert("keyword", 6.0);
node.push("pos1");
assert_eq!(node.entries().len(), 3);
node.insert(0, "inserted0");
node.insert(2, "inserted1");
assert_eq!(node.entries().len(), 5);
assert_eq!(node[0], "inserted0".into());
assert_eq!(node[1], "pos0".into());
assert_eq!(node[2], "inserted1".into());
assert_eq!(node[3], "pos1".into());
}
#[test]
fn removal() {
let mut node = KdlNode::new("foo");
node.push("pos0");
node.insert("keyword", 6.0);
node.push("pos1");
assert_eq!(node.entries().len(), 3);
node.remove(1);
assert_eq!(node.entries().len(), 2, "index removal should succeed");
assert!(
node.get("keyword").is_some(),
"keyword property should not be removed by index removal"
);
node.remove("not an existing keyword");
assert_eq!(node.entries().len(), 2, "key removal should not succeed");
node.remove("keyword");
assert_eq!(node.entries().len(), 1, "key removal should succeed");
node.remove(0);
assert_eq!(node.entries().len(), 0, "index removal should suceed");
}
#[test]
#[should_panic(expected = "removal index (is 0) should be < number of index entries (is 0)")]
fn remove_panic() {
let mut node = KdlNode::new("foo");
node.push("pos0");
node.insert("keyword", 6.0);
node.remove(0);
assert_eq!(node.entries().len(), 1, "key removal should succeed");
node.remove(0); }
}