1#[cfg(feature = "span")]
2use miette::SourceSpan;
3use std::{fmt::Display, str::FromStr};
4
5use crate::{v2_parser, KdlError, KdlIdentifier, KdlValue};
6
7#[derive(Debug, Clone, Eq)]
12pub struct KdlEntry {
13 pub(crate) ty: Option<KdlIdentifier>,
14 pub(crate) value: KdlValue,
15 pub(crate) name: Option<KdlIdentifier>,
16 pub(crate) format: Option<KdlEntryFormat>,
17 #[cfg(feature = "span")]
18 pub(crate) span: SourceSpan,
19}
20
21impl PartialEq for KdlEntry {
22 fn eq(&self, other: &Self) -> bool {
23 self.ty == other.ty
24 && self.value == other.value
25 && self.name == other.name
26 && self.format == other.format
27 }
29}
30
31impl std::hash::Hash for KdlEntry {
32 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
33 self.ty.hash(state);
34 self.value.hash(state);
35 self.name.hash(state);
36 self.format.hash(state);
37 }
39}
40
41impl KdlEntry {
42 pub fn new(value: impl Into<KdlValue>) -> Self {
44 Self {
45 ty: None,
46 value: value.into(),
47 name: None,
48 format: None,
49 #[cfg(feature = "span")]
50 span: (0..0).into(),
51 }
52 }
53
54 pub fn name(&self) -> Option<&KdlIdentifier> {
56 self.name.as_ref()
57 }
58
59 pub fn name_mut(&mut self) -> Option<&mut KdlIdentifier> {
61 self.name.as_mut()
62 }
63
64 pub fn set_name(&mut self, name: Option<impl Into<KdlIdentifier>>) {
66 self.name = name.map(|x| x.into());
67 }
68
69 pub fn value(&self) -> &KdlValue {
71 &self.value
72 }
73
74 pub fn value_mut(&mut self) -> &mut KdlValue {
76 &mut self.value
77 }
78
79 pub fn set_value(&mut self, value: impl Into<KdlValue>) {
81 self.value = value.into();
82 }
83
84 #[cfg(feature = "span")]
90 pub fn span(&self) -> SourceSpan {
91 self.span
92 }
93
94 #[cfg(feature = "span")]
96 pub fn set_span(&mut self, span: impl Into<SourceSpan>) {
97 self.span = span.into();
98 }
99
100 pub fn ty(&self) -> Option<&KdlIdentifier> {
102 self.ty.as_ref()
103 }
104
105 pub fn ty_mut(&mut self) -> Option<&mut KdlIdentifier> {
107 self.ty.as_mut()
108 }
109
110 pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
112 self.ty = Some(ty.into());
113 }
114
115 pub fn format(&self) -> Option<&KdlEntryFormat> {
117 self.format.as_ref()
118 }
119
120 pub fn format_mut(&mut self) -> Option<&mut KdlEntryFormat> {
122 self.format.as_mut()
123 }
124
125 pub fn set_format(&mut self, format: KdlEntryFormat) {
127 self.format = Some(format);
128 }
129
130 pub fn new_prop(key: impl Into<KdlIdentifier>, value: impl Into<KdlValue>) -> Self {
132 Self {
133 ty: None,
134 value: value.into(),
135 name: Some(key.into()),
136 format: None,
137 #[cfg(feature = "span")]
138 span: SourceSpan::from(0..0),
139 }
140 }
141
142 pub fn clear_format(&mut self) {
145 self.format = None;
146 if let Some(ty) = &mut self.ty {
147 ty.clear_format();
148 }
149 if let Some(name) = &mut self.name {
150 name.clear_format();
151 }
152 }
153
154 pub fn len(&self) -> usize {
156 format!("{self}").len()
157 }
158
159 pub fn is_empty(&self) -> bool {
161 self.len() == 0
162 }
163
164 pub fn keep_format(&mut self) {
167 if let Some(fmt) = self.format_mut() {
168 fmt.autoformat_keep = true;
169 }
170 }
171
172 pub fn autoformat(&mut self) {
174 if !self
177 .format
178 .as_ref()
179 .map(|f| f.autoformat_keep)
180 .unwrap_or(false)
181 {
182 self.format = None;
183 } else {
184 #[cfg(feature = "v1")]
185 self.ensure_v2();
186 self.format = self.format.take().map(|f| KdlEntryFormat {
187 value_repr: f.value_repr,
188 leading: f.leading,
189 ..Default::default()
190 });
191 }
192
193 if let Some(name) = &mut self.name {
194 name.autoformat();
195 }
196 }
197
198 pub fn parse(s: &str) -> Result<Self, KdlError> {
205 #[cfg(not(feature = "v1-fallback"))]
206 {
207 v2_parser::try_parse(v2_parser::padded_node_entry, s)
208 }
209 #[cfg(feature = "v1-fallback")]
210 {
211 v2_parser::try_parse(v2_parser::padded_node_entry, s)
212 .or_else(|e| KdlEntry::parse_v1(s).map_err(|_| e))
213 }
214 }
215
216 #[cfg(feature = "v1")]
218 pub fn parse_v1(s: &str) -> Result<Self, KdlError> {
219 let ret: Result<kdlv1::KdlEntry, kdlv1::KdlError> = s.parse();
220 ret.map(|x| x.into()).map_err(|e| e.into())
221 }
222
223 pub fn ensure_v2(&mut self) {
225 let value_repr = self.format.as_ref().map(|x| {
226 match &self.value {
227 KdlValue::String(val) => {
228 let s = x.value_repr.trim();
231 let s = s.strip_prefix('r').unwrap_or(s);
233 let s = if crate::value::is_plain_ident(val) {
234 val.into()
235 } else if s
236 .find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
237 .is_some()
238 {
239 if s.contains("\"\"\"") {
241 s.to_string()
243 } else {
244 let s = s.replacen('\"', "\"\"\"\n", 1);
247 s.chars()
248 .rev()
249 .collect::<String>()
250 .replacen('\"', "\"\"\"\n", 1)
251 .chars()
252 .rev()
253 .collect::<String>()
254 }
255 } else if !s.starts_with('#') {
256 s.replace("\\/", "/")
258 } else {
259 s.to_string()
261 };
262 s
263 }
264 KdlValue::Bool(_) | KdlValue::Null => format!("{}", self.value),
267 KdlValue::Integer(_) | KdlValue::Float(_) => x.value_repr.clone(),
269 }
270 });
271
272 if let Some(value_repr) = value_repr.as_ref() {
273 self.format = Some(
274 self.format
275 .clone()
276 .map(|mut x| {
277 x.value_repr = value_repr.into();
278 x
279 })
280 .unwrap_or_else(|| KdlEntryFormat {
281 value_repr: value_repr.into(),
282 leading: " ".into(),
283 ..Default::default()
284 }),
285 )
286 }
287 }
288
289 #[cfg(feature = "v1")]
291 pub fn ensure_v1(&mut self) {
292 let value_repr = self.format.as_ref().map(|x| {
293 match &self.value {
294 KdlValue::String(val) => {
295 let s = x.value_repr.trim();
298 let s = if s.starts_with('#') {
300 format!("r{s}")
301 } else {
302 s.to_string()
303 };
304 let s = if crate::value::is_plain_ident(val)
305 && !s.starts_with('\"')
306 && !s.starts_with("r#")
307 {
308 format!("\"{val}\"")
309 } else if s
310 .find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
311 .is_some()
312 {
313 if s.contains("\"\"\"") {
315 let prefix = s
316 .chars()
317 .rev()
318 .skip_while(|c| c == &'"')
319 .take_while(|c| {
320 v2_parser::NEWLINES.iter().any(|nl| nl.contains(*c))
321 })
322 .collect::<String>();
323 let prefix = prefix.chars().rev().collect::<String>();
324 let mut s = s;
326 for nl in v2_parser::NEWLINES {
327 s = s.replace(&format!("{nl}{prefix}"), nl);
328 }
329 s
332 } else {
333 s
335 }
336 } else if !s.starts_with("r#") {
337 let s = s.replace("\\/", "/"); s.replace('/', "\\/")
340 } else {
341 s.to_string()
343 };
344 s
345 }
346 KdlValue::Bool(b) => b.to_string(),
348 KdlValue::Null => "null".to_string(),
349 KdlValue::Integer(_) | KdlValue::Float(_) => x.value_repr.clone(),
351 }
352 });
353
354 if let Some(value_repr) = value_repr.as_ref() {
355 self.format = Some(
356 self.format
357 .clone()
358 .map(|mut x| {
359 x.value_repr = value_repr.into();
360 x
361 })
362 .unwrap_or_else(|| KdlEntryFormat {
363 value_repr: value_repr.into(),
364 leading: " ".into(),
365 ..Default::default()
366 }),
367 )
368 } else {
369 let v1_val = match self.value() {
370 KdlValue::String(s) => kdlv1::KdlValue::String(s.clone()),
371 KdlValue::Integer(i) => kdlv1::KdlValue::Base10(*i as i64),
372 KdlValue::Float(f) => kdlv1::KdlValue::Base10Float(*f),
373 KdlValue::Bool(b) => kdlv1::KdlValue::Bool(*b),
374 KdlValue::Null => kdlv1::KdlValue::Null,
375 };
376 self.format = Some(KdlEntryFormat {
377 value_repr: v1_val.to_string(),
378 leading: " ".into(),
379 ..Default::default()
380 })
381 }
382 }
383}
384
385#[cfg(feature = "v1")]
386impl From<kdlv1::KdlEntry> for KdlEntry {
387 fn from(value: kdlv1::KdlEntry) -> Self {
388 Self {
389 ty: value.ty().map(|x| x.clone().into()),
390 value: value.value().clone().into(),
391 name: value.name().map(|x| x.clone().into()),
392 format: Some(KdlEntryFormat {
393 value_repr: value.value_repr().unwrap_or("").into(),
394 leading: value.leading().unwrap_or("").into(),
395 trailing: value.trailing().unwrap_or("").into(),
396 ..Default::default()
397 }),
398 #[cfg(feature = "span")]
399 span: SourceSpan::new(value.span().offset().into(), value.span().len()),
400 }
401 }
402}
403
404impl Display for KdlEntry {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 if let Some(KdlEntryFormat { leading, .. }) = &self.format {
407 write!(f, "{leading}")?;
408 }
409 if let Some(name) = &self.name {
410 write!(f, "{name}")?;
411 if let Some(KdlEntryFormat {
412 after_key,
413 after_eq,
414 ..
415 }) = &self.format
416 {
417 write!(f, "{after_key}={after_eq}")?;
418 } else {
419 write!(f, "=")?;
420 }
421 }
422 if let Some(ty) = &self.ty {
423 write!(f, "(")?;
424 if let Some(KdlEntryFormat { before_ty_name, .. }) = &self.format {
425 write!(f, "{before_ty_name}")?;
426 }
427 write!(f, "{ty}")?;
428 if let Some(KdlEntryFormat { after_ty_name, .. }) = &self.format {
429 write!(f, "{after_ty_name}")?;
430 }
431 write!(f, ")")?;
432 }
433 if let Some(KdlEntryFormat {
434 after_ty,
435 value_repr,
436 ..
437 }) = &self.format
438 {
439 write!(f, "{after_ty}{value_repr}")?;
440 } else {
441 write!(f, "{}", self.value)?;
442 }
443 if let Some(KdlEntryFormat { trailing, .. }) = &self.format {
444 write!(f, "{trailing}")?;
445 }
446 Ok(())
447 }
448}
449
450impl<T> From<T> for KdlEntry
451where
452 T: Into<KdlValue>,
453{
454 fn from(value: T) -> Self {
455 Self::new(value)
456 }
457}
458
459impl<K, V> From<(K, V)> for KdlEntry
460where
461 K: Into<KdlIdentifier>,
462 V: Into<KdlValue>,
463{
464 fn from((key, value): (K, V)) -> Self {
465 Self::new_prop(key, value)
466 }
467}
468
469impl FromStr for KdlEntry {
470 type Err = KdlError;
471
472 fn from_str(s: &str) -> Result<Self, Self::Err> {
473 Self::parse(s)
474 }
475}
476
477#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
479pub struct KdlEntryFormat {
480 pub value_repr: String,
482 pub leading: String,
484 pub trailing: String,
486 pub after_ty: String,
489 pub before_ty_name: String,
492 pub after_ty_name: String,
495 pub after_key: String,
497 pub after_eq: String,
499 pub autoformat_keep: bool,
501}
502
503#[cfg(test)]
504mod test {
505 use super::*;
506
507 #[test]
508 fn reset_value_repr() -> miette::Result<()> {
509 let mut left_entry: KdlEntry = " name=1.03e2".parse()?;
510 let mut right_entry: KdlEntry = " name=103.0".parse()?;
511 assert_ne!(left_entry, right_entry);
512 left_entry.clear_format();
513 right_entry.clear_format();
514 assert_eq!(left_entry, right_entry);
515 Ok(())
516 }
517
518 #[test]
519 fn new() {
520 let entry = KdlEntry::new(42);
521 assert_eq!(
522 entry,
523 KdlEntry {
524 ty: None,
525 value: KdlValue::Integer(42),
526 name: None,
527 format: None,
528 #[cfg(feature = "span")]
529 span: SourceSpan::from(0..0),
530 }
531 );
532
533 let entry = KdlEntry::new_prop("name", 42);
534 assert_eq!(
535 entry,
536 KdlEntry {
537 ty: None,
538 value: KdlValue::Integer(42),
539 name: Some("name".into()),
540 format: None,
541 #[cfg(feature = "span")]
542 span: SourceSpan::from(0..0),
543 }
544 );
545 }
546
547 #[test]
548 fn parsing() -> miette::Result<()> {
549 let entry: KdlEntry = "foo".parse()?;
550 assert_eq!(
551 entry,
552 KdlEntry {
553 ty: None,
554 value: KdlValue::from("foo"),
555 name: None,
556 format: Some(KdlEntryFormat {
557 value_repr: "foo".into(),
558 ..Default::default()
559 }),
560 #[cfg(feature = "span")]
561 span: SourceSpan::from(0..3),
562 }
563 );
564
565 let entry: KdlEntry = "foo=bar".parse()?;
566 assert_eq!(
567 entry,
568 KdlEntry {
569 ty: None,
570 value: KdlValue::from("bar"),
571 name: Some("foo".parse()?),
572 format: Some(KdlEntryFormat {
573 value_repr: "bar".into(),
574 ..Default::default()
575 }),
576 #[cfg(feature = "span")]
577 span: SourceSpan::from(0..7),
578 }
579 );
580
581 let entry: KdlEntry = " \\\n (\"m\\\"eh\")0xDEADbeef\t\\\n".parse()?;
582 #[cfg_attr(not(feature = "span"), allow(unused_mut))]
583 {
584 let mut ty: KdlIdentifier = "\"m\\\"eh\"".parse()?;
585 #[cfg(feature = "span")]
586 {
587 ty.span = (5..12).into();
588 }
589 assert_eq!(
590 entry,
591 KdlEntry {
592 ty: Some(ty),
593 value: KdlValue::Integer(0xdeadbeef),
594 name: None,
595 format: Some(KdlEntryFormat {
596 leading: " \\\n ".into(),
597 trailing: "\t\\\n".into(),
598 value_repr: "0xDEADbeef".into(),
599 ..Default::default()
600 }),
601 #[cfg(feature = "span")]
602 span: SourceSpan::from(0..26),
603 }
604 );
605 }
606
607 let entry: KdlEntry = " \\\n \"foo\"=(\"m\\\"eh\")0xDEADbeef\t\\\n".parse()?;
608 assert_eq!(
609 entry,
610 KdlEntry {
611 format: Some(KdlEntryFormat {
612 leading: " \\\n ".into(),
613 trailing: "\t\\\n".into(),
614 value_repr: "0xDEADbeef".into(),
615 before_ty_name: "".into(),
616 after_ty_name: "".into(),
617 after_ty: "".into(),
618 after_key: "".into(),
619 after_eq: "".into(),
620 autoformat_keep: false
621 }),
622 ty: Some("\"m\\\"eh\"".parse()?),
623 value: KdlValue::Integer(0xdeadbeef),
624 name: Some("\"foo\"".parse()?),
625 #[cfg(feature = "span")]
626 span: SourceSpan::from(0..0),
627 }
628 );
629
630 Ok(())
631 }
632
633 #[test]
634 fn display() {
635 let entry = KdlEntry::new(KdlValue::Integer(42));
636 assert_eq!(format!("{entry}"), "42");
637
638 let entry = KdlEntry::new_prop("name", KdlValue::Integer(42));
639 assert_eq!(format!("{entry}"), "name=42");
640 }
641
642 #[cfg(feature = "v1")]
643 #[test]
644 fn v1_to_v2_format() -> miette::Result<()> {
645 let mut entry = KdlEntry::parse_v1(r##"r#"hello, world!"#"##)?;
646 entry.keep_format();
647 entry.autoformat();
648 assert_eq!(format!("{}", entry), r##" #"hello, world!"#"##);
649
650 let mut entry = KdlEntry::parse_v1(r#""hello, \" world!""#)?;
651 entry.keep_format();
652 entry.autoformat();
653 assert_eq!(format!("{}", entry), r#" "hello, \" world!""#);
654
655 let mut entry = KdlEntry::parse_v1("\"foo!`~.,<>\"")?;
656 entry.keep_format();
657 entry.autoformat();
658 assert_eq!(format!("{}", entry), " foo!`~.,<>");
659
660 let mut entry = KdlEntry::parse_v1("\"\nhello, world!\"")?;
661 entry.keep_format();
662 entry.autoformat();
663 assert_eq!(format!("{}", entry), " \"\"\"\n\nhello, world!\n\"\"\"");
664
665 let mut entry = KdlEntry::parse_v1("r#\"\nhello, world!\"#")?;
666 entry.keep_format();
667 entry.autoformat();
668 assert_eq!(format!("{}", entry), " #\"\"\"\n\nhello, world!\n\"\"\"#");
669
670 let mut entry = KdlEntry::parse_v1("true")?;
671 entry.keep_format();
672 entry.autoformat();
673 assert_eq!(format!("{}", entry), " #true");
674
675 let mut entry = KdlEntry::parse_v1("false")?;
676 entry.keep_format();
677 entry.autoformat();
678 assert_eq!(format!("{}", entry), " #false");
679
680 let mut entry = KdlEntry::parse_v1("null")?;
681 entry.keep_format();
682 entry.autoformat();
683 assert_eq!(format!("{}", entry), " #null");
684
685 let mut entry = KdlEntry::parse_v1("1_234_567")?;
686 entry.keep_format();
687 entry.autoformat();
688 assert_eq!(format!("{}", entry), " 1_234_567");
689
690 let mut entry = KdlEntry::parse_v1("1_234_567E-10")?;
691 entry.keep_format();
692 entry.autoformat();
693 assert_eq!(format!("{}", entry), " 1_234_567E-10");
694 Ok(())
695 }
696}