Compare commits

...

4 Commits

Author SHA1 Message Date
Kat Marchán d52d1702a8
chore: Release 2026-05-30 15:13:32 -07:00
Kat Marchán 30722e70f0 docs: update changelog 2026-05-30 15:13:10 -07:00
Kat Marchán e81b148201
feat(serde): Add support for flags and #rest (#163)
Fixes: https://github.com/kdl-org/kdl-rs/issues/161
2026-05-30 15:07:41 -07:00
Horu f824881cab
feat(serde): add diagnostic info to error (#162) 2026-05-30 12:52:24 -07:00
9 changed files with 762 additions and 175 deletions

View File

@ -1,5 +1,13 @@
# `kdl` Release Changelog
<a name="6.7.0"></a>
## 6.7.0 (2026-05-30)
### Features
* **serde:** add diagnostic info to error (#162) ([f824881c](https://github.com/kdl-org/kdl-rs/commit/f824881cabce33c2bbcfc1cd51862977c59dd8f4))
* **serde:** Add support for flags and #rest (#163) ([e81b1482](https://github.com/kdl-org/kdl-rs/commit/e81b148201ad0f1fda58e2b327394c7ba0ca556b))
<a name="6.6.1"></a>
## 6.6.1 (2026-05-29)

6
Cargo.lock generated
View File

@ -393,7 +393,7 @@ dependencies = [
[[package]]
name = "kdl"
version = "6.6.1"
version = "6.7.0"
dependencies = [
"kdl 4.7.1",
"miette 7.6.0",
@ -406,10 +406,10 @@ dependencies = [
[[package]]
name = "kdl-lsp"
version = "6.6.1"
version = "6.7.0"
dependencies = [
"dashmap 6.1.0",
"kdl 6.6.1",
"kdl 6.7.0",
"miette 7.6.0",
"ropey",
"tokio",

View File

@ -1,6 +1,6 @@
[package]
name = "kdl"
version = "6.6.1"
version = "6.7.0"
description = "Document-oriented KDL parser and API. Allows formatting/whitespace/comment-preserving parsing and modification of KDL text."
authors = ["Kat Marchán <kzm@zkat.tech>", "KDL Community"]
license = "Apache-2.0"
@ -9,7 +9,7 @@ homepage = "https://kdl.dev"
repository = "https://github.com/kdl-org/kdl-rs"
keywords = ["kdl", "document", "serialization", "config"]
rust-version = "1.95"
edition = "2021"
edition = "2024"
[features]
default = ["span", "serde"]

816
src/de.rs

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
use miette::SourceSpan;
use std::{fmt::Display, str::FromStr};
use crate::{v2_parser, KdlError, KdlIdentifier, KdlValue};
use crate::{KdlError, KdlIdentifier, KdlValue, v2_parser};
/// KDL Entries are the "arguments" to KDL nodes: either a (positional)
/// [`Argument`](https://github.com/kdl-org/kdl/blob/main/SPEC.md#argument) or
@ -230,7 +230,8 @@ impl KdlEntry {
let s = x.value_repr.trim();
// convert raw strings to new format
let s = s.strip_prefix('r').unwrap_or(s);
let s = if crate::value::is_plain_ident(val) {
if crate::value::is_plain_ident(val) {
val.into()
} else if s
.find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
@ -258,8 +259,7 @@ impl KdlEntry {
} else {
// We're all good! Let's move on.
s.to_string()
};
s
}
}
// These have `#` prefixes now. The regular Display impl will
// take care of that.
@ -301,7 +301,8 @@ impl KdlEntry {
} else {
s.to_string()
};
let s = if crate::value::is_plain_ident(val)
if crate::value::is_plain_ident(val)
&& !s.starts_with('\"')
&& !s.starts_with("r#")
{
@ -340,8 +341,7 @@ impl KdlEntry {
} else {
// We're all good! Let's move on.
s.to_string()
};
s
}
}
// No more # prefix for these
KdlValue::Bool(b) => b.to_string(),

View File

@ -2,7 +2,7 @@
use miette::SourceSpan;
use std::{fmt::Display, str::FromStr};
use crate::{v2_parser, KdlError, KdlValue};
use crate::{KdlError, KdlValue, v2_parser};
/// Represents a KDL
/// [Identifier](https://github.com/kdl-org/kdl/blob/main/SPEC.md#identifier).

View File

@ -10,8 +10,8 @@ use std::{
use miette::SourceSpan;
use crate::{
v2_parser, FormatConfig, KdlDocument, KdlDocumentFormat, KdlEntry, KdlError, KdlIdentifier,
KdlValue,
FormatConfig, KdlDocument, KdlDocumentFormat, KdlEntry, KdlError, KdlIdentifier, KdlValue,
v2_parser,
};
/// Represents an individual KDL
@ -291,10 +291,10 @@ impl KdlNode {
if !terminator.starts_with('\n') {
*terminator = "\n".into();
}
if let Some(c) = trailing.chars().next() {
if !c.is_whitespace() {
trailing.insert(0, ' ');
}
if let Some(c) = trailing.chars().next()
&& !c.is_whitespace()
{
trailing.insert(0, ' ');
}
*before_children = " ".into();

View File

@ -7,7 +7,8 @@ use miette::{Severity, SourceSpan};
use num_traits::CheckedMul;
use winnow::{
ascii::{digit1, hex_digit1, oct_digit1, Caseless},
LocatingSlice,
ascii::{Caseless, digit1, hex_digit1, oct_digit1},
combinator::{
alt, cut_err, empty, eof, fail, not, opt, peek, preceded, repeat, repeat_till, separated,
terminated, trace,
@ -16,7 +17,6 @@ use winnow::{
prelude::*,
stream::{AsChar, Location, Recover, Recoverable, Stream},
token::{any, none_of, one_of, take_while},
LocatingSlice,
};
use crate::{
@ -270,10 +270,10 @@ pub(crate) fn document(input: &mut Input<'_>) -> PResult<KdlDocument> {
if badend {
document.parse_next(input)?;
}
if let Some(bom) = bom {
if let Some(fmt) = doc.format_mut() {
fmt.leading = format!("{bom}{}", fmt.leading);
}
if let Some(bom) = bom
&& let Some(fmt) = doc.format_mut()
{
fmt.leading = format!("{bom}{}", fmt.leading);
}
Ok(doc)
}
@ -300,11 +300,11 @@ fn nodes(input: &mut Input<'_>) -> PResult<KdlDocument> {
// If there is a node, let it have the leading format
// This gives more consistent behavior
if let Some(first_node) = ns.get_mut(0) {
if let Some(first_node_format) = first_node.format_mut() {
first_node_format.leading = leading.into();
leading = "";
}
if let Some(first_node) = ns.get_mut(0)
&& let Some(first_node_format) = first_node.format_mut()
{
first_node_format.leading = leading.into();
leading = "";
}
Ok(KdlDocument {
@ -1467,9 +1467,11 @@ mod string_tests {
Some(KdlValue::String("\"\"\"".into()))
);
assert!(string
.parse(new_input("\"\"\"\nfoo\n bar\n baz\n \"\"\""))
.is_err());
assert!(
string
.parse(new_input("\"\"\"\nfoo\n bar\n baz\n \"\"\""))
.is_err()
);
}
#[test]
@ -1507,9 +1509,11 @@ mod string_tests {
.unwrap(),
Some(KdlValue::String("foo\n \\nbar\n baz".into()))
);
assert!(string
.parse(new_input("#\"\"\"\nfoo\n bar\n baz\n \"\"\"#"))
.is_err());
assert!(
string
.parse(new_input("#\"\"\"\nfoo\n bar\n baz\n \"\"\"#"))
.is_err()
);
assert!(string.parse(new_input("#\"\nfoo\nbar\nbaz\n\"#")).is_err());
assert!(string.parse(new_input("\"\nfoo\nbar\nbaz\n\"")).is_err());
@ -1699,9 +1703,11 @@ fn multi_line_comment_test() {
assert!(multi_line_comment.parse(new_input("/*\nfoo*/")).is_ok());
assert!(multi_line_comment.parse(new_input("/*foo\n*/")).is_ok());
assert!(multi_line_comment.parse(new_input("/* foo\n*/")).is_ok());
assert!(multi_line_comment
.parse(new_input("/* /*bar*/ foo\n*/"))
.is_ok());
assert!(
multi_line_comment
.parse(new_input("/* /*bar*/ foo\n*/"))
.is_ok()
);
}
/// slashdash := '/-' (node-space | line-space)*
@ -1724,15 +1730,18 @@ fn slashdash_tests() {
assert!(node_entry.parse(new_input("/-commented tada")).is_ok());
assert!(node.parse(new_input("foo /- { }")).is_ok());
assert!(node.parse(new_input("foo /- { bar }")).is_ok());
assert!(node
.parse(new_input("/- foo bar\nnode /-1 2 { x }"))
.is_ok());
assert!(node
.parse(new_input("/- foo bar\nnode 2 /-3 { x }"))
.is_ok());
assert!(node
.parse(new_input("/- foo bar\nnode /-1 2 /-3 { x }"))
.is_ok());
assert!(
node.parse(new_input("/- foo bar\nnode /-1 2 { x }"))
.is_ok()
);
assert!(
node.parse(new_input("/- foo bar\nnode 2 /-3 { x }"))
.is_ok()
);
assert!(
node.parse(new_input("/- foo bar\nnode /-1 2 /-3 { x }"))
.is_ok()
);
}
/// `number := keyword-number | hex | octal | binary | decimal`
@ -2031,7 +2040,9 @@ macro_rules! impl_from_str_radix {
};
}
impl_from_str_radix!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl_from_str_radix!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
trait MaybeNegatable: CheckedMul {
fn negated(&self) -> Option<Self>;

View File

@ -1,6 +1,6 @@
[package]
name = "kdl-lsp"
version = "6.6.1"
version = "6.7.0"
edition = "2021"
description = "LSP Server for the KDL Document Language"
authors = ["Kat Marchán <kzm@zkat.tech>", "KDL Community"]
@ -13,7 +13,7 @@ rust-version = "1.81"
[dependencies]
miette.workspace = true
kdl = { version = "6.6.1", path = "../../", features = ["span", "v1-fallback"] }
kdl = { version = "6.7.0", path = "../../", features = ["span", "v1-fallback"] }
tower-lsp = "0.20.0"
dashmap = "6.1.0"
ropey = "1.6.1"