Skip to content

Commit 58a40fd

Browse files
authored
fix(compliance): pull in spec test suite and fix issues (#40)
This commit includes a whole bunch of fixes, some of which are significant changes to the parser and some related functionality. But I consider all changes to be bugfixes because they were compliance failures.
1 parent 71df712 commit 58a40fd

File tree

406 files changed

+897
-59
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

406 files changed

+897
-59
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,32 @@ Error:
9696
╰────
9797
help: Floating point numbers must be base 10, and have numbers after the decimal point.
9898
```
99+
100+
### Quirks
101+
102+
#### Properties
103+
104+
Multiple properties with the same name are allowed, and all duplicated
105+
**will be preserved**, meaning those documents will correctly round-trip.
106+
When using `node.get()`/`node["key"]` & company, the _last_ property with
107+
that name's value will be returned.
108+
109+
#### Numbers
110+
111+
KDL itself does not specify a particular representation for numbers and
112+
accepts just about anything valid, no matter how large and how small. This
113+
means a few things:
114+
115+
* Numbers without a decimal point are interpreted as u64.
116+
* Numbers with a decimal point are interpreted as f64.
117+
* Floating point numbers that evaluate to f64::INFINITY or
118+
f64::NEG_INFINITY or NaN will be represented as such in the values,
119+
instead of the original numbers.
120+
* A similar restriction applies to overflowed u64 values.
121+
* The original _representation_ of these numbers will be preserved, unless
122+
you `doc.fmt()`, in which case the original representation will be
123+
thrown away and the actual value will be used when serializing.
124+
99125
### License
100126

101127
The code in this repository is covered by [the Apache-2.0

src/document.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,12 @@ impl KdlDocument {
188188
/// Auto-formats this Document, making everything nice while preserving
189189
/// comments.
190190
pub fn fmt(&mut self) {
191-
self.fmt_impl(0);
191+
self.fmt_impl(0, false);
192+
}
193+
194+
/// Formats the document and removes all comments from the document.
195+
pub fn fmt_no_comments(&mut self) {
196+
self.fmt_impl(0, true);
192197
}
193198
}
194199

@@ -199,15 +204,20 @@ impl Display for KdlDocument {
199204
}
200205

201206
impl KdlDocument {
202-
pub(crate) fn fmt_impl(&mut self, indent: usize) {
207+
pub(crate) fn fmt_impl(&mut self, indent: usize, no_comments: bool) {
203208
if let Some(s) = self.leading.as_mut() {
204-
crate::fmt::fmt_leading(s, indent);
205-
}
206-
if let Some(s) = self.trailing.as_mut() {
207-
crate::fmt::fmt_trailing(s);
209+
crate::fmt::fmt_leading(s, indent, no_comments);
208210
}
211+
let mut has_nodes = false;
209212
for node in &mut self.nodes {
210-
node.fmt_impl(indent);
213+
has_nodes = true;
214+
node.fmt_impl(indent, no_comments);
215+
}
216+
if let Some(s) = self.trailing.as_mut() {
217+
crate::fmt::fmt_trailing(s, no_comments);
218+
if !has_nodes {
219+
s.push('\n');
220+
}
211221
}
212222
}
213223

src/entry.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,12 @@ impl Display for KdlEntry {
124124
if let Some(leading) = &self.leading {
125125
write!(f, "{}", leading)?;
126126
}
127-
if let Some(ty) = &self.ty {
128-
write!(f, "({})", ty)?;
129-
}
130127
if let Some(name) = &self.name {
131128
write!(f, "{}=", name)?;
132129
}
130+
if let Some(ty) = &self.ty {
131+
write!(f, "({})", ty)?;
132+
}
133133
if let Some(repr) = &self.value_repr {
134134
write!(f, "{}", repr)?;
135135
} else {
@@ -165,7 +165,7 @@ impl FromStr for KdlEntry {
165165
type Err = KdlError;
166166

167167
fn from_str(s: &str) -> Result<Self, Self::Err> {
168-
parser::parse(s, parser::entry_with_node_space)
168+
parser::parse(s, parser::entry_with_trailing)
169169
}
170170
}
171171

@@ -217,7 +217,7 @@ mod test {
217217
}
218218
);
219219

220-
let entry: KdlEntry = " \\\n (\"m\\\"eh\")\"foo\"=0xDEADbeef\t\\\n".parse()?;
220+
let entry: KdlEntry = " \\\n \"foo\"=(\"m\\\"eh\")0xDEADbeef\t\\\n".parse()?;
221221
assert_eq!(
222222
entry,
223223
KdlEntry {

src/fmt.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
1-
pub(crate) fn fmt_leading(leading: &mut String, indent: usize) {
1+
pub(crate) fn fmt_leading(leading: &mut String, indent: usize, no_comments: bool) {
22
if leading.is_empty() {
33
return;
44
}
5-
let comments = crate::parser::parse(leading.trim(), crate::parser::leading_comments)
6-
.expect("invalid leading text");
75
let mut result = String::new();
8-
for line in comments {
9-
let trimmed = line.trim();
10-
if !trimmed.is_empty() {
11-
result.push_str(&format!("{:indent$}{}\n", "", trimmed, indent = indent));
6+
if !no_comments {
7+
let comments = crate::parser::parse(leading.trim(), crate::parser::leading_comments)
8+
.expect("invalid leading text");
9+
for line in comments {
10+
let trimmed = line.trim();
11+
if !trimmed.is_empty() {
12+
result.push_str(&format!("{:indent$}{}\n", "", trimmed, indent = indent));
13+
}
1214
}
1315
}
1416
result.push_str(&format!("{:indent$}", "", indent = indent));
1517
*leading = result;
1618
}
1719

18-
pub(crate) fn fmt_trailing(decor: &mut String) {
20+
pub(crate) fn fmt_trailing(decor: &mut String, no_comments: bool) {
1921
if decor.is_empty() {
2022
return;
2123
}
2224
*decor = decor.trim().to_string();
2325
let mut result = String::new();
24-
let comments = crate::parser::parse(decor, crate::parser::trailing_comments)
25-
.expect("invalid trailing text");
26-
for comment in comments {
27-
result.push_str(comment);
26+
if !no_comments {
27+
let comments = crate::parser::parse(decor, crate::parser::trailing_comments)
28+
.expect("invalid trailing text");
29+
for comment in comments {
30+
result.push_str(comment);
31+
}
2832
}
2933
*decor = result;
3034
}

src/identifier.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{parser, KdlError};
44

55
/// Represents a KDL
66
/// [Identifier](https://github.com/kdl-org/kdl/blob/main/SPEC.md#identifier).
7-
#[derive(Debug, Clone, PartialEq)]
7+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
88
pub struct KdlIdentifier {
99
pub(crate) value: String,
1010
pub(crate) repr: Option<String>,
@@ -199,9 +199,6 @@ mod test {
199199
let invalid = "\"x";
200200
assert!(invalid.parse::<KdlIdentifier>().is_err());
201201

202-
let invalid = "r#\"foo\"#";
203-
assert!(invalid.parse::<KdlIdentifier>().is_err());
204-
205202
Ok(())
206203
}
207204

src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,32 @@
9494
//! ╰────
9595
//! help: Floating point numbers must be base 10, and have numbers after the decimal point.
9696
//! ```
97+
//!
98+
//! ## Quirks
99+
//!
100+
//! ### Properties
101+
//!
102+
//! Multiple properties with the same name are allowed, and all duplicated
103+
//! **will be preserved**, meaning those documents will correctly round-trip.
104+
//! When using `node.get()`/`node["key"]` & company, the _last_ property with
105+
//! that name's value will be returned.
106+
//!
107+
//! ### Numbers
108+
//!
109+
//! KDL itself does not specify a particular representation for numbers and
110+
//! accepts just about anything valid, no matter how large and how small. This
111+
//! means a few things:
112+
//!
113+
//! * Numbers without a decimal point are interpreted as [`u64`].
114+
//! * Numbers with a decimal point are interpreted as [`f64`].
115+
//! * Floating point numbers that evaluate to [`f64::INFINITY`] or
116+
//! [`f64::NEG_INFINITY`] or NaN will be represented as such in the values,
117+
//! instead of the original numbers.
118+
//! * A similar restriction applies to overflowed [`u64`] values.
119+
//! * The original _representation_ of these numbers will be preserved, unless
120+
//! you [`KdlDocument::fmt`] in which case the original representation will be
121+
//! thrown away and the actual value will be used when serializing.
122+
//!
97123
//! ## License
98124
//!
99125
//! The code in this repository is covered by [the Apache-2.0
@@ -102,6 +128,7 @@
102128
#![deny(missing_debug_implementations, nonstandard_style)]
103129
#![warn(missing_docs, unreachable_pub, rust_2018_idioms, unreachable_pub)]
104130
#![cfg_attr(test, deny(warnings))]
131+
#![doc(html_favicon_url = "https://kdl.dev/favicon.ico")]
105132
#![doc(html_logo_url = "https://kdl.dev/logo.svg")]
106133

107134
pub use document::*;

src/node.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,15 @@ impl KdlNode {
134134
fn get_impl(&self, key: NodeKey) -> Option<&KdlEntry> {
135135
match key {
136136
NodeKey::Key(key) => {
137+
let mut current = None;
137138
for entry in &self.entries {
138139
if entry.name.is_some()
139140
&& entry.name.as_ref().map(|i| i.value()) == Some(key.value())
140141
{
141-
return Some(entry);
142+
current = Some(entry);
142143
}
143144
}
144-
None
145+
current
145146
}
146147
NodeKey::Index(idx) => {
147148
let mut current_idx = 0;
@@ -170,14 +171,15 @@ impl KdlNode {
170171
fn get_mut_impl(&mut self, key: NodeKey) -> Option<&mut KdlEntry> {
171172
match key {
172173
NodeKey::Key(key) => {
174+
let mut current = None;
173175
for entry in &mut self.entries {
174176
if entry.name.is_some()
175177
&& entry.name.as_ref().map(|i| i.value()) == Some(key.value())
176178
{
177-
return Some(entry);
179+
current = Some(entry);
178180
}
179181
}
180-
None
182+
current
181183
}
182184
NodeKey::Index(idx) => {
183185
let mut current_idx = 0;
@@ -340,7 +342,12 @@ impl KdlNode {
340342

341343
/// Auto-formats this node and its contents.
342344
pub fn fmt(&mut self) {
343-
self.fmt_impl(0);
345+
self.fmt_impl(0, false);
346+
}
347+
348+
/// Auto-formats this node and its contents, stripping comments.
349+
pub fn fmt_no_comments(&mut self) {
350+
self.fmt_impl(0, true);
344351
}
345352
}
346353

@@ -421,12 +428,12 @@ impl Display for KdlNode {
421428
}
422429

423430
impl KdlNode {
424-
pub(crate) fn fmt_impl(&mut self, indent: usize) {
431+
pub(crate) fn fmt_impl(&mut self, indent: usize, no_comments: bool) {
425432
if let Some(s) = self.leading.as_mut() {
426-
crate::fmt::fmt_leading(s, indent);
433+
crate::fmt::fmt_leading(s, indent, no_comments);
427434
}
428435
if let Some(s) = self.trailing.as_mut() {
429-
crate::fmt::fmt_trailing(s);
436+
crate::fmt::fmt_trailing(s, no_comments);
430437
if s.starts_with(';') {
431438
s.remove(0);
432439
}
@@ -446,7 +453,7 @@ impl KdlNode {
446453
entry.fmt();
447454
}
448455
if let Some(children) = self.children.as_mut() {
449-
children.fmt_impl(indent + 4);
456+
children.fmt_impl(indent + 4, no_comments);
450457
if let Some(leading) = children.leading.as_mut() {
451458
leading.push('\n');
452459
}
@@ -511,6 +518,11 @@ mod test {
511518
assert_eq!(node.name(), &"\"node\"".parse()?);
512519
assert_eq!(node.get(0), Some(&"0xDEADbeef".parse()?));
513520

521+
r#"
522+
node "test" {
523+
link "blah" anything="self"
524+
}"#
525+
.parse::<KdlNode>()?;
514526
Ok(())
515527
}
516528

@@ -528,5 +540,9 @@ mod test {
528540

529541
assert_eq!(node[0], false.into());
530542
assert_eq!(node["foo"], KdlValue::Null);
543+
544+
node.entries_mut().push(KdlEntry::new_prop("x", 1));
545+
node.entries_mut().push(KdlEntry::new_prop("x", 2));
546+
assert_eq!(&node["x"], &2.into())
531547
}
532548
}

0 commit comments

Comments
 (0)