1use std::{
6 borrow::Cow,
7 char::CharTryFromError,
8 convert::TryInto,
9 fmt::{self, Formatter},
10 hash::{Hash, Hasher},
11 iter::FromIterator,
12 ops::{Deref, DerefMut, Index, RangeFrom, RangeTo},
13};
14
15use memchr::{memchr, memchr2};
16use nom::{
17 branch::alt,
18 bytes::complete::take_while_m_n,
19 character::complete::char,
20 combinator::success,
21 error::{make_error, ErrorKind, FromExternalError, ParseError},
22 Err as NomErr, IResult, Parser,
23};
24use nom_supreme::{
25 context::ContextError,
26 multi::parse_separated_terminated,
27 tag::{complete::tag, TagError},
28 ParserExt,
29};
30use serde::{de, Deserialize, Serialize};
31
32#[derive(Debug, Clone, Eq)]
37pub struct KdlString<'a> {
38 inner: Cow<'a, str>,
39}
40
41impl<'a> KdlString<'a> {
42 pub fn new() -> Self {
44 Self::from_borrowed("")
45 }
46
47 pub fn from_cow(cow: Cow<'a, str>) -> Self {
49 Self { inner: cow }
50 }
51
52 pub fn from_borrowed(s: &'a str) -> Self {
54 Self::from_cow(Cow::Borrowed(s))
55 }
56
57 pub fn from_string(s: String) -> Self {
59 Self::from_cow(Cow::Owned(s))
60 }
61
62 pub fn into_string(self) -> String {
64 self.inner.into_owned()
65 }
66
67 pub fn as_str(&self) -> &str {
69 self
70 }
71
72 pub fn visit_to<V, E>(self, visitor: V) -> Result<V::Value, E>
74 where
75 V: de::Visitor<'a>,
76 E: de::Error,
77 {
78 match self.inner {
79 Cow::Borrowed(value) => visitor.visit_borrowed_str(value),
80 Cow::Owned(value) => visitor.visit_string(value),
81 }
82 }
83}
84
85impl<T: AsRef<str>> PartialEq<T> for KdlString<'_> {
86 fn eq(&self, other: &T) -> bool {
87 self.as_ref() == other.as_ref()
88 }
89}
90
91impl Hash for KdlString<'_> {
92 fn hash<H: Hasher>(&self, state: &mut H) {
93 self.as_str().hash(state)
94 }
95}
96
97impl Default for KdlString<'_> {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103impl<'a> Deref for KdlString<'a> {
104 type Target = Cow<'a, str>;
105
106 fn deref(&self) -> &Cow<'a, str> {
107 &self.inner
108 }
109}
110
111impl DerefMut for KdlString<'_> {
112 fn deref_mut(&mut self) -> &mut Self::Target {
113 &mut self.inner
114 }
115}
116
117impl AsRef<str> for KdlString<'_> {
118 fn as_ref(&self) -> &str {
119 self
120 }
121}
122
123impl<'a> Extend<&'a str> for KdlString<'a> {
124 fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
125 iter.into_iter().for_each(|s| self.push_str(s))
126 }
127}
128
129impl Extend<char> for KdlString<'_> {
130 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
131 iter.into_iter().for_each(|c| self.push_char(c))
132 }
133}
134
135impl<'a, 'b> Extend<&'b char> for KdlString<'a> {
136 fn extend<T: IntoIterator<Item = &'b char>>(&mut self, iter: T) {
137 self.extend(iter.into_iter().copied())
138 }
139}
140
141impl<T> FromIterator<T> for KdlString<'_>
142where
143 Self: Extend<T>,
144{
145 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
146 let mut string = KdlString::new();
147 string.extend(iter);
148 string
149 }
150}
151
152impl Serialize for KdlString<'_> {
153 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154 where
155 S: serde::Serializer,
156 {
157 serializer.serialize_str(self)
158 }
159}
160
161impl<'de> Deserialize<'de> for KdlString<'de> {
162 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
163 where
164 D: serde::Deserializer<'de>,
165 {
166 struct KdlStringVisitor;
167
168 impl<'de> de::Visitor<'de> for KdlStringVisitor {
169 type Value = KdlString<'de>;
170
171 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
172 write!(formatter, "a KDL string")
173 }
174
175 fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
176 where
177 E: de::Error,
178 {
179 Ok(KdlString::from_borrowed(value))
180 }
181
182 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
183 where
184 E: de::Error,
185 {
186 self.visit_string(value.to_owned())
187 }
188
189 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
190 where
191 E: de::Error,
192 {
193 Ok(KdlString::from_string(value))
194 }
195 }
196
197 deserializer.deserialize_string(KdlStringVisitor)
198 }
199
200 fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
201 where
202 D: serde::Deserializer<'de>,
203 {
204 struct KdlStringVisitor<'de, 'a>(&'a mut KdlString<'de>);
205
206 impl<'de> de::Visitor<'de> for KdlStringVisitor<'de, '_> {
207 type Value = ();
208
209 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
210 write!(formatter, "a KDL string")
211 }
212
213 fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
214 where
215 E: de::Error,
216 {
217 *self.0 = KdlString::from_borrowed(value);
218 Ok(())
219 }
220
221 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
222 where
223 E: de::Error,
224 {
225 match self.0.inner {
226 Cow::Owned(ref mut s) => {
227 s.clear();
228 s.push_str(value);
229 Ok(())
230 }
231 Cow::Borrowed(_) => self.visit_string(value.to_owned()),
232 }
233 }
234
235 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
236 where
237 E: de::Error,
238 {
239 *self.0 = KdlString::from_string(value);
240 Ok(())
241 }
242 }
243
244 deserializer.deserialize_string(KdlStringVisitor(place))
245 }
246}
247
248pub trait StringBuilder<'a>: Sized {
252 fn push_str(&mut self, s: &'a str);
254
255 fn push_char(&mut self, c: char);
257
258 fn from_str(s: &'a str) -> Self;
260
261 fn empty() -> Self {
263 Self::from_str("")
264 }
265}
266
267impl<'a> StringBuilder<'a> for () {
270 fn push_str(&mut self, _s: &'a str) {}
271 fn push_char(&mut self, _c: char) {}
272 fn from_str(_s: &'a str) {}
273}
274
275impl<'a> StringBuilder<'a> for String {
277 fn push_str(&mut self, s: &'a str) {
278 self.push_str(s)
279 }
280
281 fn push_char(&mut self, c: char) {
282 self.push(c)
283 }
284
285 fn from_str(s: &'a str) -> Self {
286 s.to_owned()
287 }
288}
289
290impl<'a> StringBuilder<'a> for KdlString<'a> {
291 fn push_str(&mut self, s: &'a str) {
292 if self.is_empty() {
293 **self = Cow::Borrowed(s)
294 } else {
295 self.to_mut().push_str(s);
296 }
297 }
298
299 fn push_char(&mut self, c: char) {
300 self.to_mut().push(c)
301 }
302
303 fn from_str(s: &'a str) -> Self {
304 Self::from_borrowed(s)
305 }
306}
307
308struct SliceShifter<'a, T: ?Sized> {
309 base: &'a T,
310 point: usize,
311}
312
313impl<'a, T: ?Sized, A: ?Sized, B: ?Sized> SliceShifter<'a, T>
314where
315 T: Index<RangeTo<usize>, Output = A>,
316 T: Index<RangeFrom<usize>, Output = B>,
317{
318 fn new(base: &'a T) -> Self {
319 Self { base, point: 0 }
320 }
321
322 fn head(&self) -> &'a A {
323 &self.base[..self.point]
324 }
325
326 fn tail(&self) -> &'a B {
327 &self.base[self.point..]
328 }
329
330 fn shift(&mut self, amount: usize) {
331 self.point += amount
332 }
333}
334
335pub fn parse_raw_string<'i, E: ParseError<&'i str>>(input: &'i str) -> IResult<&'i str, &'i str, E>
337where
338 E: ParseError<&'i str>,
339{
340 let (input, hash_count) =
341 parse_separated_terminated(char('#'), success(()), char('"'), || 0, |n, _c| n + 1)
342 .or(char('"').value(0))
343 .preceded_by(char('r'))
344 .parse(input)?;
345
346 let mut shifter = SliceShifter::new(input);
347
348 loop {
349 match memchr(b'"', shifter.tail().as_bytes()) {
350 None => return Err(NomErr::Failure(make_error("", ErrorKind::Eof))),
352
353 Some(quote_idx) => {
355 shifter.shift(quote_idx);
356 let payload = shifter.head();
357 shifter.shift(1);
358
359 match shifter.tail().as_bytes().get(0..hash_count) {
360 None => return Err(NomErr::Failure(make_error("", ErrorKind::Eof))),
363
364 Some(chunk) => {
367 if chunk.iter().all(|&b| b == b'#') {
368 shifter.shift(hash_count);
369 return Ok((shifter.tail(), payload));
370 }
371 }
372 }
373 }
374 }
375 }
376}
377
378#[cfg(test)]
379mod test_parse_raw {
380 use super::*;
381 use cool_asserts::assert_matches;
382 use nom::error::Error;
383
384 fn typed_parse_raw(input: &str) -> IResult<&str, &str, Error<&str>> {
385 parse_raw_string(input)
386 }
387
388 #[test]
389 fn hashless() {
390 assert_eq!(typed_parse_raw(r#"r"abc"def"#), Ok(("def", "abc")))
391 }
392
393 #[test]
394 fn hashed() {
395 assert_eq!(
396 typed_parse_raw(r####"r##"abc"##def"####),
397 Ok(("def", "abc"))
398 )
399 }
400
401 #[test]
402 fn inner_hashes() {
403 assert_eq!(
404 typed_parse_raw(r####"r##"abc"#abc"##def"####),
405 Ok(("def", r##"abc"#abc"##))
406 )
407 }
408
409 #[test]
410 fn extra_hashes() {
411 assert_eq!(typed_parse_raw(r####"r##"abc"###"####), Ok(("#", "abc")))
412 }
413
414 #[test]
415 fn unfinished() {
416 assert_matches!(
417 typed_parse_raw(r####"r###"abc"####),
418 Err(NomErr::Failure(Error { input: "", .. }))
419 )
420 }
421
422 #[test]
423 fn partially_finished() {
424 assert_matches!(
425 typed_parse_raw(r####"r###"abc"#"####),
426 Err(NomErr::Failure(Error { input: "", .. }))
427 )
428 }
429
430 #[test]
431 fn not_regular_string() {
432 assert_matches!(
433 typed_parse_raw(r##""abc""##),
434 Err(NomErr::Error(Error {
435 input: r##""abc""##,
436 ..
437 }))
438 )
439 }
440
441 #[test]
442 fn not_identifier() {
443 assert_matches!(
444 typed_parse_raw("abc"),
445 Err(NomErr::Error(Error { input: "abc", .. }))
446 )
447 }
448
449 #[test]
450 fn not_r_identifier() {
451 assert_matches!(
452 typed_parse_raw("raw"),
453 Err(NomErr::Error(Error { input: "aw", .. }))
454 )
455 }
456}
457
458#[inline]
460pub fn is_identifier(c: char) -> bool {
461 let code_point: u32 = c.into();
462 (b"\\/(){}<>;[]=,\"".iter().all(|&b| code_point != b.into()))
463 && (code_point > 0x20)
464 && (code_point <= 0x10FFFF)
465}
466
467#[inline]
469pub fn is_initial_identifier(c: char) -> bool {
470 is_identifier(c) && !c.is_ascii_digit()
471}
472
473pub fn parse_bare_identifier<'i, E: ParseError<&'i str>>(
480 input: &'i str,
481) -> IResult<&'i str, &'i str, E> {
482 let mut chars = input.chars();
483 match chars.next() {
484 Some(c) if is_initial_identifier(c) => {
485 let split_point = chars
486 .as_str()
487 .find(|c: char| !is_identifier(c))
488 .unwrap_or(0)
489 + c.len_utf8();
490 let (ident, tail) = input.split_at(split_point);
491 Ok((tail, ident))
492 }
493 _ => Err(NomErr::Error(make_error(input, ErrorKind::Alpha))),
494 }
495}
496
497#[cfg(test)]
498mod test_parse_identifier {
499 use super::*;
500 use nom::error::{Error, ErrorKind};
501
502 fn typed_parse_identifier(input: &str) -> IResult<&str, &str, Error<&str>> {
503 parse_bare_identifier(input)
504 }
505
506 #[test]
507 fn basic() {
508 assert_eq!(typed_parse_identifier("abc abc"), Ok((" abc", "abc")))
509 }
510
511 #[test]
512 fn with_num() {
513 assert_eq!(typed_parse_identifier("abc123 abc"), Ok((" abc", "abc123")))
514 }
515
516 #[test]
517 fn start_with_letter() {
518 assert_eq!(
519 typed_parse_identifier("123"),
520 Err(NomErr::Error(Error {
521 input: "123",
522 code: ErrorKind::Alpha
523 }))
524 )
525 }
526
527 #[test]
528 fn with_punctuation() {
529 assert_eq!(
530 typed_parse_identifier("abc-def_ghi 123"),
531 Ok((" 123", "abc-def_ghi"))
532 )
533 }
534
535 #[test]
536 fn is_dash() {
537 assert_eq!(typed_parse_identifier("- 10"), Ok((" 10", "-")))
538 }
539}
540
541fn parse_unicode_escape<'i, E>(input: &'i str) -> IResult<&'i str, char, E>
543where
544 E: ParseError<&'i str>,
545 E: TagError<&'i str, &'static str>,
546 E: FromExternalError<&'i str, CharTryFromError>,
547{
548 take_while_m_n(1, 6, |c: char| c.is_ascii_hexdigit())
549 .map(|s| u32::from_str_radix(s, 16).expect("failed to parse 1-6 hex digits to a u32?"))
550 .map_res(|c: u32| c.try_into())
551 .terminated(char('}'))
552 .cut()
553 .preceded_by(tag("u{"))
554 .parse(input)
555}
556
557fn parse_escape<'i, E>(input: &'i str) -> IResult<&'i str, char, E>
558where
559 E: ParseError<&'i str>,
560 E: TagError<&'i str, &'static str>,
561 E: FromExternalError<&'i str, CharTryFromError>,
562{
563 alt((
564 char('n').value('\n'),
565 char('r').value('\r'),
566 char('t').value('\t'),
567 char('\\').value('\\'),
568 char('/').value('/'),
569 char('"').value('"'),
570 char('b').value('\u{08}'),
571 char('f').value('\u{0C}'),
572 parse_unicode_escape,
573 ))
574 .preceded_by(char('\\'))
575 .parse(input)
576}
577
578fn parse_unescaped_chunk<'i, E>(input: &'i str) -> IResult<&'i str, &'i str, E>
580where
581 E: ParseError<&'i str>,
582{
583 match memchr2(b'"', b'\\', input.as_bytes()) {
584 None => Err(NomErr::Error(E::or(
585 make_error("", ErrorKind::Eof),
586 E::from_char("", '"'),
587 ))),
588
589 Some(0) => Err(NomErr::Error(make_error(input, ErrorKind::TakeWhile1))),
590 Some(n) => {
591 let (head, tail) = input.split_at(n);
592 Ok((tail, head))
593 }
594 }
595}
596
597enum StringChunk<'a> {
598 Chunk(&'a str),
599 Char(char),
600}
601
602fn parse_chunk<'i, E>(input: &'i str) -> IResult<&'i str, StringChunk<'i>, E>
603where
604 E: ParseError<&'i str>,
605 E: TagError<&'i str, &'static str>,
606 E: FromExternalError<&'i str, CharTryFromError>,
607{
608 alt((
609 parse_unescaped_chunk.map(StringChunk::Chunk),
610 parse_escape.map(StringChunk::Char),
611 ))
612 .parse(input)
613}
614
615pub fn parse_escaped_string<'i, T, E>(input: &'i str) -> IResult<&'i str, T, E>
620where
621 T: StringBuilder<'i>,
622 E: ParseError<&'i str>,
623 E: TagError<&'i str, &'static str>,
624 E: FromExternalError<&'i str, CharTryFromError>,
625{
626 parse_separated_terminated(
627 parse_chunk,
628 success(()),
629 char('"'),
630 T::empty,
631 |mut string, chunk| {
632 match chunk {
633 StringChunk::Chunk(chunk) => string.push_str(chunk),
634 StringChunk::Char(c) => string.push_char(c),
635 }
636 string
637 },
638 )
639 .or(char('"').map(|_| T::empty()))
640 .cut()
641 .preceded_by(char('"'))
642 .parse(input)
643}
644
645#[cfg(test)]
646mod test_parse_escaped_string {
647 use super::*;
648 use cool_asserts::assert_matches;
649 use nom::error::Error;
650
651 fn typed_parse_identifier(input: &str) -> IResult<&str, KdlString<'_>, Error<&str>> {
652 parse_escaped_string(input)
653 }
654
655 #[test]
656 fn basic() {
657 assert_matches!(
658 typed_parse_identifier("\"hello\" abc"),
659 Ok((
660 " abc",
661 KdlString {
662 inner: Cow::Borrowed("hello")
663 }
664 ))
665 )
666 }
667
668 #[test]
669 fn with_escape() {
670 assert_matches!(
671 typed_parse_identifier("\"hello \\t world\" abc"),
672 Ok((
673 " abc",
674 KdlString { inner: Cow::Owned(s) }
675 )) => assert_eq!(s, "hello \t world")
676 );
677 }
678
679 #[test]
680 fn with_escaped_unicode() {
681 assert_matches!(
682 typed_parse_identifier("\"hello\\u{0A}world\" abc"),
683 Ok((
684 " abc",
685 KdlString {
686 inner: Cow::Owned(s)
687 }
688 )) => assert_eq!(s, "hello\nworld")
689 );
690 }
691}
692
693pub fn parse_string<'i, T, E>(input: &'i str) -> IResult<&'i str, T, E>
695where
696 T: StringBuilder<'i>,
697 E: ParseError<&'i str>,
698 E: TagError<&'i str, &'static str>,
699 E: FromExternalError<&'i str, CharTryFromError>,
700 E: ContextError<&'i str, &'static str>,
701{
702 alt((
703 parse_escaped_string.context("escaped string"),
704 parse_raw_string.context("raw string").map(T::from_str),
705 ))
706 .parse(input)
707}
708
709pub fn parse_identifier<'i, T, E>(input: &'i str) -> IResult<&'i str, T, E>
711where
712 T: StringBuilder<'i>,
713 E: ParseError<&'i str>,
714 E: TagError<&'i str, &'static str>,
715 E: FromExternalError<&'i str, CharTryFromError>,
716 E: ContextError<&'i str, &'static str>,
717{
718 alt((
719 parse_bare_identifier
720 .map(T::from_str)
721 .context("bare identifier"),
722 parse_string.context("string"),
723 ))
724 .parse(input)
725}