Răsfoiți Sursa

drk/interactive: unix style input/output file definitions/handling added

skoupidi 1 an în urmă
părinte
comite
e6795bf36e
1 a modificat fișierele cu 123 adăugiri și 8 ștergeri
  1. 123 8
      bin/drk/src/interactive.rs

+ 123 - 8
bin/drk/src/interactive.rs

@@ -17,7 +17,8 @@
  */
 
 use std::{
-    io::{stdin, ErrorKind, Read},
+    fs::{File, OpenOptions},
+    io::{stdin, BufRead, BufReader, ErrorKind, Read, Seek, SeekFrom, Write},
     str::FromStr,
 };
 
@@ -57,10 +58,9 @@ use crate::{
 };
 
 // TODO:
-//  1. add rest commands handling, along with their completions, hints and help message.
-//  2. add input definitions, so you input from files not just stdin.
-//  3. add output definitions, so you can output to files not just stdout.
-//  4. create a transactions cache in the wallet db, so you can use it to handle them.
+//  1. Add rest commands handling, along with their completions, hints and help message.
+//  2. Subscribe/scan ux is a bit flaky, fix it.
+//  3. Create a transactions cache in the wallet db, so you can use it to handle them.
 
 /// Auxiliary function to print the help message.
 fn help(output: &mut Vec<String>) {
@@ -295,7 +295,83 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
 
         // Process each command
         let mut output = vec![];
-        for command in commands {
+        'commands_loop: for command in commands {
+            let mut input = output;
+            output = vec![];
+
+            // Check if we have to output to a file
+            let (mut command, file, append) = if command.contains('>') {
+                // Check if we write or append to the file
+                let mut split = ">";
+                let mut append = false;
+                if command.contains(">>") {
+                    split = ">>";
+                    append = true;
+                }
+
+                // Parse command parts
+                let parts: Vec<&str> = command.split(split).collect();
+                if parts.len() == 1 || parts[0].contains('>') {
+                    output.push(format!("Malformed command output file definition: {command}"));
+                    continue
+                }
+                let file = String::from(parts[1..].join("").trim());
+                if file.is_empty() || file.contains('>') {
+                    output.push(format!("Malformed command output file definition: {command}"));
+                    continue
+                }
+                (parts[0], Some(file), append)
+            } else {
+                (command, None, false)
+            };
+
+            // Check if we have to use a file as input
+            if command.contains('<') {
+                // Parse command parts
+                let parts: Vec<&str> = command.split('<').collect();
+                if parts.len() == 1 {
+                    output.push(format!("Malformed command input file definition: {command}"));
+                    continue
+                }
+
+                // Read the input file name
+                let file = String::from(parts[1..].join("").trim());
+                if file.is_empty() {
+                    output.push(format!("Malformed command input file definition: {command}"));
+                    continue
+                }
+
+                // Expand the input file path
+                let file_path = match expand_path(&file) {
+                    Ok(p) => p,
+                    Err(e) => {
+                        output.push(format!("Error while expanding input file path: {e}"));
+                        continue
+                    }
+                };
+
+                // Read the file lines
+                let file = match File::open(file_path) {
+                    Ok(f) => f,
+                    Err(e) => {
+                        output.push(format!("Error while openning input file: {e}"));
+                        continue
+                    }
+                };
+                input = vec![];
+                for (index, line) in BufReader::new(file).lines().enumerate() {
+                    match line {
+                        Ok(l) => input.push(l),
+                        Err(e) => {
+                            output
+                                .push(format!("Error while reading input file line {index}: {e}"));
+                            continue 'commands_loop
+                        }
+                    }
+                }
+                command = parts[0];
+            }
+
             // Parse command parts
             let parts: Vec<&str> = command.split_whitespace().collect();
             if parts.is_empty() {
@@ -303,8 +379,6 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
             }
 
             // Handle command
-            let input = output;
-            output = vec![];
             match parts[0] {
                 "help" => help(&mut output),
                 "kaching" => kaching().await,
@@ -339,6 +413,47 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
                 "scan" => handle_scan(drk, &subscription_active, &parts, &mut output).await,
                 _ => output.push(format!("Unreconized command: {}", parts[0])),
             }
+
+            // Write output to file, if requested
+            if let Some(file) = file {
+                // Expand the output file path
+                let file_path = match expand_path(&file) {
+                    Ok(p) => p,
+                    Err(e) => {
+                        output.push(format!("Error while expanding output file path: {e}"));
+                        continue
+                    }
+                };
+
+                // Open the file
+                let file = if append {
+                    OpenOptions::new().create(true).append(true).open(&file_path)
+                } else {
+                    File::create(file_path)
+                };
+                let mut file = match file {
+                    Ok(f) => f,
+                    Err(e) => {
+                        output.push(format!("Error while openning output file: {e}"));
+                        continue
+                    }
+                };
+
+                // Seek end if we append to it
+                if append {
+                    if let Err(e) = file.seek(SeekFrom::End(0)) {
+                        output.push(format!("Error while seeking end of output file: {e}"));
+                        continue
+                    }
+                }
+
+                // Write the output
+                if let Err(e) = file.write_all((output.join("\n") + "\n").as_bytes()) {
+                    output.push(format!("Error while writing output file: {e}"));
+                    continue
+                }
+                output = vec![];
+            }
         }
 
         // Handle last command output