1use smol_str::SmolStr;
2use winnow::ascii::{multispace0, take_escaped};
3use winnow::combinator::{alt, delimited, fail, opt, peek, preceded, separated_pair};
4use winnow::error::{ContextError, ErrMode};
5use winnow::stream::Stream;
6use winnow::token::{any, literal, none_of, one_of, take, take_until, take_while};
7use wp_model_core::model::Value;
8use wp_primitives::Parser;
9use wp_primitives::WResult;
10
11use wp_primitives::symbol::ctx_desc;
12
13pub fn take_ref_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
15 let s = *input;
16 let mut pos = 0usize;
17 let mut paren_depth = 0usize;
18
19 while pos < s.len() {
20 let rest = &s[pos..];
21 let ch = rest.chars().next().expect("pos bounds checked");
22 let ch_len = ch.len_utf8();
23 match ch {
24 ')' if paren_depth == 0 => break,
25 '(' => {
26 paren_depth += 1;
27 pos += ch_len;
28 }
29 ')' => {
30 paren_depth = paren_depth.saturating_sub(1);
31 pos += ch_len;
32 }
33 '<' => {
34 if let Some(seg_len) = parse_wrapped_key_segment(rest, '<', '>') {
35 pos += seg_len;
36 } else {
37 break;
38 }
39 }
40 '{' => {
41 if let Some(seg_len) = parse_wrapped_key_segment(rest, '{', '}') {
42 pos += seg_len;
43 } else {
44 break;
45 }
46 }
47 '>' | '}' => break,
48 _ => {
49 let allowed =
50 ch.is_alphanumeric() || matches!(ch, '_' | '/' | '-' | '.' | '[' | ']' | '*');
51 if !allowed {
52 break;
53 }
54 pos += ch_len;
55 }
56 }
57 }
58
59 if pos == 0 {
60 return fail.context(ctx_desc("<ref_path>")).parse_next(input);
61 }
62
63 let (head, tail) = s.split_at(pos);
64 *input = tail;
65 Ok(head)
66}
67
68fn parse_wrapped_key_segment(s: &str, open: char, close: char) -> Option<usize> {
69 let mut iter = s.char_indices();
70 let (_, first) = iter.next()?;
71 if first != open {
72 return None;
73 }
74 let mut has_inner = false;
75 for (idx, ch) in iter {
76 if ch == close {
77 return has_inner.then_some(idx + ch.len_utf8());
78 }
79 if !is_wrapped_key_char(ch) {
80 return None;
81 }
82 has_inner = true;
83 }
84 None
85}
86
87#[inline]
88fn is_wrapped_key_char(ch: char) -> bool {
89 ch.is_alphanumeric() || matches!(ch, '_' | '/' | '-' | '.')
90}
91
92pub fn take_ref_path_or_quoted(input: &mut &str) -> WResult<String> {
96 alt((
97 single_quot_raw_str,
98 take_ref_path.map(|s: &str| s.to_string()),
99 ))
100 .parse_next(input)
101}
102pub fn take_exact_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
103 take_while(1.., |c: char| {
104 c.is_alphanumeric() || c == '_' || c == '/' || c == '-' || c == '.'
105 })
106 .parse_next(input)
107}
108
109pub fn take_key<'a>(input: &mut &'a str) -> WResult<&'a str> {
110 take_while(1.., |c: char| {
111 c.is_alphanumeric() || c == '_' || c == '/' || c == '-' || c == '.'
112 })
113 .parse_next(input)
114}
115
116pub fn take_kv_key<'a>(input: &mut &'a str) -> WResult<&'a str> {
117 take_while(1.., |c: char| {
118 c.is_alphanumeric()
119 || matches!(
120 c,
121 '_' | '/' | '-' | '.' | '(' | ')' | '<' | '>' | '[' | ']' | '{' | '}'
122 )
123 })
124 .parse_next(input)
125}
126
127pub fn take_var_name<'a>(input: &mut &'a str) -> WResult<&'a str> {
128 take_while(1.., |c: char| {
129 c.is_alphanumeric() || c == '_' || c == '.' || c == '-'
130 })
131 .parse_next(input)
132}
133
134pub fn take_fun_name<'a>(input: &mut &'a str) -> WResult<&'a str> {
135 take_while(1.., |c: char| c.is_alphanumeric() || c == '_' || c == '.').parse_next(input)
137 }
140
141pub fn take_meta_name<'a>(input: &mut &'a str) -> WResult<&'a str> {
142 take_while(1.., |c: char| c.is_alphanumeric() || c == '_' || c == '/').parse_next(input)
144 }
147
148pub fn take_sql_tval(input: &mut &str) -> WResult<Value> {
149 let chars = opt(alt((
150 delimited('"', take_until(0.., "\""), '"'),
151 delimited('\'', take_until(0.., "'"), '\''),
152 )))
153 .parse_next(input)?;
154 if let Some(chars) = chars {
155 return Ok(Value::Chars(chars.into()));
156 }
157 if let Some(value) = opt(take_while(0.., ('0'..='9', '.', '-', '+'))).parse_next(input)? {
158 if let Ok(digit) = value.parse::<i64>() {
159 return Ok(Value::Digit(digit));
160 } else {
161 return Ok(Value::Float(value.parse::<f64>().unwrap_or(0.0)));
162 }
163 }
164
165 "fail-value".parse_next(input)?;
167 Ok(Value::Chars("fail-value".into()))
168}
169
170#[inline]
171pub fn quot_str<'a>(input: &mut &'a str) -> WResult<&'a str> {
172 alt((
173 duble_quot_str_impl.context(ctx_desc(
174 "<quoted_string>::= '\"' , <character_sequence> , '\"' ",
175 )),
176 single_quot_str_impl.context(ctx_desc(
177 "<quoted_string>::= '\"' , <character_sequence> , '\"' ",
178 )),
179 ))
180 .parse_next(input)
181}
182
183#[inline]
184pub fn chars_value<'a>(input: &mut &'a str) -> WResult<&'a str> {
185 let s = *input;
186 if s.starts_with('"') || s.starts_with('\'') {
187 let cp = input.checkpoint();
188 if let Ok(value) = quot_str(input) {
189 return Ok(value);
190 }
191 input.reset(&cp);
192 return alt((window_path, take_to_end)).parse_next(input);
193 }
194 if s.starts_with("r#\"") || s.starts_with("r\"") {
195 let cp = input.checkpoint();
196 if let Ok(value) = quot_r_str(input) {
197 return Ok(value);
198 }
199 input.reset(&cp);
200 }
201 alt((window_path, take_to_end)).parse_next(input)
202}
203
204#[inline]
205pub fn interval_data<'a>(input: &mut &'a str) -> WResult<&'a str> {
206 interval_impl
207 .context(ctx_desc("extract bracketed segments: (), [], {}, <>"))
208 .parse_next(input)
209}
210
211#[inline]
214pub fn duble_quot_str_impl<'a>(input: &mut &'a str) -> WResult<&'a str> {
215 literal('"')
216 .context(ctx_desc("<beg>\""))
217 .parse_next(input)?;
218 let content = take_escaped(none_of(['\\', '"']), '\\', any).parse_next(input)?;
219 literal('"')
220 .context(ctx_desc("<end>\""))
221 .parse_next(input)?;
222 Ok(content)
223}
224#[inline]
225pub fn single_quot_str_impl<'a>(input: &mut &'a str) -> WResult<&'a str> {
226 literal('\'')
227 .context(ctx_desc("<beg>'"))
228 .parse_next(input)?;
229 let content = take_escaped(none_of(['\\', '\'']), '\\', any).parse_next(input)?;
230 literal('\'')
231 .context(ctx_desc("<end>'"))
232 .parse_next(input)?;
233 Ok(content)
234}
235
236#[inline]
239pub fn single_quot_raw_str(input: &mut &str) -> WResult<String> {
240 literal('\'')
241 .context(ctx_desc("<beg>'"))
242 .parse_next(input)?;
243
244 let mut result = String::new();
245 let mut chars = input.chars();
246
247 loop {
248 match chars.next() {
249 None => {
250 return fail
251 .context(ctx_desc("unclosed single quote"))
252 .parse_next(input);
253 }
254 Some('\\') => {
255 match chars.as_str().chars().next() {
257 Some('\'') => {
258 result.push('\'');
259 chars.next(); }
261 _ => {
262 result.push('\\');
264 }
265 }
266 }
267 Some('\'') => {
268 let consumed = input.len() - chars.as_str().len();
270 *input = &input[consumed..];
271 return Ok(result);
272 }
273 Some(ch) => result.push(ch),
274 }
275 }
276}
277
278#[inline]
279pub fn interval_impl<'a>(input: &mut &'a str) -> WResult<&'a str> {
280 let s = *input;
281 let mut chars = s.char_indices();
282 let Some((_, first)) = chars.next() else {
283 return fail
284 .context(ctx_desc("interval requires leading bracket"))
285 .parse_next(input);
286 };
287
288 let Some(first_close) = closing_for_bracket(first) else {
289 return fail
290 .context(ctx_desc("interval must start with [ ( { <"))
291 .parse_next(input);
292 };
293 let mut stack: Vec<char> = vec![first_close];
294 let mut iter = chars.peekable();
295
296 while let Some((idx, ch)) = iter.next() {
297 if ch == '\\' {
298 let _ = iter.next();
300 continue;
301 }
302 match ch {
303 '[' | '(' | '{' | '<' => {
304 if let Some(close) = closing_for_bracket(ch) {
305 stack.push(close);
306 }
307 }
308 ']' | ')' | '}' | '>' => {
309 let expected = stack.pop().unwrap();
310 if ch != expected {
311 return fail
312 .context(ctx_desc("interval bracket mismatch"))
313 .parse_next(input);
314 }
315 if stack.is_empty() {
316 let end = idx + ch.len_utf8();
317 let (matched, rest) = s.split_at(end);
318 *input = rest;
319 return Ok(matched);
320 }
321 }
322 '"' | '\'' => {
323 let quote = ch;
324 let mut escaped = false;
325 for (_, qc) in iter.by_ref() {
326 if escaped {
327 escaped = false;
328 continue;
329 }
330 if qc == '\\' {
331 escaped = true;
332 continue;
333 }
334 if qc == quote {
335 break;
336 }
337 }
338 }
339 _ => {}
340 }
341 }
342
343 fail.context(ctx_desc("interval missing closing bracket"))
344 .parse_next(input)
345}
346
347fn closing_for_bracket(ch: char) -> Option<char> {
348 match ch {
349 '[' => Some(']'),
350 '(' => Some(')'),
351 '{' => Some('}'),
352 '<' => Some('>'),
353 _ => None,
354 }
355}
356
357pub fn window_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
358 literal('"').parse_next(input)?;
359 let content = take_until(0.., "\"").parse_next(input)?;
360 literal('"').parse_next(input)?;
361 Ok(content)
362}
363
364pub fn quot_r_str<'a>(input: &mut &'a str) -> WResult<&'a str> {
367 let s = *input;
368 if let Some(rest) = s.strip_prefix("r#\"") {
370 if let Some(pos) = rest.find("\"#") {
371 let content = &rest[..pos];
372 let new_rest = &rest[pos + 2..];
373 *input = new_rest;
374 return Ok(content);
375 } else {
376 return fail
377 .context(ctx_desc("raw string not closed: r#\"...\"#"))
378 .parse_next(input);
379 }
380 }
381 if let Some(rest) = s.strip_prefix("r\"") {
383 if let Some(pos) = rest.find('"') {
384 let content = &rest[..pos];
385 let new_rest = &rest[pos + 1..];
386 *input = new_rest;
387 return Ok(content);
388 } else {
389 return fail
390 .context(ctx_desc("raw string not closed: r\"...\""))
391 .parse_next(input);
392 }
393 }
394 fail.parse_next(input)
396}
397
398pub fn quot_raw<'a>(input: &mut &'a str) -> WResult<&'a str> {
399 let cp = input.checkpoint();
400 literal('"').parse_next(input)?;
401 let content = take_escaped(none_of(['\\', '"']), '\\', any).parse_next(input)?;
402 literal('"').parse_next(input)?;
403 let len = content.len() + 2;
404 input.reset(&cp);
405 let raw = take(len).parse_next(input)?;
406 Ok(raw)
407}
408
409pub fn take_parentheses<'a>(input: &mut &'a str) -> WResult<&'a str> {
410 literal('(').parse_next(input)?;
411 let content = take_escaped(none_of(['\\', ')']), '\\', one_of([')'])).parse_next(input)?;
412 literal(')').parse_next(input)?;
413 Ok(content)
414}
415
416pub fn decode_escapes(s: &str) -> String {
418 let mut out: Vec<u8> = Vec::with_capacity(s.len());
419 let mut it = s.chars().peekable();
420 while let Some(c) = it.next() {
421 if c == '\\' {
422 match it.peek().copied() {
423 Some('"') => {
424 out.push(b'"');
425 it.next();
426 }
427 Some('\'') => {
428 out.push(b'\'');
429 it.next();
430 }
431 Some('\\') => {
432 out.push(b'\\');
433 it.next();
434 }
435 Some('n') => {
436 out.push(b'\n');
437 it.next();
438 }
439 Some('t') => {
440 out.push(b'\t');
441 it.next();
442 }
443 Some('r') => {
444 out.push(b'\r');
445 it.next();
446 }
447 Some('x') => {
448 it.next();
449 let h1 = it.next();
450 let h2 = it.next();
451 if let (Some(h1), Some(h2)) = (h1, h2) {
452 let hex = [h1, h2];
453 let val = hex
454 .iter()
455 .try_fold(0u8, |v, ch| ch.to_digit(16).map(|d| (v << 4) | (d as u8)));
456 if let Some(b) = val {
457 out.push(b);
458 } else {
459 out.extend_from_slice(b"\\x");
460 out.extend_from_slice(h1.to_string().as_bytes());
461 out.extend_from_slice(h2.to_string().as_bytes());
462 }
463 } else {
464 out.extend_from_slice(b"\\x");
465 if let Some(h1) = h1 {
466 out.extend_from_slice(h1.to_string().as_bytes());
467 }
468 if let Some(h2) = h2 {
469 out.extend_from_slice(h2.to_string().as_bytes());
470 }
471 }
472 }
473 Some(ch) => {
474 out.push(b'\\');
475 out.extend_from_slice(ch.to_string().as_bytes());
476 it.next();
477 }
478 None => {}
479 }
480 } else {
481 let mut buf = [0u8; 4];
482 let s = c.encode_utf8(&mut buf);
483 out.extend_from_slice(s.as_bytes());
484 }
485 }
486 String::from_utf8_lossy(&out).to_string()
487}
488
489pub fn take_tag_kv(input: &mut &str) -> WResult<(SmolStr, SmolStr)> {
490 separated_pair(
492 preceded(multispace0, take_key),
493 (multispace0, ':', multispace0),
494 alt((
495 quot_r_str.map(|s: &str| SmolStr::from(s)),
496 quot_str.map(|s: &str| SmolStr::from(decode_escapes(s))),
497 )),
498 )
499 .map(|(k, v)| (SmolStr::from(k), v))
500 .parse_next(input)
501}
502
503#[inline]
504pub fn take_to_end<'a>(input: &mut &'a str) -> WResult<&'a str> {
505 take_while(0.., |_| true).parse_next(input)
507 }
510
511pub fn peek_str(what: &str, input: &mut &str) -> WResult<()> {
512 match peek(what).parse_next(input) {
515 Ok(_) => Ok(()),
516 Err(e) => Err(ErrMode::Backtrack(e)),
517 }
518}
519
520pub fn peek_next<'a, O, ParseNext>(parser: ParseNext, input: &mut &'a str) -> WResult<()>
521where
522 ParseNext: Parser<&'a str, O, ContextError>,
523{
524 match peek(parser).parse_next(input) {
525 Ok(_) => Ok(()),
526 Err(e) => Err(ErrMode::Backtrack(e)),
527 }
528}
529pub fn is_sep_next(input: &mut &str) -> bool {
530 let _ = multispace0::<&str, ErrMode<ContextError>>.parse_next(input);
531 if peek_str(",", input).is_ok() {
532 let _: Result<&str, ErrMode<ContextError>> = literal(",").parse_next(input);
533 return true;
534 }
535 false
536}
537pub fn is_next_unit(prefix: &str, input: &mut &str) -> bool {
538 let _ = multispace0::<&str, ErrMode<ContextError>>.parse_next(input);
539 if peek_str(prefix, input).is_ok() {
540 return true;
541 }
542 false
543}
544
545pub fn is_next<'a, O, ParseNext>(parser: ParseNext, input: &mut &'a str) -> bool
546where
547 ParseNext: Parser<&'a str, O, ContextError>,
548{
549 let _ = multispace0::<&str, ErrMode<ContextError>>.parse_next(input);
550 if peek_next(parser, input).is_ok() {
551 return true;
552 }
553 false
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559 use crate::parser::error::error_detail;
560 use crate::parser::utils::{
561 chars_value, quot_str, take_key, take_kv_key, take_parentheses, take_to_end,
562 };
563 use crate::parser::wpl_pkg::wpl_package;
564 use orion_error::dev::testing::TestAssert;
565 use winnow::LocatingSlice;
566 use wp_primitives::WResult as ModalResult;
567
568 #[test]
569 fn test_take_val() -> ModalResult<()> {
570 assert_eq!(
571 Value::Chars("key".into()),
572 take_sql_tval.parse_next(&mut "'key'")?
573 );
574 assert_eq!(Value::Digit(100), take_sql_tval.parse_next(&mut "100")?);
575 assert_eq!(
576 Value::Float(100.01),
577 take_sql_tval.parse_next(&mut "100.01")?
578 );
579 assert_eq!(
580 Value::Float(-100.01),
581 take_sql_tval.parse_next(&mut "-100.01")?
582 );
583 Ok(())
584 }
585
586 #[test]
587 fn test_key_ident() {
588 assert_eq!(Ok(("", "key")), take_key.parse_peek("key"));
589 assert_eq!(Ok(("!", "key")), take_key.parse_peek("key!"));
590 assert_eq!(
591 Ok(("!", "http/request")),
592 take_key.parse_peek("http/request!")
593 );
594 assert_eq!(
595 Ok(("!", "123http/request")),
596 take_key.parse_peek("123http/request!")
597 );
598 }
599 #[test]
600 fn test_kv_key_ident() {
601 assert_eq!(Ok(("", "key")), take_kv_key.parse_peek("key"));
603 assert_eq!(Ok(("!", "key")), take_kv_key.parse_peek("key!"));
604 assert_eq!(
605 Ok(("!", "http/request")),
606 take_kv_key.parse_peek("http/request!")
607 );
608 assert_eq!(Ok(("=v", "fn(arg)")), take_kv_key.parse_peek("fn(arg)=v"));
610 assert_eq!(
612 Ok(("=1", "list<int>")),
613 take_kv_key.parse_peek("list<int>=1")
614 );
615 assert_eq!(Ok((":x", "arr[0]")), take_kv_key.parse_peek("arr[0]:x"));
617 assert_eq!(Ok(("=ok", "set{a}")), take_kv_key.parse_peek("set{a}=ok"));
619 assert_eq!(
621 Ok(("=v", "a(b)[c]<d>{e}")),
622 take_kv_key.parse_peek("a(b)[c]<d>{e}=v")
623 );
624 assert_eq!(Ok(("=val", "key(x)")), take_kv_key.parse_peek("key(x)=val"));
626 assert_eq!(Ok((":val", "key(x)")), take_kv_key.parse_peek("key(x):val"));
627 }
628 #[test]
629 fn test_quot_str() {
630 assert_eq!(quot_str.parse_peek("\"123\""), Ok(("", "123")));
631 assert_eq!(quot_str.parse_peek(r#""\a123""#), Ok(("", r#"\a123"#)));
632 assert_eq!(quot_str.parse_peek("'123'"), Ok(("", "123")));
633 assert_eq!(quot_str.parse_peek("\"1-?#ab\""), Ok(("", "1-?#ab")));
634 assert_eq!(quot_str.parse_peek(r#""12\"3""#), Ok(("", r#"12\"3"#)));
635 assert_eq!(quot_str.parse_peek(r#"'12\"3'"#), Ok(("", r#"12\"3"#)));
636 assert_eq!(quot_str.parse_peek("\"中文🙂\""), Ok(("", "中文🙂")));
638 assert_eq!(
640 window_path.parse_peek(r#""sddD:\招标项目\6-MSS\mss日志映射表""#),
641 Ok(("", r#"sddD:\招标项目\6-MSS\mss日志映射表"#))
642 );
643 }
644 #[test]
645 fn test_quot_r_str() {
646 use crate::parser::utils::quot_r_str;
647 assert_eq!(
649 quot_r_str.parse_peek("r#\"a\\b \"c\"\"#"),
650 Ok(("", "a\\b \"c\""))
651 );
652 assert_eq!(quot_r_str.parse_peek("r#\"end\"#"), Ok(("", "end")));
653 assert_eq!(quot_r_str.parse_peek("r\"raw\""), Ok(("", "raw")));
655 }
656 #[test]
657 fn test_chars_value_dispatch() {
658 assert_eq!(
659 chars_value.parse_peek(r#""GET / HTTP/1.1""#),
660 Ok(("", "GET / HTTP/1.1"))
661 );
662 assert_eq!(
663 chars_value.parse_peek("r#\"raw value\"#"),
664 Ok(("", "raw value"))
665 );
666 assert_eq!(
667 chars_value.parse_peek(r#""unterminated fallback"#),
668 Ok(("", r#""unterminated fallback"#))
669 );
670 assert_eq!(
671 chars_value.parse_peek("plain chars"),
672 Ok(("", "plain chars"))
673 );
674 }
675 #[test]
676 fn test_take_pat() {
677 assert_eq!(take_parentheses.parse_peek("(123)"), Ok(("", "123")));
678 assert_eq!(
679 take_parentheses.parse_peek(r#"(12\)3)"#),
680 Ok(("", r#"12\)3"#))
681 );
682 }
683
684 #[test]
685 fn test_take_to_end() {
686 let input = "";
687 let x = take_to_end.parse(input).assert();
688 assert_eq!(x, "");
689
690 let input = "hello 你好 😂 😁 π \u{3001} \n \t en";
691 let x = take_to_end.parse(input).assert();
692 assert_eq!(x, input);
693 }
694
695 #[test]
696 fn test_prefix() {
697 let data = "{ (digit, time,sn,chars,time,kv,ip,kv,chars,kv,kv,chars,kv,kv,chars,chars,ip,chars,http/request,http/agent)}";
698 if let Err(err) = crate::parser::parse_code::segment.parse(data) {
699 println!("{}", error_detail(err));
700 }
701 assert_eq!(
702 crate::parser::parse_code::segment
703 .parse(data)
704 .assert()
705 .to_string(),
706 r#" (
707 digit,
708 time,
709 sn,
710 chars,
711 time,
712 kv,
713 ip,
714 kv,
715 chars,
716 kv,
717 kv,
718 chars,
719 kv,
720 kv,
721 chars,
722 chars,
723 ip,
724 chars,
725 http/request,
726 http/agent
727 )"#
728 );
729 }
730 #[test]
731 fn test_meta() {
732 let input = r#" package test {
733 rule test { (
734 time,
735 time_timestamp
736 ) }
737 }
738 "#;
739
740 assert_eq!(
741 wpl_package
742 .parse(&LocatingSlice::new(input))
743 .assert()
744 .to_string(),
745 r#"package test {
746 rule test {
747 (
748 time,
749 time_timestamp
750 )
751 }
752}
753"#
754 );
755 }
756
757 #[test]
758 fn test_tag_kv_hex_escape() {
759 use super::take_tag_kv;
760 let mut s = "key:\"\\xE4\\xB8\\xAD\\xE6\\x96\\x87\"";
761 let (k, v) = take_tag_kv.parse_next(&mut s).assert();
762 assert_eq!(k, "key");
763 assert_eq!(v, "中文");
764 }
765
766 #[test]
767 fn test_interval_simple() {
768 let mut input = "{payload}";
769 let parsed = interval_data(&mut input).assert();
770 assert_eq!(parsed, "{payload}");
771 assert_eq!(input, "");
772 }
773
774 #[test]
775 fn test_interval_nested_with_quotes() {
776 let mut input = "<({\"[(foo)]\"}, ['x'])>tail";
777 let parsed = interval_data(&mut input).assert();
778 assert_eq!(parsed, "<({\"[(foo)]\"}, ['x'])>");
779 assert_eq!(input, "tail");
780 }
781
782 #[test]
783 fn test_interval_missing_closer() {
784 let mut input = "[1,2";
785 assert!(interval_data(&mut input).is_err());
786 }
787
788 #[test]
789 fn test_take_ref_path_or_quoted() {
790 assert_eq!(
792 take_ref_path_or_quoted.parse_peek("field_name"),
793 Ok(("", "field_name".to_string()))
794 );
795
796 assert_eq!(
798 take_ref_path_or_quoted.parse_peek("'@abc'"),
799 Ok(("", "@abc".to_string()))
800 );
801
802 assert_eq!(
804 take_ref_path_or_quoted.parse_peek("'field with spaces'"),
805 Ok(("", "field with spaces".to_string()))
806 );
807
808 assert_eq!(
810 take_ref_path_or_quoted.parse_peek("'@special-field#123'"),
811 Ok(("", "@special-field#123".to_string()))
812 );
813
814 let input = "'field\\'s name'";
816 assert_eq!(
817 take_ref_path_or_quoted.parse_peek(input),
818 Ok(("", "field's name".to_string()))
819 );
820
821 assert_eq!(
823 take_ref_path_or_quoted.parse_peek("process/path[0]"),
824 Ok(("", "process/path[0]".to_string()))
825 );
826 assert_eq!(
827 take_ref_path_or_quoted.parse_peek("list<int>"),
828 Ok(("", "list<int>".to_string()))
829 );
830 assert_eq!(
831 take_ref_path_or_quoted.parse_peek("set{a}"),
832 Ok(("", "set{a}".to_string()))
833 );
834 assert_eq!(
835 take_ref_path_or_quoted.parse_peek("curr<[,]>"),
836 Ok(("<[,]>", "curr".to_string()))
837 );
838 assert_eq!(
839 take_ref_path_or_quoted.parse_peek("curr{\\s(\\S=)}"),
840 Ok(("{\\s(\\S=)}", "curr".to_string()))
841 );
842
843 assert_eq!(
844 take_ref_path_or_quoted.parse_peek("protocal(80)"),
845 Ok(("", "protocal(80)".to_string()))
846 );
847 assert_eq!(
848 take_ref_path_or_quoted.parse_peek("protocal(80))"),
849 Ok((")", "protocal(80)".to_string()))
850 );
851
852 assert_eq!(
854 take_ref_path_or_quoted.parse_peek(r"'raw\nstring'"),
855 Ok(("", r"raw\nstring".to_string()))
856 );
857
858 assert_eq!(
859 take_ref_path_or_quoted.parse_peek(r"'path\to\file'"),
860 Ok(("", r"path\to\file".to_string()))
861 );
862
863 assert_eq!(
865 take_ref_path_or_quoted.parse_peek(r"'it\'s here'"),
866 Ok(("", "it's here".to_string()))
867 );
868 }
869}