Merge pull request #591 from pmeredit/topic/save_binary

Topic/save binary
This commit is contained in:
Jonathan Turner 2019-09-04 15:07:54 +12:00 committed by GitHub
commit 7a5fc82ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 53 deletions

View File

@ -496,6 +496,10 @@ pub trait WholeStreamCommand: Send + Sync {
args: CommandArgs, args: CommandArgs,
registry: &registry::CommandRegistry, registry: &registry::CommandRegistry,
) -> Result<OutputStream, ShellError>; ) -> Result<OutputStream, ShellError>;
fn is_binary(&self) -> bool {
false
}
} }
pub trait PerItemCommand: Send + Sync { pub trait PerItemCommand: Send + Sync {
@ -521,6 +525,10 @@ pub trait PerItemCommand: Send + Sync {
raw_args: &RawCommandArgs, raw_args: &RawCommandArgs,
input: Tagged<Value>, input: Tagged<Value>,
) -> Result<OutputStream, ShellError>; ) -> Result<OutputStream, ShellError>;
fn is_binary(&self) -> bool {
false
}
} }
pub enum Command { pub enum Command {
@ -608,6 +616,13 @@ impl Command {
} }
} }
} }
pub fn is_binary(&self) -> bool {
match self {
Command::WholeStream(command) => command.is_binary(),
Command::PerItem(command) => command.is_binary(),
}
}
} }
pub struct FnFilterCommand { pub struct FnFilterCommand {

View File

@ -6,6 +6,80 @@ use std::path::{Path, PathBuf};
pub struct Save; pub struct Save;
macro_rules! process_string {
($input:ident, $name_span:ident) => {{
let mut result_string = String::new();
for res in $input {
match res {
Tagged {
item: Value::Primitive(Primitive::String(s)),
..
} => {
result_string.push_str(&s);
}
_ => {
yield core::task::Poll::Ready(Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during save",
$name_span,
)));
}
}
}
Ok(result_string.into_bytes())
}};
}
macro_rules! process_string_return_success {
($result_vec:ident, $name_span:ident) => {{
let mut result_string = String::new();
for res in $result_vec {
match res {
Ok(ReturnSuccess::Value(Tagged {
item: Value::Primitive(Primitive::String(s)),
..
})) => {
result_string.push_str(&s);
}
_ => {
yield core::task::Poll::Ready(Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during text save",
$name_span,
)));
}
}
}
Ok(result_string.into_bytes())
}};
}
macro_rules! process_binary_return_success {
($result_vec:ident, $name_span:ident) => {{
let mut result_binary: Vec<u8> = Vec::new();
for res in $result_vec {
match res {
Ok(ReturnSuccess::Value(Tagged {
item: Value::Binary(b),
..
})) => {
for u in b.into_iter() {
result_binary.push(u);
}
}
_ => {
yield core::task::Poll::Ready(Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during binary save",
$name_span,
)));
}
}
}
Ok(result_binary)
}};
}
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct SaveArgs { pub struct SaveArgs {
path: Option<Tagged<PathBuf>>, path: Option<Tagged<PathBuf>>,
@ -96,7 +170,7 @@ fn save(
} }
} }
let content = if !save_raw { let content : Result<Vec<u8>, ShellError> = if !save_raw {
if let Some(extension) = full_path.extension() { if let Some(extension) = full_path.extension() {
let command_name = format!("to-{}", extension.to_str().unwrap()); let command_name = format!("to-{}", extension.to_str().unwrap());
if let Some(converter) = registry.get_command(&command_name) { if let Some(converter) = registry.get_command(&command_name) {
@ -116,60 +190,19 @@ fn save(
}; };
let mut result = converter.run(new_args.with_input(input), &registry); let mut result = converter.run(new_args.with_input(input), &registry);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await; let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
let mut result_string = String::new(); if converter.is_binary() {
for res in result_vec { process_binary_return_success!(result_vec, name_span)
match res { } else {
Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => { process_string_return_success!(result_vec, name_span)
result_string.push_str(&s);
}
_ => {
yield Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during save",
name_span,
));
},
}
} }
Ok(result_string)
} else { } else {
let mut result_string = String::new(); process_string!(input, name_span)
for res in input {
match res {
Tagged { item: Value::Primitive(Primitive::String(s)), .. } => {
result_string.push_str(&s);
}
_ => {
yield Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during save",
name_span,
));
},
}
}
Ok(result_string)
} }
} else { } else {
let mut result_string = String::new(); process_string!(input, name_span)
for res in input {
match res {
Tagged { item: Value::Primitive(Primitive::String(s)), .. } => {
result_string.push_str(&s);
}
_ => {
yield Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during save",
name_span,
));
},
}
}
Ok(result_string)
} }
} else { } else {
string_from(&input) Ok(string_from(&input).into_bytes())
}; };
match content { match content {
@ -185,7 +218,7 @@ fn save(
Ok(OutputStream::new(stream)) Ok(OutputStream::new(stream))
} }
fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> { fn string_from(input: &Vec<Tagged<Value>>) -> String {
let mut save_data = String::new(); let mut save_data = String::new();
if input.len() > 0 { if input.len() > 0 {
@ -202,5 +235,5 @@ fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
} }
} }
Ok(save_data) save_data
} }

View File

@ -26,6 +26,10 @@ impl WholeStreamCommand for ToBSON {
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_bson(args, registry) to_bson(args, registry)
} }
fn is_binary(&self) -> bool {
true
}
} }
pub fn value_to_bson_value(v: &Tagged<Value>) -> Result<Bson, ShellError> { pub fn value_to_bson_value(v: &Tagged<Value>) -> Result<Bson, ShellError> {

View File

@ -27,6 +27,10 @@ impl WholeStreamCommand for ToSQLite {
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_sqlite(args, registry) to_sqlite(args, registry)
} }
fn is_binary(&self) -> bool {
true
}
} }
pub struct ToDB; pub struct ToDB;
@ -51,6 +55,10 @@ impl WholeStreamCommand for ToDB {
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_sqlite(args, registry) to_sqlite(args, registry)
} }
fn is_binary(&self) -> bool {
true
}
} }
fn comma_concat(acc: String, current: String) -> String { fn comma_concat(acc: String, current: String) -> String {

View File

@ -36,8 +36,8 @@ fn save_figures_out_intelligently_where_to_write_out_with_metadata() {
description = "A shell for the GitHub era" description = "A shell for the GitHub era"
license = "ISC" license = "ISC"
edition = "2018" edition = "2018"
"#) "#,
]); )]);
let subject_file = dirs.test().join("cargo_sample.toml"); let subject_file = dirs.test().join("cargo_sample.toml");
@ -66,3 +66,32 @@ fn save_can_write_out_csv() {
assert!(actual.contains("[list list],A shell for the GitHub era,2018,ISC,nu,0.2.0")); assert!(actual.contains("[list list],A shell for the GitHub era,2018,ISC,nu,0.2.0"));
}) })
} }
#[test]
fn save_can_write_out_bson() {
Playground::setup("save_test_3", |dirs, _| {
let expected_file = dirs.test().join("cargo_sample.bson");
nu!(
cwd: dirs.root(),
"open {}/cargo_sample.toml | inc package.version --minor | get package | save save_test_3/cargo_sample.bson",
dirs.formats()
);
let actual = h::file_contents_binary(expected_file);
assert!(
actual
== vec![
168, 0, 0, 0, 4, 97, 117, 116, 104, 111, 114, 115, 0, 43, 0, 0, 0, 2, 48, 0,
31, 0, 0, 0, 89, 101, 104, 117, 100, 97, 32, 75, 97, 116, 122, 32, 60, 119,
121, 99, 97, 116, 115, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 62, 0, 0,
2, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 0, 27, 0, 0, 0, 65,
32, 115, 104, 101, 108, 108, 32, 102, 111, 114, 32, 116, 104, 101, 32, 71, 105,
116, 72, 117, 98, 32, 101, 114, 97, 0, 2, 101, 100, 105, 116, 105, 111, 110, 0,
5, 0, 0, 0, 50, 48, 49, 56, 0, 2, 108, 105, 99, 101, 110, 115, 101, 0, 4, 0, 0,
0, 73, 83, 67, 0, 2, 110, 97, 109, 101, 0, 3, 0, 0, 0, 110, 117, 0, 2, 118,
101, 114, 115, 105, 111, 110, 0, 6, 0, 0, 0, 48, 46, 50, 46, 48, 0, 0
]
);
})
}

View File

@ -307,6 +307,14 @@ pub fn file_contents(full_path: impl AsRef<Path>) -> String {
contents contents
} }
pub fn file_contents_binary(full_path: impl AsRef<Path>) -> Vec<u8> {
let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file");
let mut contents = Vec::new();
file.read_to_end(&mut contents)
.expect("can not read file");
contents
}
pub fn line_ending() -> String { pub fn line_ending() -> String {
#[cfg(windows)] #[cfg(windows)]
{ {