diff options
Diffstat (limited to 'src/bin')
-rw-r--r-- | src/bin/aft-demo.rs | 35 | ||||
-rw-r--r-- | src/bin/aft-head.rs | 8 | ||||
-rw-r--r-- | src/bin/aft.rs | 105 |
3 files changed, 148 insertions, 0 deletions
diff --git a/src/bin/aft-demo.rs b/src/bin/aft-demo.rs new file mode 100644 index 0000000..9a680c1 --- /dev/null +++ b/src/bin/aft-demo.rs @@ -0,0 +1,35 @@ + +/* + * Writes example aft to stdout + */ + +fn main() { + + /* + * Sepal length Sepal width Petal length Petal width Species + * 5.1 3.5 1.4 0.2 I. setosa + * 4.9 3.0 1.4 0.2 I. setosa + * 4.7 3.2 1.3 0.2 I. setosa + * 4.6 3.1 1.5 0.2 I. setosa + * 5.0 3.6 1.4 0.2 I. setosa + */ + + // start header + print!("\x01"); + + // colum names + println!("{}", ["Sepal length", "Speal width", "Petal length", "Petal width", "Species"].join("\x1E")); + + // colum types + println!("{}", ["float", "float", "float", "float", "str"].join("\x1E")); + + // end header / start table + print!("\x02"); + + // print rows + println!("{}", ["5.1", "3.5", "1.4", "0.2", "I. setosa"].join("\x1E")); + println!("{}", ["4.9", "3.0", "1.4", "0.2", "I. setosa"].join("\x1E")); + println!("{}", ["4.7", "3.2", "1.3", "0.2", "I. setosa"].join("\x1E")); + println!("{}", ["4.6", "3.1", "1.5", "0.2", "I. setosa"].join("\x1E")); + println!("{}", ["5.0", "3.6", "1.4", "0.2", "I. setosa"].join("\x1E")); +} diff --git a/src/bin/aft-head.rs b/src/bin/aft-head.rs new file mode 100644 index 0000000..b8c848c --- /dev/null +++ b/src/bin/aft-head.rs @@ -0,0 +1,8 @@ + +/* + * Takes the first n lines of input, passes to stdout. + */ + +fn main() { + println!("Hello, world2!"); +} diff --git a/src/bin/aft.rs b/src/bin/aft.rs new file mode 100644 index 0000000..0ada921 --- /dev/null +++ b/src/bin/aft.rs @@ -0,0 +1,105 @@ + +/* + * Default behavior: + * + * aft < file.aft + * pretty-prints as a table to stdout + * + * ls | aft | aft-sort + * detects non-aft stdin and converts to aft on stdout + */ + +use std::str::FromStr; +use std::io::{self, Read, Write, BufRead}; +use std::error::Error; +use colored::*; + +#[derive(Debug)] +enum AftFieldType { + Str, + Int, + Float, + Bool, + Array, + Null, + Other(String), +} + +impl FromStr for AftFieldType { + type Err = Box<dyn Error>; + + fn from_str(raw: &str) -> Result<AftFieldType, Box<dyn Error>> { + match raw { + "" => Ok(AftFieldType::Null), + "str" => Ok(AftFieldType::Str), + "int" | "integer" => Ok(AftFieldType::Int), + "float" => Ok(AftFieldType::Float), + "bool" | "boolean" => Ok(AftFieldType::Bool), + "array" => Ok(AftFieldType::Array), + other => Ok(AftFieldType::Other(other.to_string())), + + } + } +} + +#[derive(Debug)] +struct AftHeader { + col_names: Vec<String>, + col_types: Option<Vec<AftFieldType>>, +} + +fn main() -> Result<(), Box<dyn Error>> { + + let mut stdout = io::stdout(); + let stdin = io::stdin(); + let mut stdin = stdin.lock(); + + // if we aren't connected to terminal, just pass through + if !atty::is(atty::Stream::Stdout) { + io::copy(&mut stdin, &mut stdout)?; + return Ok(()) + } + + // read first byte of input to check if we are getting AFT; if not just pass through + let mut first_byte = [0; 1]; + let got = stdin.read(&mut first_byte)?; + if got != 1 { + panic!("couldn't read a byte from stdin"); + }; + if first_byte[0] != 0x01 { + stdout.write(&first_byte)?; + io::copy(&mut stdin, &mut stdout)?; + return Ok(()) + } + + let mut stdin = io::BufReader::new(stdin); + let mut header_bytes = vec![]; + // TODO: sanity limit on length + stdin.read_until(0x02, &mut header_bytes)?; + if header_bytes[header_bytes.len()-1] != 0x02 { + panic!("expected an AFT header"); + }; + let header_string = String::from_utf8_lossy(&header_bytes); + let header_rows: Vec<&str> = header_string.splitn(2, "\n").collect(); + let header = match header_rows.len() { + 0 => panic!("expected a header"), + 1 => AftHeader { + col_names: header_rows[0].split("\x1E").map(|v| v.to_string()).collect(), + col_types: None, + }, + _ => AftHeader { + col_names: header_rows[0].split("\x1E").map(|v| v.to_string()).collect(), + col_types: Some(header_rows[1].split("\x1E").map(|v| AftFieldType::from_str(v)).collect::<Result<Vec<AftFieldType>, Box<dyn Error>>>()?), + }, + }; + + let mut tw = tabwriter::TabWriter::new(stdout); + writeln!(tw, "{}", header.col_names.join("\t").bold())?; + for line in stdin.lines() { + let line = line?; + writeln!(tw, "{}", line.replace("\x1E", "\t"))?; + } + tw.flush()?; + + Ok(()) +} |