From 1a4906b89123f015dfb1337e384ebed696d09f5c Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 28 May 2020 02:59:52 -0700 Subject: first rust experiments --- .gitignore | 2 + Cargo.lock | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 23 +++++++ src/bin/aft-demo.rs | 35 ++++++++++ src/bin/aft-head.rs | 8 +++ src/bin/aft.rs | 105 ++++++++++++++++++++++++++++++ src/lib.rs | 0 7 files changed, 354 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/bin/aft-demo.rs create mode 100644 src/bin/aft-head.rs create mode 100644 src/bin/aft.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore index 81a4762..97a2952 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +target/ + *.o *.a *.pyc diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..80a6d46 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,181 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aft" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_cmd", + "atty", + "colored", + "tabwriter", +] + +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + +[[package]] +name = "assert_cmd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c88b9ca26f9c16ec830350d309397e74ee9abdfd8eb1f71cb6ecc71a3fc818da" +dependencies = [ + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "hermit-abi" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "predicates" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "347a1b6f0b21e636bc9872fb60b83b8e185f6f5516298b8238699f7f9a531030" +dependencies = [ + "difference", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" + +[[package]] +name = "predicates-tree" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +dependencies = [ + "predicates-core", + "treeline", +] + +[[package]] +name = "regex" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + +[[package]] +name = "tabwriter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36205cfc997faadcc4b0b87aaef3fbedafe20d38d4959a7ca6ff803564051111" +dependencies = [ + "lazy_static", + "regex", + "unicode-width", +] + +[[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e5428b2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "aft" +version = "0.1.0" +authors = ["Bryan Newbold "] +edition = "2018" + +[dependencies] +#serde_json = "1.0" +#bstr = "0.2" +#structopt = "0.3" +anyhow = "1.0" +colored = "1.9" +atty = "0.2" +tabwriter = { version = "1.2", features = ["ansi_formatting"] } + +[dev-dependencies] +assert_cmd = "1" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = "z" +panic = "abort" 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; + + fn from_str(raw: &str) -> Result> { + 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, + col_types: Option>, +} + +fn main() -> Result<(), Box> { + + 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::, Box>>()?), + }, + }; + + 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(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3