1use super::Error;
18use super::ErrorCause;
19use super::Parser;
20use super::SyntaxError;
21use super::lex::Lexer;
22use super::lex::Operator;
23use super::lex::ParseOperatorError;
24use super::lex::Token;
25use super::lex::TokenId;
26use super::lex::WordContext;
27use super::lex::WordLexer;
28use crate::syntax::*;
29use std::str::FromStr;
30
31fn unwrap_ready<F: Future>(f: F) -> <F as Future>::Output {
33 use futures_util::future::FutureExt;
34 f.now_or_never()
35 .expect("Expected Ready but received Pending")
36}
37
38async fn reject_redundant_token(parser: &mut Parser<'_, '_>) -> Result<(), Error> {
40 let token = parser.take_token_raw().await?;
41 if token.id == TokenId::EndOfInput {
42 Ok(())
43 } else {
44 Err(Error {
45 cause: ErrorCause::Syntax(SyntaxError::RedundantToken),
46 location: token.word.location,
47 })
48 }
49}
50
51trait Shift {
53 type Output;
54 fn shift(self) -> Self::Output;
55}
56
57impl<T, E> Shift for Result<Option<T>, E> {
58 type Output = Result<T, Option<E>>;
59 fn shift(self) -> Result<T, Option<E>> {
60 match self {
61 Ok(Some(t)) => Ok(t),
62 Ok(None) => Err(None),
63 Err(e) => Err(Some(e)),
64 }
65 }
66}
67
68impl FromStr for BracedParam {
69 type Err = Option<Error>;
75
76 fn from_str(s: &str) -> Result<BracedParam, Option<Error>> {
77 match TextUnit::from_str(s)? {
78 TextUnit::BracedParam(param) => Ok(param),
79 _ => Err(None),
80 }
81 }
82}
83
84impl FromStr for TextUnit {
86 type Err = Option<Error>;
91
92 fn from_str(s: &str) -> Result<TextUnit, Option<Error>> {
93 let mut lexer = Lexer::with_code(s);
94 let mut lexer = WordLexer {
95 lexer: &mut lexer,
96 context: WordContext::Word,
97 };
98 unwrap_ready(lexer.text_unit(|_| false, |_| true)).shift()
99 }
100}
101
102impl FromStr for Text {
104 type Err = Error;
105 fn from_str(s: &str) -> Result<Text, Error> {
106 let mut lexer = Lexer::with_code(s);
107 unwrap_ready(lexer.text(|_| false, |_| true))
108 }
109}
110
111impl FromStr for EscapeUnit {
112 type Err = Option<Error>;
117
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 let mut lexer = Lexer::with_code(s);
120 unwrap_ready(lexer.escape_unit()).shift()
121 }
122}
123
124impl FromStr for EscapedString {
126 type Err = Error;
127 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 let mut lexer = Lexer::with_code(s);
129 unwrap_ready(lexer.escaped_string(|_| false))
130 }
131}
132
133impl FromStr for WordUnit {
134 type Err = Option<Error>;
139
140 fn from_str(s: &str) -> Result<WordUnit, Option<Error>> {
141 let mut lexer = Lexer::with_code(s);
142 let mut lexer = WordLexer {
143 lexer: &mut lexer,
144 context: WordContext::Word,
145 };
146 unwrap_ready(lexer.word_unit(|_| false)).shift()
147 }
148}
149
150impl FromStr for Word {
156 type Err = Error;
157
158 fn from_str(s: &str) -> Result<Word, Error> {
159 let mut lexer = Lexer::with_code(s);
160 let mut lexer = WordLexer {
161 lexer: &mut lexer,
162 context: WordContext::Word,
163 };
164 unwrap_ready(lexer.word(|_| false))
165 }
166}
167
168impl FromStr for Value {
169 type Err = Error;
170 fn from_str(s: &str) -> Result<Value, Error> {
171 let s = format!("x={s}");
172 let a = s.parse::<Assign>().map_err(Option::unwrap)?;
173 Ok(a.value)
174 }
175}
176
177impl FromStr for Assign {
179 type Err = Option<Error>;
184
185 fn from_str(s: &str) -> Result<Assign, Option<Error>> {
186 let mut c = s.parse::<SimpleCommand>()?;
187
188 match c.assigns.pop() {
189 Some(last) if c.assigns.is_empty() => {
190 if let Some(word) = c.words.pop() {
191 Err(Some(Error {
192 cause: ErrorCause::Syntax(SyntaxError::RedundantToken),
193 location: word.0.location,
194 }))
195 } else if let Some(redir) = c.redirs.first() {
196 Err(Some(Error {
197 cause: ErrorCause::Syntax(SyntaxError::RedundantToken),
198 location: redir.body.operand().location.clone(),
199 }))
200 } else {
201 Ok(last)
202 }
203 }
204 Some(last) => Err(Some(Error {
205 cause: ErrorCause::Syntax(SyntaxError::RedundantToken),
206 location: last.location,
207 })),
208 None => Err(None),
209 }
210 }
211}
212
213impl FromStr for Operator {
214 type Err = ParseOperatorError;
215 fn from_str(s: &str) -> Result<Operator, ParseOperatorError> {
216 let mut lexer = Lexer::with_code(s);
217 match unwrap_ready(lexer.operator()) {
218 Ok(Some(Token {
219 id: TokenId::Operator(op),
220 ..
221 })) => Ok(op),
222
223 _ => Err(ParseOperatorError),
224 }
225 }
226}
227
228impl FromStr for RedirOp {
229 type Err = ParseOperatorError;
230 fn from_str(s: &str) -> Result<RedirOp, ParseOperatorError> {
231 Operator::from_str(s)?
232 .try_into()
233 .map_err(|_| ParseOperatorError)
234 }
235}
236
237impl FromStr for Redir {
241 type Err = Option<Error>;
246
247 fn from_str(s: &str) -> Result<Redir, Option<Error>> {
248 let mut lexer = Lexer::with_code(s);
249 let mut parser = Parser::new(&mut lexer);
250 unwrap_ready(async {
251 let redir = parser.redirection().await?;
252 if redir.is_some() {
253 reject_redundant_token(&mut parser).await?;
254 parser.ensure_no_unread_here_doc()?;
258 }
259 Ok(redir)
260 })
261 .shift()
262 }
263}
264
265impl FromStr for SimpleCommand {
270 type Err = Option<Error>;
275
276 fn from_str(s: &str) -> Result<SimpleCommand, Option<Error>> {
277 let mut lexer = Lexer::with_code(s);
278 let mut parser = Parser::new(&mut lexer);
279 unwrap_ready(async {
280 let command = parser.simple_command().await?.unwrap();
281 if command.is_some() {
282 reject_redundant_token(&mut parser).await?;
283 parser.ensure_no_unread_here_doc()?;
287 }
288 Ok(command)
289 })
290 .shift()
291 }
292}
293
294impl FromStr for CaseItem {
296 type Err = Option<Error>;
301
302 fn from_str(s: &str) -> Result<CaseItem, Option<Error>> {
303 let mut lexer = Lexer::with_code(s);
304 let mut parser = Parser::new(&mut lexer);
305 unwrap_ready(async {
306 let item = parser.case_item().await?.map(|(item, _)| item);
307 if item.is_some() {
308 if parser.peek_token().await?.id == TokenId::Operator(Operator::SemicolonSemicolon)
309 {
310 parser.take_token_raw().await?;
311 }
312 reject_redundant_token(&mut parser).await?;
313 parser.ensure_no_unread_here_doc()?;
314 }
315 Ok(item)
316 })
317 .shift()
318 }
319}
320
321impl FromStr for CompoundCommand {
323 type Err = Option<Error>;
328
329 fn from_str(s: &str) -> Result<CompoundCommand, Option<Error>> {
330 let mut lexer = Lexer::with_code(s);
331 let mut parser = Parser::new(&mut lexer);
332 unwrap_ready(async {
333 let command = parser.compound_command().await?;
334 if command.is_some() {
335 reject_redundant_token(&mut parser).await?;
336 parser.ensure_no_unread_here_doc()?;
337 }
338 Ok(command)
339 })
340 .shift()
341 }
342}
343
344impl FromStr for FullCompoundCommand {
346 type Err = Option<Error>;
351
352 fn from_str(s: &str) -> Result<FullCompoundCommand, Option<Error>> {
353 let mut lexer = Lexer::with_code(s);
354 let mut parser = Parser::new(&mut lexer);
355 unwrap_ready(async {
356 let command = parser.full_compound_command().await?;
357 if command.is_some() {
358 reject_redundant_token(&mut parser).await?;
359 parser.ensure_no_unread_here_doc()?;
360 }
361 Ok(command)
362 })
363 .shift()
364 }
365}
366
367impl FromStr for Command {
369 type Err = Option<Error>;
374
375 fn from_str(s: &str) -> Result<Command, Option<Error>> {
376 let mut lexer = Lexer::with_code(s);
377 let mut parser = Parser::new(&mut lexer);
378 unwrap_ready(async {
379 let command = parser.command().await?.unwrap();
380 if command.is_some() {
381 reject_redundant_token(&mut parser).await?;
382 parser.ensure_no_unread_here_doc()?;
383 }
384 Ok(command)
385 })
386 .shift()
387 }
388}
389
390impl FromStr for Pipeline {
392 type Err = Option<Error>;
397
398 fn from_str(s: &str) -> Result<Pipeline, Option<Error>> {
399 let mut lexer = Lexer::with_code(s);
400 let mut parser = Parser::new(&mut lexer);
401 unwrap_ready(async {
402 let pipeline = parser.pipeline().await?.unwrap();
403 if pipeline.is_some() {
404 reject_redundant_token(&mut parser).await?;
405 parser.ensure_no_unread_here_doc()?;
406 }
407 Ok(pipeline)
408 })
409 .shift()
410 }
411}
412
413impl FromStr for AndOr {
414 type Err = ParseOperatorError;
415 fn from_str(s: &str) -> Result<AndOr, ParseOperatorError> {
416 Operator::from_str(s)?
417 .try_into()
418 .map_err(|_| ParseOperatorError)
419 }
420}
421
422impl FromStr for AndOrList {
424 type Err = Option<Error>;
429
430 fn from_str(s: &str) -> Result<AndOrList, Option<Error>> {
431 let mut lexer = Lexer::with_code(s);
432 let mut parser = Parser::new(&mut lexer);
433 unwrap_ready(async {
434 let list = parser.and_or_list().await?.unwrap();
435 if list.is_some() {
436 reject_redundant_token(&mut parser).await?;
437 parser.ensure_no_unread_here_doc()?;
438 }
439 Ok(list)
440 })
441 .shift()
442 }
443}
444
445impl FromStr for List {
447 type Err = Error;
448 fn from_str(s: &str) -> Result<List, Error> {
449 let mut lexer = Lexer::with_code(s);
450 let mut parser = Parser::new(&mut lexer);
451 let list = unwrap_ready(parser.maybe_compound_list())?;
452 parser.ensure_no_unread_here_doc()?;
453 Ok(list)
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use assert_matches::assert_matches;
461 use futures_executor::block_on;
462
463 #[test]
467 fn param_from_str() {
468 block_on(async {
469 let parse: BracedParam = "${foo}".parse().unwrap();
470 assert_eq!(parse.to_string(), "${foo}");
471
472 let error = "".parse::<BracedParam>().unwrap_err();
473 assert!(error.is_none(), "{error:?}");
474
475 let error = "foo".parse::<BracedParam>().unwrap_err();
476 assert!(error.is_none(), "{error:?}");
477 })
478 }
479
480 #[test]
481 fn text_unit_from_str() {
482 block_on(async {
483 let parse: TextUnit = "a".parse().unwrap();
484 assert_eq!(parse.to_string(), "a");
485
486 let error = "".parse::<TextUnit>().unwrap_err();
487 assert!(error.is_none(), "{error:?}");
488 })
489 }
490
491 #[test]
492 fn text_from_str() {
493 block_on(async {
494 let parse: Text = r"a\b$(c)".parse().unwrap();
495 assert_eq!(parse.0.len(), 3);
496 assert_eq!(parse.0[0], Literal('a'));
497 assert_eq!(parse.0[1], Backslashed('b'));
498 assert_matches!(&parse.0[2], CommandSubst { content, .. } => {
499 assert_eq!(&**content, "c");
500 });
501 })
502 }
503
504 #[test]
505 fn escape_unit_from_str() {
506 block_on(async {
507 let parse: EscapeUnit = r"\n".parse().unwrap();
508 assert_eq!(parse, EscapeUnit::Newline);
509 })
510 }
511
512 #[test]
513 fn escaped_string_from_str() {
514 block_on(async {
515 let parse: EscapedString = r"a\nb".parse().unwrap();
516 use EscapeUnit::*;
517 assert_eq!(parse.0, [Literal('a'), Newline, Literal('b')]);
518 })
519 }
520
521 #[test]
522 fn word_unit_from_str() {
523 block_on(async {
524 let parse: WordUnit = "a".parse().unwrap();
525 assert_eq!(parse.to_string(), "a");
526
527 let error = "".parse::<WordUnit>().unwrap_err();
528 assert!(error.is_none(), "{error:?}");
529 })
530 }
531
532 #[test]
533 fn word_from_str() {
534 block_on(async {
535 let parse: Word = "a".parse().unwrap();
536 assert_eq!(parse.to_string(), "a");
537 })
538 }
539
540 #[test]
541 fn value_from_str() {
542 block_on(async {
543 let parse: Value = "v".parse().unwrap();
544 assert_eq!(parse.to_string(), "v");
545
546 let parse: Value = "(1 2 3)".parse().unwrap();
547 assert_eq!(parse.to_string(), "(1 2 3)");
548 })
549 }
550
551 #[test]
552 fn assign_from_str() {
553 block_on(async {
554 let parse: Assign = "a=b".parse().unwrap();
555 assert_eq!(parse.to_string(), "a=b");
556
557 let parse: Assign = "x=(1 2 3)".parse().unwrap();
558 assert_eq!(parse.to_string(), "x=(1 2 3)");
559 })
560 }
561
562 #[test]
563 fn assign_from_str_empty() {
564 block_on(async {
565 let e = "".parse::<Assign>().unwrap_err();
566 assert!(e.is_none(), "{e:?}");
567 })
568 }
569
570 #[test]
571 fn assign_from_str_redundant_word() {
572 block_on(async {
573 let e = "a=b c".parse::<Assign>().unwrap_err().unwrap();
574 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
575 assert_eq!(*e.location.code.value.borrow(), "a=b c");
576 assert_eq!(e.location.range, 4..5);
577 })
578 }
579
580 #[test]
581 fn assign_from_str_redundant_redir() {
582 block_on(async {
583 let e = "a=b <c".parse::<Assign>().unwrap_err().unwrap();
584 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
585 assert_eq!(*e.location.code.value.borrow(), "a=b <c");
586 assert_eq!(e.location.range, 5..6);
587 })
588 }
589
590 #[test]
591 fn assign_from_str_redundant_assign() {
592 block_on(async {
593 let e = "a=b c=".parse::<Assign>().unwrap_err().unwrap();
594 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
595 assert_eq!(*e.location.code.value.borrow(), "a=b c=");
596 assert_eq!(e.location.range, 4..6);
597 })
598 }
599
600 #[test]
601 fn operator_from_str() {
602 block_on(async {
603 let parse: Operator = "<<".parse().unwrap();
604 assert_eq!(parse, Operator::LessLess);
605 })
606 }
607
608 #[test]
609 fn redir_op_from_str() {
610 block_on(async {
611 let parse: RedirOp = ">|".parse().unwrap();
612 assert_eq!(parse, RedirOp::FileClobber);
613 })
614 }
615
616 #[test]
617 fn redir_from_str() {
618 block_on(async {
619 let parse: Redir = "2> /dev/null".parse().unwrap();
620 assert_eq!(parse.fd, Some(Fd(2)));
621 assert_matches!(parse.body, RedirBody::Normal { operator, operand } => {
622 assert_eq!(operator, RedirOp::FileOut);
623 assert_eq!(operand.to_string(), "/dev/null");
624 });
625 })
626 }
627
628 #[test]
629 fn redir_from_str_redundant_token() {
630 block_on(async {
631 let e = "2> /dev/null x".parse::<Redir>().unwrap_err().unwrap();
632 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
633 assert_eq!(*e.location.code.value.borrow(), "2> /dev/null x");
634 assert_eq!(e.location.range, 13..14);
635 })
636 }
637
638 #[test]
639 fn redir_from_str_unfillable_here_doc_content() {
640 block_on(async {
641 let result: Result<Redir, _> = "<<FOO".parse();
642 let e = result.unwrap_err().unwrap();
643 assert_eq!(
644 e.cause,
645 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
646 );
647 })
648 }
649
650 #[test]
651 fn simple_command_from_str() {
652 block_on(async {
653 let parse: SimpleCommand = " a=b</dev/null foo ".parse().unwrap();
654 assert_eq!(parse.to_string(), "a=b foo </dev/null");
655 })
656 }
657
658 #[test]
659 fn simple_command_from_str_empty() {
660 block_on(async {
661 let e = "".parse::<SimpleCommand>().unwrap_err();
662 assert_eq!(e, None);
663
664 let e = "if".parse::<SimpleCommand>().unwrap_err();
665 assert_eq!(e, None);
666 })
667 }
668
669 #[test]
670 fn simple_command_from_str_unfillable_here_doc_content() {
671 block_on(async {
672 let result: Result<SimpleCommand, _> = "<<FOO".parse();
673 let e = result.unwrap_err().unwrap();
674 assert_eq!(
675 e.cause,
676 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
677 );
678 })
679 }
680
681 #[test]
682 fn simple_command_from_str_redundant_token() {
683 block_on(async {
684 let e = "x\n".parse::<SimpleCommand>().unwrap_err().unwrap();
685 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
686 assert_eq!(*e.location.code.value.borrow(), "x\n");
687 assert_eq!(e.location.range, 1..2);
688 })
689 }
690
691 #[test]
692 fn case_item_from_str() {
693 block_on(async {
694 let parse: CaseItem = " foo) ".parse().unwrap();
695 assert_eq!(parse.to_string(), "(foo) ;;");
696 })
697 }
698
699 #[test]
700 fn case_item_from_str_with_double_semicolon() {
701 block_on(async {
702 let parse: CaseItem = " foo) ;; ".parse().unwrap();
703 assert_eq!(parse.to_string(), "(foo) ;;");
704
705 let parse: CaseItem = " foo) echo;; ".parse().unwrap();
706 assert_eq!(parse.to_string(), "(foo) echo;;");
707 })
708 }
709
710 #[test]
711 fn case_item_from_str_empty() {
712 block_on(async {
713 let e = "esac".parse::<CaseItem>().unwrap_err();
714 assert_eq!(e, None);
715 })
716 }
717
718 #[test]
719 fn case_item_from_str_unfillable_here_doc_content() {
720 block_on(async {
721 let result: Result<CaseItem, _> = "(foo) <<FOO".parse();
722 let e = result.unwrap_err().unwrap();
723 assert_eq!(
724 e.cause,
725 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
726 );
727 })
728 }
729
730 #[test]
731 fn case_item_from_str_redundant_token() {
732 block_on(async {
733 let e = "foo)fi".parse::<CaseItem>().unwrap_err().unwrap();
734 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
735 assert_eq!(*e.location.code.value.borrow(), "foo)fi");
736 assert_eq!(e.location.range, 4..6);
737 })
738 }
739
740 #[test]
741 fn compound_command_from_str() {
742 block_on(async {
743 let parse: CompoundCommand = " { :; } ".parse().unwrap();
744 assert_eq!(parse.to_string(), "{ :; }");
745 })
746 }
747
748 #[test]
749 fn compound_command_from_str_redundant_token() {
750 block_on(async {
751 let e = " { :; } x".parse::<CompoundCommand>().unwrap_err().unwrap();
752 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
753 assert_eq!(*e.location.code.value.borrow(), " { :; } x");
754 assert_eq!(e.location.range, 8..9);
755 })
756 }
757
758 #[test]
759 fn compound_command_from_str_unfillable_here_doc_content() {
760 block_on(async {
761 let result: Result<CompoundCommand, _> = "{ <<FOO; }".parse();
762 let e = result.unwrap_err().unwrap();
763 assert_eq!(
764 e.cause,
765 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
766 );
767 })
768 }
769
770 #[test]
771 fn full_compound_command_from_str() {
772 block_on(async {
773 let parse: FullCompoundCommand = " { :; } <&- ".parse().unwrap();
774 assert_eq!(parse.to_string(), "{ :; } <&-");
775 })
776 }
777
778 #[test]
779 fn full_compound_command_from_str_redundant_token() {
780 block_on(async {
781 let e = " { :; } <&- ;"
782 .parse::<FullCompoundCommand>()
783 .unwrap_err()
784 .unwrap();
785 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
786 assert_eq!(*e.location.code.value.borrow(), " { :; } <&- ;");
787 assert_eq!(e.location.range, 12..13);
788 })
789 }
790
791 #[test]
792 fn full_compound_command_from_str_unfillable_here_doc_content() {
793 block_on(async {
794 let result: Result<FullCompoundCommand, _> = "{ :; } <<FOO".parse();
795 let e = result.unwrap_err().unwrap();
796 assert_eq!(
797 e.cause,
798 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
799 );
800 })
801 }
802
803 #[test]
804 fn command_from_str() {
805 block_on(async {
806 let parse: Command = "f(){ :; }>&2".parse().unwrap();
807 assert_eq!(parse.to_string(), "f() { :; } >&2");
808 })
809 }
810
811 #[test]
812 fn command_from_str_redundant_token() {
813 block_on(async {
814 let e = "f(){ :; }>&2 ;".parse::<Command>().unwrap_err().unwrap();
815 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
816 assert_eq!(*e.location.code.value.borrow(), "f(){ :; }>&2 ;");
817 assert_eq!(e.location.range, 13..14);
818 })
819 }
820
821 #[test]
822 fn command_from_str_unfillable_here_doc_content() {
823 block_on(async {
824 let result: Result<Command, _> = "<<FOO".parse();
825 let e = result.unwrap_err().unwrap();
826 assert_eq!(
827 e.cause,
828 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
829 );
830 })
831 }
832
833 #[test]
834 fn pipeline_from_str() {
835 block_on(async {
836 let parse: Pipeline = " ! a|b|c".parse().unwrap();
837 assert_eq!(parse.to_string(), "! a | b | c");
838 })
839 }
840
841 #[test]
842 fn pipeline_from_str_redundant_token() {
843 block_on(async {
844 let e = "a|b|c ;".parse::<Pipeline>().unwrap_err().unwrap();
845 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
846 assert_eq!(*e.location.code.value.borrow(), "a|b|c ;");
847 assert_eq!(e.location.range, 6..7);
848 })
849 }
850
851 #[test]
852 fn pipeline_from_str_unfillable_here_doc_content() {
853 block_on(async {
854 let result: Result<Pipeline, _> = "<<FOO".parse();
855 let e = result.unwrap_err().unwrap();
856 assert_eq!(
857 e.cause,
858 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
859 );
860 })
861 }
862
863 #[test]
864 fn and_or_from_str() {
865 assert_eq!(AndOr::from_str("&&"), Ok(AndOr::AndThen));
866 assert_eq!(AndOr::from_str("||"), Ok(AndOr::OrElse));
867 }
868
869 #[test]
870 fn and_or_list_from_str() {
871 block_on(async {
872 let parse: AndOrList = " a|b&&! c||d|e ".parse().unwrap();
873 assert_eq!(parse.to_string(), "a | b && ! c || d | e");
874 })
875 }
876
877 #[test]
878 fn and_or_list_from_str_redundant_token() {
879 block_on(async {
880 let e = "a||b;".parse::<AndOrList>().unwrap_err().unwrap();
881 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::RedundantToken));
882 assert_eq!(*e.location.code.value.borrow(), "a||b;");
883 assert_eq!(e.location.range, 4..5);
884 })
885 }
886
887 #[test]
888 fn and_or_list_from_str_unfillable_here_doc_content() {
889 block_on(async {
890 let result: Result<AndOrList, _> = "<<FOO".parse();
891 let e = result.unwrap_err().unwrap();
892 assert_eq!(
893 e.cause,
894 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
895 );
896 })
897 }
898
899 #[test]
900 fn list_from_str() {
901 block_on(async {
902 let parse: List = " a;b&&c&d ".parse().unwrap();
903 assert_eq!(parse.to_string(), "a; b && c& d");
904 })
905 }
906
907 #[test]
908 fn list_from_str_unfillable_here_doc_content() {
909 block_on(async {
910 let result: Result<List, _> = "<<FOO".parse();
911 let e = result.unwrap_err();
912 assert_eq!(
913 e.cause,
914 ErrorCause::Syntax(SyntaxError::MissingHereDocContent)
915 );
916 })
917 }
918}