summaryrefslogtreecommitdiffstats
path: root/src/poem.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/poem.rs')
-rw-r--r--src/poem.rs284
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);
+ }
+}