diff options
Diffstat (limited to 'src/poem.rs')
-rw-r--r-- | src/poem.rs | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/poem.rs b/src/poem.rs new file mode 100644 index 0000000..c0e7d6b --- /dev/null +++ b/src/poem.rs @@ -0,0 +1,284 @@ +mod elements; +use elements::verse::Verse; +pub mod read; +pub mod recite; + +/// Parse and run shell commands or `dwvsh` files +/// +/// A [Poem] is the structure that contains a full shell command/program. It +/// may be composed of one or many [Verse]'s. +pub type Poem = Vec<Verse>; + +#[cfg(test)] +mod tests { + use super::elements::rune::Rune; + use super::read::Readable; + use super::*; + + #[test] + fn it_parses_a_verse_with_no_meter() { + let poem = Poem::read("cargo build --release".to_string()); + assert!(poem.is_ok()); + let poem = poem.unwrap(); + assert_eq!(poem.first().unwrap().verb(), "cargo"); + } + + #[test] + fn it_parses_a_verse_with_the_couplet_meter() { + let poem = Poem::read("ls -la | lolcat".to_string()); + assert!(poem.is_ok()); + let poem = poem.unwrap(); + assert_eq!(poem.first().unwrap().verb(), "ls"); + assert_eq!(poem.first().unwrap().meter, Rune::Couplet); + } + + #[test] + fn it_parses_a_verse_with_the_quiet_meter() { + let poem = Poem::read("sleep 20 &".to_string()); + assert!(poem.is_ok()); + let poem = poem.unwrap(); + assert_eq!(poem.first().unwrap().verb(), "sleep"); + assert_eq!(poem.first().unwrap().meter, Rune::Quiet); + } + + #[test] + fn it_parses_a_verse_with_the_and_meter() { + let poem = Poem::read("sleep 2 && ls -la".to_string()); + assert!(poem.is_ok()); + let poem = poem.unwrap(); + assert_eq!(poem.first().unwrap().verb(), "sleep"); + assert_eq!(poem.first().unwrap().meter, Rune::And); + } + + #[test] + fn it_parses_a_verse_with_the_continue_meter() { + let poem = Poem::read("sleep 2; ls -la".to_string()); + assert!(poem.is_ok()); + let poem = poem.unwrap(); + assert_eq!(poem.first().unwrap().verb(), "sleep"); + assert_eq!(poem.first().unwrap().meter, Rune::Continue); + } + + // #[test] + // fn it_parses_verse_with_the_read_meter() { + // let poem = Poem::read("lolcat < src/main.rs".to_string()); + // assert!(poem.is_ok()); + // let mut verses = poem.unwrap().into_iter(); + + // let verse = verses.next().unwrap(); + // assert_eq!(verse.verb(), "lolcat"); + // assert_eq!(verse.meter, Rune::Read); + + // let verse = verses.next().unwrap(); + // assert_eq!(verse.stanza, vec!["src/main.rs".to_string()]); + // } + + // #[test] + // fn it_parses_verse_with_the_write_meter() { + // let poem = Poem::read("cat src/main.rs > /dev/null".to_string()); + // assert!(poem.is_ok()); + // let mut verses = poem.unwrap().into_iter(); + + // let verse = verses.next().unwrap(); + // assert_eq!(verse.verb(), "cat"); + // assert_eq!(verse.clause().unwrap(), vec!["src/main.rs".to_string()]); + // assert_eq!(verse.meter, Rune::Write); + + // let verse = verses.next().unwrap(); + // assert_eq!(verse.stanza, vec!["/dev/null".to_string()]); + // } + + // #[test] + // fn it_parses_verse_with_the_addenum_meter() { + // let poem = Poem::read("cat src/main.rs >> /dev/null".to_string()); + // assert!(poem.is_ok()); + // let mut verses = poem.unwrap().into_iter(); + + // let verse = verses.next().unwrap(); + // assert_eq!(verse.verb(), "cat"); + // assert_eq!(verse.clause().unwrap(), vec!["src/main.rs".to_string()]); + // assert_eq!(verse.meter, Rune::Addendum); + + // let verse = verses.next().unwrap(); + // assert_eq!(verse.stanza, vec!["/dev/null".to_string()]); + // } + + #[test] + fn it_throws_a_parse_error_if_no_files_are_specified_for_the_read_meter() { + let poem = Poem::read("lolcat <".to_string()); + assert!(poem.is_err()); + let poem = Poem::read("lolcat <;".to_string()); + assert!(poem.is_err()); + let poem = Poem::read("lolcat < && ls -la".to_string()); + assert!(poem.is_err()); + } + + #[test] + fn it_throws_a_parse_error_if_no_files_are_specified_for_the_write_meter() { + let poem = Poem::read("cat src/main.rs >".to_string()); + assert!(poem.is_err()); + let poem = Poem::read("cat src/main.rs >;".to_string()); + assert!(poem.is_err()); + let poem = Poem::read("cat > && ls -la".to_string()); + assert!(poem.is_err()); + } + + #[test] + fn it_throws_a_parse_error_if_no_files_are_specified_for_the_addendum_meter() { + let poem = Poem::read("cat src/main.rs >>".to_string()); + assert!(poem.is_err()); + let poem = Poem::read("cat src/main.rs >>;".to_string()); + assert!(poem.is_err()); + let poem = Poem::read("cat >> && ls -la".to_string()); + assert!(poem.is_err()); + } + + #[test] + fn it_parses_a_complex_verse_with_lots_of_different_meters() { + let poem = Poem::read("ls -la | lolcat && echo hello | lolcat && sleep 2 &".to_string()); + assert!(poem.is_ok()); + let mut verses = poem.unwrap().into_iter(); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "ls"); + assert_eq!(verse.clause().unwrap(), vec!["-la".to_string()]); + assert_eq!(verse.meter, Rune::Couplet); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "lolcat"); + assert_eq!(verse.meter, Rune::And); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "echo"); + assert_eq!(verse.clause().unwrap(), vec!["hello".to_string()]); + assert_eq!(verse.meter, Rune::Couplet); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "lolcat"); + assert_eq!(verse.meter, Rune::And); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "sleep"); + assert_eq!(verse.clause().unwrap(), vec!["2".to_string()]); + assert_eq!(verse.meter, Rune::Quiet); + } + + #[test] + fn it_parses_the_continue_meter_without_a_stanza() { + let poem = Poem::read(";;;;;;;".to_string()); + assert!(poem.is_ok()); + } + + #[test] + fn it_errors_if_the_couplet_meter_is_used_without_a_stanza() { + let poem = Poem::read("|".to_string()); + assert!(poem.is_err()); + } + + #[test] + fn it_errors_if_the_quiet_meter_is_used_without_a_stanza() { + let poem = Poem::read("&".to_string()); + assert!(poem.is_err()); + } + + #[test] + fn it_errors_if_the_and_meter_is_used_without_a_stanza() { + let poem = Poem::read("&&".to_string()); + assert!(poem.is_err()); + } + + #[test] + fn it_parses_a_file() { + let file = r" + ps aux | lolcat + sleep 2 + "; + + let poem = Poem::read(file.to_string()); + assert!(poem.is_ok()); + + let poem = poem.unwrap(); + assert_eq!(poem.len(), 3); + + let mut verses = poem.into_iter(); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "ps"); + assert_eq!(verse.clause().unwrap(), vec!["aux".to_string()]); + assert_eq!(verse.meter, Rune::Couplet); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "lolcat"); + assert_eq!(verse.meter, Rune::Continue); + + let verse = verses.next().unwrap(); + assert_eq!(verse.verb(), "sleep"); + assert_eq!(verse.clause().unwrap(), vec!["2".to_string()]); + assert_eq!(verse.meter, Rune::Continue); + } + + #[test] + fn it_parses_a_longer_file() { + let file = r" + ps aux | lolcat + sleep 2 + ps aux | lolcat + sleep 2 + + echo hello there + export PATH=$PATH:~/.local/bin + + ps aux | lolcat && lolcat src/main.rs + fortune | cowsay | lolcat + + wc -l src/**/*.rs | lolcat; ls -la | grep git + "; + + let poem = Poem::read(file.to_string()); + assert!(poem.is_ok()); + + let poem = poem.unwrap(); + assert_eq!(poem.len(), 18); + } + + #[test] + fn it_catches_parser_errors_related_to_invalid_use_of_special_runes() { + let poetry = "cat file.txt &&&".to_string(); + assert_eq!(Poem::read(poetry).is_err(), true); + + let poetry = "cat file.txt&&|".to_string(); + assert_eq!(Poem::read(poetry).is_err(), true); + + let poetry = "cat <".to_string(); + assert_eq!(Poem::read(poetry).is_err(), true); + } + + #[test] + fn it_catches_parser_errors_related_to_strings() { + let poetry = "echo 'hello".to_string(); + assert_eq!(Poem::read(poetry).is_err(), true); + + let poetry = "echo \"hello".to_string(); + assert_eq!(Poem::read(poetry).is_err(), true); + + let poetry = "`true".to_string(); + assert_eq!(Poem::read(poetry).is_err(), true); + } + + #[test] + fn it_interprets_tilda_as_home() { + let poetry = "cd ~".to_string(); + let poem = Poem::read(poetry).unwrap(); + assert_eq!(poem[0].verb(), "cd"); + assert_eq!(poem[0].clause(), Some(vec!["/home/rory".to_string()])); + + let poetry = "cd ~/Code/dwarvish".to_string(); + let poem = Poem::read(poetry).unwrap(); + assert_eq!(poem[0].verb(), "cd"); + assert_eq!( + poem[0].clause(), + Some(vec!["/home/rory/Code/dwarvish".to_string()]) + ); + assert_eq!(poem[0].meter, Rune::None); + } +} |