Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fucking around with kcl #303

Merged
merged 22 commits into from
Aug 29, 2023
299 changes: 293 additions & 6 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
[package]
name = "kittycad"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Expand All @@ -14,8 +13,9 @@ atty = "0.2.14"
chrono = { version = "0.4", default-features = false, features = ["serde"] }
chrono-humanize = "0.2.3"
clap = { version = "4.4.1", features = ["cargo", "derive", "env", "unicode", "help", "wrap_help"] }
clap_complete = { version = "4.2.4" }
clap_complete = { version = "4.4.0" }
cli-macro = { path = "cli-macro" }
colored = "2.0.4"
colored_json = "3.2.0"
data-encoding = "2"
dialoguer = "0.10.2"
Expand All @@ -24,15 +24,16 @@ git_rev = "0.1.0"
heck = "0.4.0"
http = "0.2.6"
itertools = "0.11.0"
kcl-lib = { version = "0.1.0" }
kittycad = { version = "0.2.20", features = ["clap", "tabled", "requests", "retry"] }
log = "0.4.20"
regex = "1"
num-traits = "0.2.14"
open = "5.0.0"
oauth2 = "4.2.3"
open = "5.0.0"
parse-display = "0.8.2"
pulldown-cmark = "0.9.2"
pulldown-cmark-to-cmark = "11.0.0"
regex = "1"
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] }
ring = "0.16.20"
#roff = { version = "0.2.1" }
Expand All @@ -47,16 +48,17 @@ slog-async = "2"
slog-scope = "4"
slog-stdlog = "4"
slog-term = "2"
tabwriter = "1.3.0"
tabled = { version = "0.14.0", features = ["color"] }
tabwriter = "1.3.0"
termbg = "0.4.0"
terminal_size = "0.2.1"
terminal-spinners = "0.3.2"
terminal_size = "0.2.1"
thiserror = "1"
tokio = { version = "1", features = ["full"] }
toml = "0.7.6"
toml_edit = "0.19.14"
url = "2.4.1"
unicode-segmentation = "1.10.1"
uuid = { version = "1.1", features = ["serde", "v4"] }
version-compare = "0.1.0"

Expand Down
2 changes: 1 addition & 1 deletion src/cmd_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ mod test {
TestItem {
name: "bash completion".to_string(),
input: "bash".to_string(),
want_out: "complete -F _kittycad -o bashdefault -o default kittycad".to_string(),
want_out: "complete -F _kittycad -o nosort -o bashdefault -o default kittycad".to_string(),
want_err: "".to_string(),
},
TestItem {
Expand Down
8 changes: 4 additions & 4 deletions src/cmd_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// Perform operations on CAD files.
///
/// # convert a step file to an obj file
/// $ kittycad file convert ./input.step ./output.obj
/// $ kittycad file convert --output-format=obj ./input.step ./
#[derive(Parser, Debug, Clone)]
#[clap(verbatim_doc_comment)]
pub struct CmdFile {
Expand Down Expand Up @@ -45,14 +45,14 @@
/// `kittycad api-call status <id_of_your_operation>` command.
///
/// # convert step to obj
/// $ kittycad file convert my-file.step my-file.obj
/// $ kittycad file convert --output-format=obj my-file.step output_dir
///
/// # convert obj to step
/// $ kittycad file convert my-obj.obj thing.step
/// $ kittycad file convert --output-format=step my-obj.obj .
///
/// # pass a file to convert from stdin
/// # when converting from stdin, the original file type is required
/// $ cat my-obj.obj | kittycad file convert - thing.step --src-format=obj
/// $ cat my-obj.obj | kittycad file convert --output-format=step - output_dir
#[derive(Parser, Debug, Clone)]
#[clap(verbatim_doc_comment)]
pub struct CmdFileConvert {
Expand Down Expand Up @@ -129,7 +129,7 @@

// Reset the output(s) field of the file conversion.
// Otherwise what we print will be crazy big.
file_conversion.output = None;

Check warning on line 132 in src/cmd_file.rs

View workflow job for this annotation

GitHub Actions / clippy

use of deprecated field `kittycad::types::FileConversion::output`

warning: use of deprecated field `kittycad::types::FileConversion::output` --> src/cmd_file.rs:132:9 | 132 | file_conversion.output = None; | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(deprecated)]` on by default
file_conversion.outputs = None;

// Print the output of the conversion.
Expand Down
80 changes: 80 additions & 0 deletions src/cmd_kcl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use anyhow::Result;
use clap::Parser;

/// Perform actions on `kcl` files.
#[derive(Parser, Debug, Clone)]
#[clap(verbatim_doc_comment)]
pub struct CmdKcl {
#[clap(subcommand)]
subcmd: SubCommand,
}

#[derive(Parser, Debug, Clone)]
enum SubCommand {
Export(CmdKclExport),
}

#[async_trait::async_trait]
impl crate::cmd::Command for CmdKcl {
async fn run(&self, ctx: &mut crate::context::Context) -> Result<()> {
match &self.subcmd {

Check warning on line 20 in src/cmd_kcl.rs

View check run for this annotation

Codecov / codecov/patch

src/cmd_kcl.rs#L20

Added line #L20 was not covered by tests
SubCommand::Export(cmd) => cmd.run(ctx).await,
}
}
}

/// Export a `kcl` file as any other supported CAD file format.
///
/// # convert kcl to obj
/// $ kittycad kcl export --output-format=obj my-file.kcl output_dir
///
/// # convert kcl to step
/// $ kittycad kcl export --output-format=step my-obj.kcl .
///
/// # pass a file to convert from stdin
/// $ cat my-obj.kcl | kittycad kcl export --output-format=step - output_dir
#[derive(Parser, Debug, Clone)]
#[clap(verbatim_doc_comment)]
pub struct CmdKclExport {
/// The path to the input file to convert.
/// If you pass `-` as the path, the file will be read from stdin.
#[clap(name = "input", required = true)]
pub input: std::path::PathBuf,

/// The path to a directory to output the files.
#[clap(name = "output-dir", required = true)]
pub output_dir: std::path::PathBuf,

/// A valid output file format.
#[clap(short = 't', long = "output-format", value_enum)]
output_format: kittycad::types::FileExportFormat,

/// Command output format.
#[clap(long, short, value_enum)]
pub format: Option<crate::types::FormatOutput>,
}

#[async_trait::async_trait]
impl crate::cmd::Command for CmdKclExport {
async fn run(&self, ctx: &mut crate::context::Context) -> Result<()> {
// Make sure the output dir is a directory.
if !self.output_dir.is_dir() {
anyhow::bail!(

Check warning on line 62 in src/cmd_kcl.rs

View check run for this annotation

Codecov / codecov/patch

src/cmd_kcl.rs#L62

Added line #L62 was not covered by tests
"output directory `{}` does not exist or is not a directory",
self.output_dir.to_str().unwrap_or("")

Check warning on line 64 in src/cmd_kcl.rs

View check run for this annotation

Codecov / codecov/patch

src/cmd_kcl.rs#L64

Added line #L64 was not covered by tests
);
}

// Get the contents of the input file.
let input = ctx.read_file(self.input.to_str().unwrap_or(""))?;
// Parse the input as a string.
let input = std::str::from_utf8(&input)?;

// Spin up websockets and do the conversion.
// This will not return until there are files.
ctx.export_kcl_file("", input, &self.output_dir, &self.output_format)
.await?;

Ok(())
}
}
97 changes: 95 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use anyhow::{anyhow, Result};

use crate::{config::Config, config_file::get_env_var, types::FormatOutput};
use crate::{config::Config, config_file::get_env_var, kcl_error_fmt, types::FormatOutput};

pub struct Context<'a> {
pub config: &'a mut (dyn Config + Send + Sync + 'a),
Expand Down Expand Up @@ -68,11 +68,25 @@
}
}

let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60));
let ws_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60))
.tcp_keepalive(std::time::Duration::from_secs(600))
.http1_only();

// Get the token for that host.
let token = self.config.get(&host, "token")?;

// Create the client.
let mut client = kittycad::Client::new(token);
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);

if baseurl != crate::DEFAULT_HOST {
client.set_base_url(&baseurl);
Expand All @@ -81,6 +95,43 @@
Ok(client)
}

pub async fn export_kcl_file(
&self,
hostname: &str,
code: &str,
output_dir: &std::path::Path,
format: &kittycad::types::FileExportFormat,
) -> Result<()> {
let client = self.api_client(hostname)?;
let ws = client
.modeling()
.commands_ws(None, None, None, None, Some(false))
.await?;

let tokens = kcl_lib::tokeniser::lexer(code);
let program = kcl_lib::parser::abstract_syntax_tree(&tokens)
.map_err(|err| kcl_error_fmt::KclError::new(code.to_string(), err))?;
let mut mem: kcl_lib::executor::ProgramMemory = Default::default();
let mut engine = kcl_lib::engine::EngineConnection::new(ws, output_dir.display().to_string().as_str()).await?;
let _ = kcl_lib::executor::execute(program, &mut mem, kcl_lib::executor::BodyType::Root, &mut engine)
.map_err(|err| kcl_error_fmt::KclError::new(code.to_string(), err))?;
// Send an export request to the engine.

Check warning on line 118 in src/context.rs

View check run for this annotation

Codecov / codecov/patch

src/context.rs#L118

Added line #L118 was not covered by tests
engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
kcl_lib::executor::SourceRange::default(),
kittycad::types::ModelingCmd::Export {
entity_ids: vec![],
format: get_output_format(format),
},
)
.map_err(|err| kcl_error_fmt::KclError::new(code.to_string(), err))?;

engine.wait_for_files().await;

Ok(())
}

/// This function opens a browser that is based on the configured
/// environment to the specified path.
///
Expand Down Expand Up @@ -153,6 +204,48 @@
}
}

fn get_output_format(format: &kittycad::types::FileExportFormat) -> kittycad::types::OutputFormat {
// KittyCAD co-ordinate system.

Check warning on line 208 in src/context.rs

View check run for this annotation

Codecov / codecov/patch

src/context.rs#L208

Added line #L208 was not covered by tests
//
// * Forward: -Y
// * Up: +Z
// * Handedness: Right
let coords = kittycad::types::System {
forward: kittycad::types::AxisDirectionPair {
axis: kittycad::types::Axis::Y,
direction: kittycad::types::Direction::Negative,
},
up: kittycad::types::AxisDirectionPair {
axis: kittycad::types::Axis::Z,
direction: kittycad::types::Direction::Positive,
},
};

match format {
kittycad::types::FileExportFormat::Fbx => kittycad::types::OutputFormat::Fbx {
storage: kittycad::types::FbxStorage::Binary,
},
kittycad::types::FileExportFormat::Glb => kittycad::types::OutputFormat::Gltf {
storage: kittycad::types::GltfStorage::Binary,
presentation: kittycad::types::GltfPresentation::Compact,
},
kittycad::types::FileExportFormat::Gltf => kittycad::types::OutputFormat::Gltf {
storage: kittycad::types::GltfStorage::Embedded,
presentation: kittycad::types::GltfPresentation::Pretty,
},
kittycad::types::FileExportFormat::Obj => kittycad::types::OutputFormat::Obj { coords },
kittycad::types::FileExportFormat::Ply => kittycad::types::OutputFormat::Ply {
storage: kittycad::types::PlyStorage::Ascii,
coords,
},
kittycad::types::FileExportFormat::Step => kittycad::types::OutputFormat::Step { coords },
kittycad::types::FileExportFormat::Stl => kittycad::types::OutputFormat::Stl {
storage: kittycad::types::StlStorage::Ascii,
coords,
},
}
}

#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
Expand Down
Loading
Loading