diff --git a/src/cmd_file.rs b/src/cmd_file.rs index c9f7a991..35ee3f2a 100644 --- a/src/cmd_file.rs +++ b/src/cmd_file.rs @@ -11,7 +11,7 @@ use kittycad_modeling_cmds::{ websocket::OkWebSocketResponseData, }; -use crate::cmd_kcl::write_deterministic_export; +use crate::{cmd_kcl::write_deterministic_export, types::FormatOutput}; /// Perform operations on CAD files. /// @@ -89,6 +89,10 @@ pub struct CmdFileConvert { #[clap(long, short, value_enum)] pub format: Option, + /// Output JSON to stdout, with human-readable status messages on stderr. + #[clap(long, conflicts_with = "format")] + pub json: bool, + /// If true, the output file should be deterministic, meaning any date or time information /// will be replaced with a fixed value. /// This is useful for when pushing to version control. @@ -126,6 +130,12 @@ impl crate::cmd::Command for CmdFileConvert { .create_conversion(self.output_format.clone(), src_format, &input.into()) .await?; + let format = if self.json { + FormatOutput::Json + } else { + ctx.format(&self.format)? + }; + // If they specified an output file, save the output to that file. if file_conversion.status == kittycad::types::ApiCallStatus::Completed { if let Some(outputs) = file_conversion.outputs { @@ -137,12 +147,21 @@ impl crate::cmd::Command for CmdFileConvert { } else { std::fs::write(&path, data)?; } - writeln!( - ctx.io.out, - "wrote file `{}` to {}", - filename, - path.to_str().unwrap_or("") - )?; + if format == FormatOutput::Table { + writeln!( + ctx.io.out, + "wrote file `{}` to {}", + filename, + path.to_str().unwrap_or("") + )?; + } else { + writeln!( + ctx.io.err_out, + "wrote file `{}` to {}", + filename, + path.to_str().unwrap_or("") + )?; + } } } else { anyhow::bail!( @@ -156,7 +175,6 @@ impl crate::cmd::Command for CmdFileConvert { file_conversion.outputs = None; // Print the output of the conversion. - let format = ctx.format(&self.format)?; ctx.io.write_output(&format, &file_conversion)?; Ok(()) @@ -803,6 +821,7 @@ mod test { output_format: kittycad::types::FileExportFormat::Obj, src_format: None, format: None, + json: false, deterministic:false, }), @@ -818,6 +837,7 @@ mod test { output_format: kittycad::types::FileExportFormat::Obj, src_format: None, format: None, + json: false, deterministic:false, }), stdin: "".to_string(), diff --git a/src/cmd_project.rs b/src/cmd_project.rs index cce333b0..e6aece33 100644 --- a/src/cmd_project.rs +++ b/src/cmd_project.rs @@ -354,6 +354,10 @@ pub struct CmdProjectPublish { /// Command output format. #[clap(long, short, value_enum)] pub format: Option, + + /// Output JSON to stdout, with human-readable status messages on stderr. + #[clap(long, conflicts_with = "format")] + pub json: bool, } #[async_trait::async_trait(?Send)] @@ -369,14 +373,23 @@ impl crate::cmd::Command for CmdProjectPublish { if let ProjectTarget::Local { local, .. } = target { crate::project::persist_cloud_project_id(&local.project_toml, &environment, project.id)?; } - writeln!( - ctx.io.out, + + let format = if self.json { + FormatOutput::Json + } else { + ctx.format(&self.format)? + }; + let message = format!( "{} Submitted Zoo cloud project {} for publication review", ctx.io.color_scheme().success_icon(), project.id - )?; + ); + if format == FormatOutput::Table { + writeln!(ctx.io.out, "{message}")?; + } else { + writeln!(ctx.io.err_out, "{message}")?; + } - let format = ctx.format(&self.format)?; write_project_output(ctx, &format, &project)?; Ok(()) } @@ -412,6 +425,10 @@ pub struct CmdProjectUpload { /// Command output format. #[clap(long, short, value_enum)] pub format: Option, + + /// Output JSON to stdout, with human-readable status messages on stderr. + #[clap(long, conflicts_with = "format")] + pub json: bool, } #[async_trait::async_trait(?Send)] @@ -443,16 +460,24 @@ impl crate::cmd::Command for CmdProjectUpload { }; crate::project::persist_cloud_project_id(&local.project_toml, &environment, project.id)?; - writeln!( - ctx.io.out, + let format = if self.json { + FormatOutput::Json + } else { + ctx.format(&self.format)? + }; + let message = format!( "{} {} Zoo cloud project id {} in {}", ctx.io.color_scheme().success_icon(), if existing_id.is_some() { "Updated" } else { "Stored" }, project.id, local.project_toml.display() - )?; + ); + if format == FormatOutput::Table { + writeln!(ctx.io.out, "{message}")?; + } else { + writeln!(ctx.io.err_out, "{message}")?; + } - let format = ctx.format(&self.format)?; write_project_output(ctx, &format, &project)?; Ok(()) } diff --git a/src/tests.rs b/src/tests.rs index a0019f95..eeb7b56d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -570,7 +570,26 @@ cli_tests! { ], ) .setup(setup_authenticated) - .stdout_contains("Completed") + .stdout_contains("wrote file `source.stl`") + } + + convert_a_file_as_json(_ctx) => { + TestItem::new( + "convert a file as json", + svec![ + "zoo", + "file", + "convert", + "assets/in_obj.obj", + "/tmp/", + "--output-format", + "stl", + "--json", + ], + ) + .setup(setup_authenticated) + .stderr_contains("wrote file `source.stl`") + .stdout_contains(r#""status": "completed""#) } get_the_file_volume(_ctx) => {