Docs macros (#318)
* initial port Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * start of macro Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * more macros Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * new Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * start of generated docs Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixups Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates for objects Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixiups Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * descriptions Signed-off-by: Jess Frazelle <github@jessfraz.com> * descriptions Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * remove vecs Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
17021
docs/kcl.json
Normal file
17021
docs/kcl.json
Normal file
File diff suppressed because it is too large
Load Diff
3048
docs/kcl.md
Normal file
3048
docs/kcl.md
Normal file
File diff suppressed because it is too large
Load Diff
1432
src/wasm-lib/Cargo.lock
generated
1432
src/wasm-lib/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@ crate-type = ["cdylib"]
|
|||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
|
derive-docs = { path = "derive-docs" }
|
||||||
futures = { version = "0.3.28", optional = true }
|
futures = { version = "0.3.28", optional = true }
|
||||||
gloo-file = { version = "0.3.0", optional = true }
|
gloo-file = { version = "0.3.0", optional = true }
|
||||||
gloo-utils = "0.2.0"
|
gloo-utils = "0.2.0"
|
||||||
@ -19,7 +20,9 @@ httparse = { version = "1.8.0", optional = true }
|
|||||||
js-sys = { version = "0.3.64", optional = true }
|
js-sys = { version = "0.3.64", optional = true }
|
||||||
kittycad = { version = "0.2.15", default-features = false, features = ["js"] }
|
kittycad = { version = "0.2.15", default-features = false, features = ["js"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
parse-display = "0.8.2"
|
||||||
regex = "1.7.1"
|
regex = "1.7.1"
|
||||||
|
schemars = { version = "0.8", features = ["url", "uuid1"] }
|
||||||
serde = {version = "1.0.152", features = ["derive"] }
|
serde = {version = "1.0.152", features = ["derive"] }
|
||||||
serde_json = "1.0.93"
|
serde_json = "1.0.93"
|
||||||
thiserror = "1.0.47"
|
thiserror = "1.0.47"
|
||||||
@ -35,6 +38,7 @@ panic = "abort"
|
|||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
expectorate = "1.0.7"
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||||
|
|
||||||
@ -42,3 +46,8 @@ tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
|||||||
default = ["web"]
|
default = ["web"]
|
||||||
web = ["dep:gloo-file", "dep:js-sys"]
|
web = ["dep:gloo-file", "dep:js-sys"]
|
||||||
noweb = ["dep:futures", "dep:httparse", "dep:tokio", "dep:tokio-tungstenite"]
|
noweb = ["dep:futures", "dep:httparse", "dep:tokio", "dep:tokio-tungstenite"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"derive-docs"
|
||||||
|
]
|
||||||
|
23
src/wasm-lib/derive-docs/Cargo.toml
Normal file
23
src/wasm-lib/derive-docs/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "derive-docs"
|
||||||
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
convert_case = "0.6.0"
|
||||||
|
proc-macro2 = "1"
|
||||||
|
quote = "1"
|
||||||
|
serde = { version = "1.0.186", features = ["derive"] }
|
||||||
|
serde_tokenstream = "0.2"
|
||||||
|
syn = { version = "2.0.29", features = ["full"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
expectorate = "1.0.7"
|
||||||
|
openapitor = "0.0.5"
|
||||||
|
pretty_assertions = "1.4.0"
|
548
src/wasm-lib/derive-docs/src/lib.rs
Normal file
548
src/wasm-lib/derive-docs/src/lib.rs
Normal file
@ -0,0 +1,548 @@
|
|||||||
|
// Copyright 2023 Oxide Computer Company
|
||||||
|
|
||||||
|
//! This package defines macro attributes associated with HTTP handlers. These
|
||||||
|
//! attributes are used both to define an HTTP API and to generate an OpenAPI
|
||||||
|
//! Spec (OAS) v3 document that describes the API.
|
||||||
|
|
||||||
|
// Clippy's style advice is definitely valuable, but not worth the trouble for
|
||||||
|
// automated enforcement.
|
||||||
|
#![allow(clippy::style)]
|
||||||
|
|
||||||
|
use convert_case::Casing;
|
||||||
|
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_tokenstream::{from_tokenstream, Error};
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
Attribute, Signature, Visibility,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct StdlibMetadata {
|
||||||
|
/// The name of the function in the API.
|
||||||
|
name: String,
|
||||||
|
/// Tags for the function.
|
||||||
|
#[serde(default)]
|
||||||
|
tags: Vec<String>,
|
||||||
|
/// Whether the function is unpublished.
|
||||||
|
/// Then docs will not be generated.
|
||||||
|
#[serde(default)]
|
||||||
|
unpublished: bool,
|
||||||
|
/// Whether the function is deprecated.
|
||||||
|
/// Then specific docs detailing that this is deprecated will be generated.
|
||||||
|
#[serde(default)]
|
||||||
|
deprecated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn stdlib(
|
||||||
|
attr: proc_macro::TokenStream,
|
||||||
|
item: proc_macro::TokenStream,
|
||||||
|
) -> proc_macro::TokenStream {
|
||||||
|
do_output(do_stdlib(attr.into(), item.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_stdlib(
|
||||||
|
attr: proc_macro2::TokenStream,
|
||||||
|
item: proc_macro2::TokenStream,
|
||||||
|
) -> Result<(proc_macro2::TokenStream, Vec<Error>), Error> {
|
||||||
|
let metadata = from_tokenstream(&attr)?;
|
||||||
|
do_stdlib_inner(metadata, attr, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_output(
|
||||||
|
res: Result<(proc_macro2::TokenStream, Vec<Error>), Error>,
|
||||||
|
) -> proc_macro::TokenStream {
|
||||||
|
match res {
|
||||||
|
Err(err) => err.to_compile_error().into(),
|
||||||
|
Ok((stdlib_docs, errors)) => {
|
||||||
|
let compiler_errors = errors.iter().map(|err| err.to_compile_error());
|
||||||
|
|
||||||
|
let output = quote! {
|
||||||
|
#stdlib_docs
|
||||||
|
#( #compiler_errors )*
|
||||||
|
};
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_stdlib_inner(
|
||||||
|
metadata: StdlibMetadata,
|
||||||
|
_attr: proc_macro2::TokenStream,
|
||||||
|
item: proc_macro2::TokenStream,
|
||||||
|
) -> Result<(proc_macro2::TokenStream, Vec<Error>), Error> {
|
||||||
|
let ast: ItemFnForSignature = syn::parse2(item.clone())?;
|
||||||
|
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
if ast.sig.constness.is_some() {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&ast.sig.constness,
|
||||||
|
"stdlib functions may not be const functions",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.sig.asyncness.is_some() {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&ast.sig.fn_token,
|
||||||
|
"stdlib functions must not be async",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.sig.unsafety.is_some() {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&ast.sig.unsafety,
|
||||||
|
"stdlib functions may not be unsafe",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.sig.abi.is_some() {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&ast.sig.abi,
|
||||||
|
"stdlib functions may not use an alternate ABI",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ast.sig.generics.params.is_empty() {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&ast.sig.generics,
|
||||||
|
"generics are not permitted for stdlib functions",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.sig.variadic.is_some() {
|
||||||
|
errors.push(Error::new_spanned(&ast.sig.variadic, "no language C here"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = metadata.name;
|
||||||
|
let name_ident = format_ident!("{}", name.to_case(convert_case::Case::UpperCamel));
|
||||||
|
let name_str = name.to_string();
|
||||||
|
|
||||||
|
let fn_name = &ast.sig.ident;
|
||||||
|
let fn_name_str = fn_name.to_string().replace("inner_", "");
|
||||||
|
let fn_name_ident = format_ident!("{}", fn_name_str);
|
||||||
|
let _visibility = &ast.vis;
|
||||||
|
|
||||||
|
let (summary_text, description_text) = extract_doc_from_attrs(&ast.attrs);
|
||||||
|
let comment_text = {
|
||||||
|
let mut buf = String::new();
|
||||||
|
buf.push_str("Std lib function: ");
|
||||||
|
buf.push_str(&name_str);
|
||||||
|
if let Some(s) = &summary_text {
|
||||||
|
buf.push_str("\n");
|
||||||
|
buf.push_str(&s);
|
||||||
|
}
|
||||||
|
if let Some(s) = &description_text {
|
||||||
|
buf.push_str("\n");
|
||||||
|
buf.push_str(&s);
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
let description_doc_comment = quote! {
|
||||||
|
#[doc = #comment_text]
|
||||||
|
};
|
||||||
|
|
||||||
|
let summary = if let Some(summary) = summary_text {
|
||||||
|
quote! { #summary }
|
||||||
|
} else {
|
||||||
|
quote! { "" }
|
||||||
|
};
|
||||||
|
let description = if let Some(description) = description_text {
|
||||||
|
quote! { #description }
|
||||||
|
} else {
|
||||||
|
quote! { "" }
|
||||||
|
};
|
||||||
|
|
||||||
|
let tags = metadata
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.map(|tag| {
|
||||||
|
quote! { #tag.to_string() }
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let deprecated = if metadata.deprecated {
|
||||||
|
quote! { true }
|
||||||
|
} else {
|
||||||
|
quote! { false }
|
||||||
|
};
|
||||||
|
|
||||||
|
let unpublished = if metadata.unpublished {
|
||||||
|
quote! { true }
|
||||||
|
} else {
|
||||||
|
quote! { false }
|
||||||
|
};
|
||||||
|
|
||||||
|
let docs_crate = get_crate(None);
|
||||||
|
|
||||||
|
// When the user attaches this proc macro to a function with the wrong type
|
||||||
|
// signature, the resulting errors can be deeply inscrutable. To attempt to
|
||||||
|
// make failures easier to understand, we inject code that asserts the types
|
||||||
|
// of the various parameters. We do this by calling dummy functions that
|
||||||
|
// require a type that satisfies SharedExtractor or ExclusiveExtractor.
|
||||||
|
let mut arg_types = Vec::new();
|
||||||
|
for arg in ast.sig.inputs.iter() {
|
||||||
|
// Get the name of the argument.
|
||||||
|
let arg_name = match arg {
|
||||||
|
syn::FnArg::Receiver(pat) => {
|
||||||
|
let span = pat.self_token.span.unwrap();
|
||||||
|
span.source_text().unwrap().to_string()
|
||||||
|
}
|
||||||
|
syn::FnArg::Typed(pat) => match &*pat.pat {
|
||||||
|
syn::Pat::Ident(ident) => ident.ident.to_string(),
|
||||||
|
_ => {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&pat.pat,
|
||||||
|
"stdlib functions may not use destructuring patterns",
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty = match arg {
|
||||||
|
syn::FnArg::Receiver(pat) => pat.ty.as_ref().into_token_stream(),
|
||||||
|
syn::FnArg::Typed(pat) => pat.ty.as_ref().into_token_stream(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty_string = ty
|
||||||
|
.to_string()
|
||||||
|
.replace('&', "")
|
||||||
|
.replace("mut", "")
|
||||||
|
.replace(' ', "");
|
||||||
|
let ty_string = ty_string.trim().to_string();
|
||||||
|
let ty_ident = if ty_string.starts_with("Vec<") {
|
||||||
|
let ty_string = ty_string.trim_start_matches("Vec<").trim_end_matches('>');
|
||||||
|
let ty_ident = format_ident!("{}", ty_string);
|
||||||
|
quote! {
|
||||||
|
Vec<#ty_ident>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let ty_ident = format_ident!("{}", ty_string);
|
||||||
|
quote! {
|
||||||
|
#ty_ident
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty_string = clean_type(&ty_string);
|
||||||
|
|
||||||
|
if ty_string != "Args" {
|
||||||
|
let schema = if ty_ident.to_string().starts_with("Vec < ") {
|
||||||
|
quote! {
|
||||||
|
<#ty_ident>::json_schema(&mut generator)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#ty_ident::json_schema(&mut generator)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
arg_types.push(quote! {
|
||||||
|
#docs_crate::StdLibFnArg {
|
||||||
|
name: #arg_name.to_string(),
|
||||||
|
type_: #ty_string.to_string(),
|
||||||
|
schema: #schema,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_ty = ast.sig.output.clone();
|
||||||
|
let ret_ty_string = ret_ty
|
||||||
|
.into_token_stream()
|
||||||
|
.to_string()
|
||||||
|
.replace("-> ", "")
|
||||||
|
.replace("Result < ", "")
|
||||||
|
.replace(", KclError >", "");
|
||||||
|
let ret_ty_string = ret_ty_string.trim().to_string();
|
||||||
|
let ret_ty_ident = format_ident!("{}", ret_ty_string);
|
||||||
|
let ret_ty_string = clean_type(&ret_ty_string);
|
||||||
|
let return_type = quote! {
|
||||||
|
#docs_crate::StdLibFnArg {
|
||||||
|
name: "".to_string(),
|
||||||
|
type_: #ret_ty_string.to_string(),
|
||||||
|
schema: #ret_ty_ident::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// For reasons that are not well understood unused constants that use the
|
||||||
|
// (default) call_site() Span do not trigger the dead_code lint. Because
|
||||||
|
// defining but not using an endpoint is likely a programming error, we
|
||||||
|
// want to be sure to have the compiler flag this. We force this by using
|
||||||
|
// the span from the name of the function to which this macro was applied.
|
||||||
|
let span = ast.sig.ident.span();
|
||||||
|
let const_struct = quote_spanned! {span=>
|
||||||
|
pub(crate) const #name_ident: #name_ident = #name_ident {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// The final TokenStream returned will have a few components that reference
|
||||||
|
// `#name_ident`, the name of the function to which this macro was applied...
|
||||||
|
let stream = quote! {
|
||||||
|
// ... a struct type called `#name_ident` that has no members
|
||||||
|
#[allow(non_camel_case_types, missing_docs)]
|
||||||
|
#description_doc_comment
|
||||||
|
pub(crate) struct #name_ident {}
|
||||||
|
// ... a constant of type `#name` whose identifier is also #name_ident
|
||||||
|
#[allow(non_upper_case_globals, missing_docs)]
|
||||||
|
#description_doc_comment
|
||||||
|
#const_struct
|
||||||
|
|
||||||
|
impl #docs_crate::StdLibFn for #name_ident
|
||||||
|
{
|
||||||
|
fn name(&self) -> String {
|
||||||
|
#name_str.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn summary(&self) -> String {
|
||||||
|
#summary.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
#description.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> Vec<String> {
|
||||||
|
vec![#(#tags),*]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args(&self) -> Vec<#docs_crate::StdLibFnArg> {
|
||||||
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
|
||||||
|
vec![#(#arg_types),*]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_value(&self) -> #docs_crate::StdLibFnArg {
|
||||||
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
|
||||||
|
#return_type
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpublished(&self) -> bool {
|
||||||
|
#unpublished
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deprecated(&self) -> bool {
|
||||||
|
#deprecated
|
||||||
|
}
|
||||||
|
|
||||||
|
fn std_lib_fn(&self) -> crate::std::StdFn {
|
||||||
|
#fn_name_ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#item
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepend the usage message if any errors were detected.
|
||||||
|
if !errors.is_empty() {
|
||||||
|
errors.insert(0, Error::new_spanned(&ast.sig, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((stream, errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
|
||||||
|
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
|
||||||
|
quote!(#(#compile_errors)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_crate(var: Option<String>) -> proc_macro2::TokenStream {
|
||||||
|
if let Some(s) = var {
|
||||||
|
if let Ok(ts) = syn::parse_str(s.as_str()) {
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quote!(crate::docs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_doc_from_attrs(attrs: &[syn::Attribute]) -> (Option<String>, Option<String>) {
|
||||||
|
let doc = syn::Ident::new("doc", proc_macro2::Span::call_site());
|
||||||
|
|
||||||
|
let mut lines = attrs.iter().flat_map(|attr| {
|
||||||
|
if let syn::Meta::NameValue(nv) = &attr.meta {
|
||||||
|
if nv.path.is_ident(&doc) {
|
||||||
|
if let syn::Expr::Lit(syn::ExprLit {
|
||||||
|
lit: syn::Lit::Str(s),
|
||||||
|
..
|
||||||
|
}) = &nv.value
|
||||||
|
{
|
||||||
|
return normalize_comment_string(s.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vec::new()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skip initial blank lines; they make for excessively terse summaries.
|
||||||
|
let summary = loop {
|
||||||
|
match lines.next() {
|
||||||
|
Some(s) if s.is_empty() => (),
|
||||||
|
next => break next,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Skip initial blank description lines.
|
||||||
|
let first = loop {
|
||||||
|
match lines.next() {
|
||||||
|
Some(s) if s.is_empty() => (),
|
||||||
|
next => break next,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match (summary, first) {
|
||||||
|
(None, _) => (None, None),
|
||||||
|
(summary, None) => (summary, None),
|
||||||
|
(Some(summary), Some(first)) => (
|
||||||
|
Some(summary),
|
||||||
|
Some(
|
||||||
|
lines
|
||||||
|
.fold(first, |acc, comment| {
|
||||||
|
if acc.ends_with('-') || acc.ends_with('\n') || acc.is_empty() {
|
||||||
|
// Continuation lines and newlines.
|
||||||
|
format!("{}{}", acc, comment)
|
||||||
|
} else if comment.is_empty() {
|
||||||
|
// Handle fully blank comments as newlines we keep.
|
||||||
|
format!("{}\n", acc)
|
||||||
|
} else {
|
||||||
|
// Default to space-separating comment fragments.
|
||||||
|
format!("{} {}", acc, comment)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.trim_end()
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_comment_string(s: String) -> Vec<String> {
|
||||||
|
s.split('\n')
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, s)| {
|
||||||
|
// Rust-style comments are intrinsically single-line. We don't want
|
||||||
|
// to trim away formatting such as an initial '*'.
|
||||||
|
if idx == 0 {
|
||||||
|
s.trim_start().trim_end()
|
||||||
|
} else {
|
||||||
|
let trimmed = s.trim_start().trim_end();
|
||||||
|
trimmed
|
||||||
|
.strip_prefix("* ")
|
||||||
|
.unwrap_or_else(|| trimmed.strip_prefix('*').unwrap_or(trimmed))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represent an item without concern for its body which may (or may not)
|
||||||
|
/// contain syntax errors.
|
||||||
|
struct ItemFnForSignature {
|
||||||
|
pub attrs: Vec<Attribute>,
|
||||||
|
pub vis: Visibility,
|
||||||
|
pub sig: Signature,
|
||||||
|
pub _block: proc_macro2::TokenStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ItemFnForSignature {
|
||||||
|
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
|
||||||
|
let attrs = input.call(Attribute::parse_outer)?;
|
||||||
|
let vis: Visibility = input.parse()?;
|
||||||
|
let sig: Signature = input.parse()?;
|
||||||
|
let block = input.parse()?;
|
||||||
|
Ok(ItemFnForSignature {
|
||||||
|
attrs,
|
||||||
|
vis,
|
||||||
|
sig,
|
||||||
|
_block: block,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_type(t: &str) -> String {
|
||||||
|
let mut t = t.to_string();
|
||||||
|
// Turn vecs into arrays.
|
||||||
|
if t.starts_with("Vec<") {
|
||||||
|
t = t.replace("Vec<", "[").replace('>', "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == "f64" {
|
||||||
|
return "number".to_string();
|
||||||
|
} else if t == "str" {
|
||||||
|
return "string".to_string();
|
||||||
|
} else {
|
||||||
|
return t.replace("f64", "number").to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stdlib_line_to() {
|
||||||
|
let (item, errors) = do_stdlib(
|
||||||
|
quote! {
|
||||||
|
name = "lineTo",
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
fn inner_line_to(
|
||||||
|
data: LineToData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let _expected = quote! {};
|
||||||
|
|
||||||
|
assert!(errors.is_empty());
|
||||||
|
expectorate::assert_contents(
|
||||||
|
"tests/lineTo.gen",
|
||||||
|
&openapitor::types::get_text_fmt(&item).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stdlib_min() {
|
||||||
|
let (item, errors) = do_stdlib(
|
||||||
|
quote! {
|
||||||
|
name = "min",
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
fn inner_min(
|
||||||
|
/// The args to do shit to.
|
||||||
|
args: Vec<f64>
|
||||||
|
) -> f64 {
|
||||||
|
let mut min = std::f64::MAX;
|
||||||
|
for arg in args.iter() {
|
||||||
|
if *arg < min {
|
||||||
|
min = *arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let _expected = quote! {};
|
||||||
|
|
||||||
|
assert!(errors.is_empty());
|
||||||
|
expectorate::assert_contents(
|
||||||
|
"tests/min.gen",
|
||||||
|
&openapitor::types::get_text_fmt(&item).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
76
src/wasm-lib/derive-docs/tests/lineTo.gen
Normal file
76
src/wasm-lib/derive-docs/tests/lineTo.gen
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#[allow(non_camel_case_types, missing_docs)]
|
||||||
|
#[doc = "Std lib function: lineTo"]
|
||||||
|
pub(crate) struct LineTo {}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals, missing_docs)]
|
||||||
|
#[doc = "Std lib function: lineTo"]
|
||||||
|
pub(crate) const LineTo: LineTo = LineTo {};
|
||||||
|
impl crate::docs::StdLibFn for LineTo {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"lineTo".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn summary(&self) -> String {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> Vec<String> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
vec![
|
||||||
|
crate::docs::StdLibFnArg {
|
||||||
|
name: "data".to_string(),
|
||||||
|
type_: "LineToData".to_string(),
|
||||||
|
schema: LineToData::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
crate::docs::StdLibFnArg {
|
||||||
|
name: "sketch_group".to_string(),
|
||||||
|
type_: "SketchGroup".to_string(),
|
||||||
|
schema: SketchGroup::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_value(&self) -> crate::docs::StdLibFnArg {
|
||||||
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
crate::docs::StdLibFnArg {
|
||||||
|
name: "".to_string(),
|
||||||
|
type_: "SketchGroup".to_string(),
|
||||||
|
schema: SketchGroup::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpublished(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deprecated(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn std_lib_fn(&self) -> crate::std::StdFn {
|
||||||
|
line_to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner_line_to(
|
||||||
|
data: LineToData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
71
src/wasm-lib/derive-docs/tests/min.gen
Normal file
71
src/wasm-lib/derive-docs/tests/min.gen
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#[allow(non_camel_case_types, missing_docs)]
|
||||||
|
#[doc = "Std lib function: min"]
|
||||||
|
pub(crate) struct Min {}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals, missing_docs)]
|
||||||
|
#[doc = "Std lib function: min"]
|
||||||
|
pub(crate) const Min: Min = Min {};
|
||||||
|
impl crate::docs::StdLibFn for Min {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"min".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn summary(&self) -> String {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> Vec<String> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "[number]".to_string(),
|
||||||
|
schema: <Vec<f64>>::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_value(&self) -> crate::docs::StdLibFnArg {
|
||||||
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
crate::docs::StdLibFnArg {
|
||||||
|
name: "".to_string(),
|
||||||
|
type_: "number".to_string(),
|
||||||
|
schema: f64::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpublished(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deprecated(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn std_lib_fn(&self) -> crate::std::StdFn {
|
||||||
|
min
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner_min(#[doc = r" The args to do shit to."] args: Vec<f64>) -> f64 {
|
||||||
|
let mut min = std::f64::MAX;
|
||||||
|
for arg in args.iter() {
|
||||||
|
if *arg < min {
|
||||||
|
min = *arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Map;
|
use serde_json::Map;
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ use crate::{
|
|||||||
executor::{MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange},
|
executor::{MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
@ -21,7 +22,7 @@ pub struct Program {
|
|||||||
pub non_code_meta: NoneCodeMeta,
|
pub non_code_meta: NoneCodeMeta,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum BodyItem {
|
pub enum BodyItem {
|
||||||
@ -30,7 +31,7 @@ pub enum BodyItem {
|
|||||||
ReturnStatement(ReturnStatement),
|
ReturnStatement(ReturnStatement),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@ -131,7 +132,7 @@ impl From<&Value> for crate::executor::SourceRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum BinaryPart {
|
pub enum BinaryPart {
|
||||||
@ -178,6 +179,7 @@ impl BinaryPart {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
pipe_info.is_in_pipe = false;
|
pipe_info.is_in_pipe = false;
|
||||||
@ -188,10 +190,10 @@ impl BinaryPart {
|
|||||||
Ok(value.clone())
|
Ok(value.clone())
|
||||||
}
|
}
|
||||||
BinaryPart::BinaryExpression(binary_expression) => {
|
BinaryPart::BinaryExpression(binary_expression) => {
|
||||||
binary_expression.get_result(memory, pipe_info, engine)
|
binary_expression.get_result(memory, pipe_info, stdlib, engine)
|
||||||
}
|
}
|
||||||
BinaryPart::CallExpression(call_expression) => {
|
BinaryPart::CallExpression(call_expression) => {
|
||||||
call_expression.execute(memory, pipe_info, engine)
|
call_expression.execute(memory, pipe_info, stdlib, engine)
|
||||||
}
|
}
|
||||||
BinaryPart::UnaryExpression(unary_expression) => {
|
BinaryPart::UnaryExpression(unary_expression) => {
|
||||||
// Return an error this should not happen.
|
// Return an error this should not happen.
|
||||||
@ -207,7 +209,7 @@ impl BinaryPart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct NoneCodeNode {
|
pub struct NoneCodeNode {
|
||||||
@ -216,7 +218,7 @@ pub struct NoneCodeNode {
|
|||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NoneCodeMeta {
|
pub struct NoneCodeMeta {
|
||||||
@ -250,7 +252,7 @@ impl<'de> Deserialize<'de> for NoneCodeMeta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct ExpressionStatement {
|
pub struct ExpressionStatement {
|
||||||
@ -261,7 +263,7 @@ pub struct ExpressionStatement {
|
|||||||
|
|
||||||
impl_value_meta!(ExpressionStatement);
|
impl_value_meta!(ExpressionStatement);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct CallExpression {
|
pub struct CallExpression {
|
||||||
@ -279,6 +281,7 @@ impl CallExpression {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
let fn_name = self.callee.name.clone();
|
let fn_name = self.callee.name.clone();
|
||||||
@ -293,20 +296,20 @@ impl CallExpression {
|
|||||||
value.clone()
|
value.clone()
|
||||||
}
|
}
|
||||||
Value::BinaryExpression(binary_expression) => {
|
Value::BinaryExpression(binary_expression) => {
|
||||||
binary_expression.get_result(memory, pipe_info, engine)?
|
binary_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
pipe_info.is_in_pipe = false;
|
pipe_info.is_in_pipe = false;
|
||||||
call_expression.execute(memory, pipe_info, engine)?
|
call_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::UnaryExpression(unary_expression) => {
|
Value::UnaryExpression(unary_expression) => {
|
||||||
unary_expression.get_result(memory, pipe_info, engine)?
|
unary_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::ObjectExpression(object_expression) => {
|
Value::ObjectExpression(object_expression) => {
|
||||||
object_expression.execute(memory, pipe_info, engine)?
|
object_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::ArrayExpression(array_expression) => {
|
Value::ArrayExpression(array_expression) => {
|
||||||
array_expression.execute(memory, pipe_info, engine)?
|
array_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::PipeExpression(pipe_expression) => {
|
Value::PipeExpression(pipe_expression) => {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
@ -353,7 +356,7 @@ impl CallExpression {
|
|||||||
fn_args.push(result);
|
fn_args.push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(func) = crate::std::INTERNAL_FNS.get(&fn_name) {
|
if let Some(func) = stdlib.fns.get(&fn_name) {
|
||||||
// Attempt to call the function.
|
// Attempt to call the function.
|
||||||
let mut args = crate::std::Args::new(fn_args, self.into(), engine);
|
let mut args = crate::std::Args::new(fn_args, self.into(), engine);
|
||||||
let result = func(&mut args)?;
|
let result = func(&mut args)?;
|
||||||
@ -365,6 +368,7 @@ impl CallExpression {
|
|||||||
&pipe_info.body.clone(),
|
&pipe_info.body.clone(),
|
||||||
pipe_info,
|
pipe_info,
|
||||||
self.into(),
|
self.into(),
|
||||||
|
stdlib,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -391,6 +395,7 @@ impl CallExpression {
|
|||||||
&pipe_info.body.clone(),
|
&pipe_info.body.clone(),
|
||||||
pipe_info,
|
pipe_info,
|
||||||
self.into(),
|
self.into(),
|
||||||
|
stdlib,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -400,7 +405,7 @@ impl CallExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct VariableDeclaration {
|
pub struct VariableDeclaration {
|
||||||
@ -412,7 +417,7 @@ pub struct VariableDeclaration {
|
|||||||
|
|
||||||
impl_value_meta!(VariableDeclaration);
|
impl_value_meta!(VariableDeclaration);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct VariableDeclarator {
|
pub struct VariableDeclarator {
|
||||||
@ -424,7 +429,7 @@ pub struct VariableDeclarator {
|
|||||||
|
|
||||||
impl_value_meta!(VariableDeclarator);
|
impl_value_meta!(VariableDeclarator);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct Literal {
|
pub struct Literal {
|
||||||
@ -458,7 +463,7 @@ impl From<&Box<Literal>> for MemoryItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct Identifier {
|
pub struct Identifier {
|
||||||
@ -469,7 +474,7 @@ pub struct Identifier {
|
|||||||
|
|
||||||
impl_value_meta!(Identifier);
|
impl_value_meta!(Identifier);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct PipeSubstitution {
|
pub struct PipeSubstitution {
|
||||||
@ -479,7 +484,7 @@ pub struct PipeSubstitution {
|
|||||||
|
|
||||||
impl_value_meta!(PipeSubstitution);
|
impl_value_meta!(PipeSubstitution);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct ArrayExpression {
|
pub struct ArrayExpression {
|
||||||
@ -495,6 +500,7 @@ impl ArrayExpression {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
let mut results = Vec::with_capacity(self.elements.len());
|
let mut results = Vec::with_capacity(self.elements.len());
|
||||||
@ -507,23 +513,23 @@ impl ArrayExpression {
|
|||||||
value.clone()
|
value.clone()
|
||||||
}
|
}
|
||||||
Value::BinaryExpression(binary_expression) => {
|
Value::BinaryExpression(binary_expression) => {
|
||||||
binary_expression.get_result(memory, pipe_info, engine)?
|
binary_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
pipe_info.is_in_pipe = false;
|
pipe_info.is_in_pipe = false;
|
||||||
call_expression.execute(memory, pipe_info, engine)?
|
call_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::UnaryExpression(unary_expression) => {
|
Value::UnaryExpression(unary_expression) => {
|
||||||
unary_expression.get_result(memory, pipe_info, engine)?
|
unary_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::ObjectExpression(object_expression) => {
|
Value::ObjectExpression(object_expression) => {
|
||||||
object_expression.execute(memory, pipe_info, engine)?
|
object_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::ArrayExpression(array_expression) => {
|
Value::ArrayExpression(array_expression) => {
|
||||||
array_expression.execute(memory, pipe_info, engine)?
|
array_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::PipeExpression(pipe_expression) => {
|
Value::PipeExpression(pipe_expression) => {
|
||||||
pipe_expression.get_result(memory, pipe_info, engine)?
|
pipe_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::PipeSubstitution(pipe_substitution) => {
|
Value::PipeSubstitution(pipe_substitution) => {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
@ -567,7 +573,7 @@ impl ArrayExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct ObjectExpression {
|
pub struct ObjectExpression {
|
||||||
@ -581,6 +587,7 @@ impl ObjectExpression {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
let mut object = Map::new();
|
let mut object = Map::new();
|
||||||
@ -592,23 +599,23 @@ impl ObjectExpression {
|
|||||||
value.clone()
|
value.clone()
|
||||||
}
|
}
|
||||||
Value::BinaryExpression(binary_expression) => {
|
Value::BinaryExpression(binary_expression) => {
|
||||||
binary_expression.get_result(memory, pipe_info, engine)?
|
binary_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
pipe_info.is_in_pipe = false;
|
pipe_info.is_in_pipe = false;
|
||||||
call_expression.execute(memory, pipe_info, engine)?
|
call_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::UnaryExpression(unary_expression) => {
|
Value::UnaryExpression(unary_expression) => {
|
||||||
unary_expression.get_result(memory, pipe_info, engine)?
|
unary_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::ObjectExpression(object_expression) => {
|
Value::ObjectExpression(object_expression) => {
|
||||||
object_expression.execute(memory, pipe_info, engine)?
|
object_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::ArrayExpression(array_expression) => {
|
Value::ArrayExpression(array_expression) => {
|
||||||
array_expression.execute(memory, pipe_info, engine)?
|
array_expression.execute(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::PipeExpression(pipe_expression) => {
|
Value::PipeExpression(pipe_expression) => {
|
||||||
pipe_expression.get_result(memory, pipe_info, engine)?
|
pipe_expression.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
}
|
}
|
||||||
Value::PipeSubstitution(pipe_substitution) => {
|
Value::PipeSubstitution(pipe_substitution) => {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
@ -653,7 +660,7 @@ impl ObjectExpression {
|
|||||||
|
|
||||||
impl_value_meta!(ObjectExpression);
|
impl_value_meta!(ObjectExpression);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct ObjectProperty {
|
pub struct ObjectProperty {
|
||||||
@ -665,7 +672,7 @@ pub struct ObjectProperty {
|
|||||||
|
|
||||||
impl_value_meta!(ObjectProperty);
|
impl_value_meta!(ObjectProperty);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum MemberObject {
|
pub enum MemberObject {
|
||||||
@ -673,7 +680,7 @@ pub enum MemberObject {
|
|||||||
Identifier(Box<Identifier>),
|
Identifier(Box<Identifier>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum LiteralIdentifier {
|
pub enum LiteralIdentifier {
|
||||||
@ -681,7 +688,7 @@ pub enum LiteralIdentifier {
|
|||||||
Literal(Box<Literal>),
|
Literal(Box<Literal>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct MemberExpression {
|
pub struct MemberExpression {
|
||||||
@ -747,7 +754,7 @@ impl MemberExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct ObjectKeyInfo {
|
pub struct ObjectKeyInfo {
|
||||||
pub key: LiteralIdentifier,
|
pub key: LiteralIdentifier,
|
||||||
@ -755,7 +762,7 @@ pub struct ObjectKeyInfo {
|
|||||||
pub computed: bool,
|
pub computed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct BinaryExpression {
|
pub struct BinaryExpression {
|
||||||
@ -774,17 +781,18 @@ impl BinaryExpression {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
pipe_info.is_in_pipe = false;
|
pipe_info.is_in_pipe = false;
|
||||||
|
|
||||||
let left_json_value = self
|
let left_json_value = self
|
||||||
.left
|
.left
|
||||||
.get_result(memory, pipe_info, engine)?
|
.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
.get_json_value()?;
|
.get_json_value()?;
|
||||||
let right_json_value = self
|
let right_json_value = self
|
||||||
.right
|
.right
|
||||||
.get_result(memory, pipe_info, engine)?
|
.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
.get_json_value()?;
|
.get_json_value()?;
|
||||||
|
|
||||||
// First check if we are doing string concatenation.
|
// First check if we are doing string concatenation.
|
||||||
@ -856,7 +864,7 @@ pub fn parse_json_value_as_string(j: &serde_json::Value) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct UnaryExpression {
|
pub struct UnaryExpression {
|
||||||
@ -873,6 +881,7 @@ impl UnaryExpression {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
pipe_info.is_in_pipe = false;
|
pipe_info.is_in_pipe = false;
|
||||||
@ -880,7 +889,7 @@ impl UnaryExpression {
|
|||||||
let num = parse_json_number_as_f64(
|
let num = parse_json_number_as_f64(
|
||||||
&self
|
&self
|
||||||
.argument
|
.argument
|
||||||
.get_result(memory, pipe_info, engine)?
|
.get_result(memory, pipe_info, stdlib, engine)?
|
||||||
.get_json_value()?,
|
.get_json_value()?,
|
||||||
self.into(),
|
self.into(),
|
||||||
)?;
|
)?;
|
||||||
@ -893,7 +902,7 @@ impl UnaryExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", tag = "type")]
|
#[serde(rename_all = "camelCase", tag = "type")]
|
||||||
pub struct PipeExpression {
|
pub struct PipeExpression {
|
||||||
@ -910,12 +919,13 @@ impl PipeExpression {
|
|||||||
&self,
|
&self,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
// Reset the previous results.
|
// Reset the previous results.
|
||||||
pipe_info.previous_results = vec![];
|
pipe_info.previous_results = vec![];
|
||||||
pipe_info.index = 0;
|
pipe_info.index = 0;
|
||||||
execute_pipe_body(memory, &self.body, pipe_info, self.into(), engine)
|
execute_pipe_body(memory, &self.body, pipe_info, self.into(), stdlib, engine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,6 +934,7 @@ fn execute_pipe_body(
|
|||||||
body: &[Value],
|
body: &[Value],
|
||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
stdlib: &crate::std::StdLib,
|
||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
if pipe_info.index == body.len() {
|
if pipe_info.index == body.len() {
|
||||||
@ -949,15 +960,15 @@ fn execute_pipe_body(
|
|||||||
|
|
||||||
match expression {
|
match expression {
|
||||||
Value::BinaryExpression(binary_expression) => {
|
Value::BinaryExpression(binary_expression) => {
|
||||||
let result = binary_expression.get_result(memory, pipe_info, engine)?;
|
let result = binary_expression.get_result(memory, pipe_info, stdlib, engine)?;
|
||||||
pipe_info.previous_results.push(result);
|
pipe_info.previous_results.push(result);
|
||||||
pipe_info.index += 1;
|
pipe_info.index += 1;
|
||||||
execute_pipe_body(memory, body, pipe_info, source_range, engine)
|
execute_pipe_body(memory, body, pipe_info, source_range, stdlib, engine)
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
pipe_info.is_in_pipe = true;
|
pipe_info.is_in_pipe = true;
|
||||||
pipe_info.body = body.to_vec();
|
pipe_info.body = body.to_vec();
|
||||||
call_expression.execute(memory, pipe_info, engine)
|
call_expression.execute(memory, pipe_info, stdlib, engine)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Return an error this should not happen.
|
// Return an error this should not happen.
|
||||||
@ -969,7 +980,7 @@ fn execute_pipe_body(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct FunctionExpression {
|
pub struct FunctionExpression {
|
||||||
@ -982,7 +993,7 @@ pub struct FunctionExpression {
|
|||||||
|
|
||||||
impl_value_meta!(FunctionExpression);
|
impl_value_meta!(FunctionExpression);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub struct ReturnStatement {
|
pub struct ReturnStatement {
|
||||||
|
195
src/wasm-lib/src/docs.rs
Normal file
195
src/wasm-lib/src/docs.rs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
//! Functions for generating docs for our stdlib functions.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::std::Primitive;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub struct StdLibFnData {
|
||||||
|
/// The name of the function.
|
||||||
|
pub name: String,
|
||||||
|
/// The summary of the function.
|
||||||
|
pub summary: String,
|
||||||
|
/// The description of the function.
|
||||||
|
pub description: String,
|
||||||
|
/// The tags of the function.
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
/// The args of the function.
|
||||||
|
pub args: Vec<StdLibFnArg>,
|
||||||
|
/// The return value of the function.
|
||||||
|
pub return_value: StdLibFnArg,
|
||||||
|
/// If the function is unpublished.
|
||||||
|
pub unpublished: bool,
|
||||||
|
/// If the function is deprecated.
|
||||||
|
pub deprecated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This struct defines a single argument to a stdlib function.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub struct StdLibFnArg {
|
||||||
|
/// The name of the argument.
|
||||||
|
pub name: String,
|
||||||
|
/// The type of the argument.
|
||||||
|
pub type_: String,
|
||||||
|
/// The schema of the argument.
|
||||||
|
pub schema: schemars::schema::Schema,
|
||||||
|
/// If the argument is required.
|
||||||
|
pub required: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdLibFnArg {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_type_string(&self) -> Result<(String, bool)> {
|
||||||
|
get_type_string_from_schema(&self.schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn description(&self) -> Option<String> {
|
||||||
|
get_description_string_from_schema(&self.schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait defines functions called upon stdlib functions to generate
|
||||||
|
/// documentation for them.
|
||||||
|
pub trait StdLibFn {
|
||||||
|
/// The name of the function.
|
||||||
|
fn name(&self) -> String;
|
||||||
|
|
||||||
|
/// The summary of the function.
|
||||||
|
fn summary(&self) -> String;
|
||||||
|
|
||||||
|
/// The description of the function.
|
||||||
|
fn description(&self) -> String;
|
||||||
|
|
||||||
|
/// The tags of the function.
|
||||||
|
fn tags(&self) -> Vec<String>;
|
||||||
|
|
||||||
|
/// The args of the function.
|
||||||
|
fn args(&self) -> Vec<StdLibFnArg>;
|
||||||
|
|
||||||
|
/// The return value of the function.
|
||||||
|
fn return_value(&self) -> StdLibFnArg;
|
||||||
|
|
||||||
|
/// If the function is unpublished.
|
||||||
|
fn unpublished(&self) -> bool;
|
||||||
|
|
||||||
|
/// If the function is deprecated.
|
||||||
|
fn deprecated(&self) -> bool;
|
||||||
|
|
||||||
|
/// The function itself.
|
||||||
|
fn std_lib_fn(&self) -> crate::std::StdFn;
|
||||||
|
|
||||||
|
/// Return a JSON struct representing the function.
|
||||||
|
fn to_json(&self) -> Result<StdLibFnData> {
|
||||||
|
Ok(StdLibFnData {
|
||||||
|
name: self.name(),
|
||||||
|
summary: self.summary(),
|
||||||
|
description: self.description(),
|
||||||
|
tags: self.tags(),
|
||||||
|
args: self.args(),
|
||||||
|
return_value: self.return_value(),
|
||||||
|
unpublished: self.unpublished(),
|
||||||
|
deprecated: self.deprecated(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Option<String> {
|
||||||
|
if let schemars::schema::Schema::Object(o) = schema {
|
||||||
|
if let Some(metadata) = &o.metadata {
|
||||||
|
if let Some(description) = &metadata.description {
|
||||||
|
return Some(description.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(String, bool)> {
|
||||||
|
match schema {
|
||||||
|
schemars::schema::Schema::Object(o) => {
|
||||||
|
if let Some(format) = &o.format {
|
||||||
|
if format == "uuid" {
|
||||||
|
return Ok((Primitive::Uuid.to_string(), false));
|
||||||
|
} else if format == "double" || format == "uint" {
|
||||||
|
return Ok((Primitive::Number.to_string(), false));
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("unknown format: {}", format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(obj_val) = &o.object {
|
||||||
|
let mut fn_docs = String::new();
|
||||||
|
fn_docs.push_str("{\n");
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
for (prop_name, prop) in obj_val.properties.iter() {
|
||||||
|
if prop_name.starts_with('_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(description) = get_description_string_from_schema(prop) {
|
||||||
|
fn_docs.push_str(&format!("\t// {}\n", description));
|
||||||
|
}
|
||||||
|
fn_docs.push_str(&format!(
|
||||||
|
"\t\"{}\": {},\n",
|
||||||
|
prop_name,
|
||||||
|
get_type_string_from_schema(prop)?.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_docs.push('}');
|
||||||
|
|
||||||
|
return Ok((fn_docs, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(array_val) = &o.array {
|
||||||
|
if let Some(schemars::schema::SingleOrVec::Single(items)) = &array_val.items {
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
return Ok((
|
||||||
|
format!("[{}]", get_type_string_from_schema(items)?.0),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
} else if let Some(items) = &array_val.contains {
|
||||||
|
return Ok((
|
||||||
|
format!("[{}]", get_type_string_from_schema(items)?.0),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(subschemas) = &o.subschemas {
|
||||||
|
let mut fn_docs = String::new();
|
||||||
|
if let Some(items) = &subschemas.one_of {
|
||||||
|
for (i, item) in items.iter().enumerate() {
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
fn_docs.push_str(&get_type_string_from_schema(item)?.0.to_string());
|
||||||
|
if i < items.len() - 1 {
|
||||||
|
fn_docs.push_str(" |\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(items) = &subschemas.any_of {
|
||||||
|
for (i, item) in items.iter().enumerate() {
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
fn_docs.push_str(&get_type_string_from_schema(item)?.0.to_string());
|
||||||
|
if i < items.len() - 1 {
|
||||||
|
fn_docs.push_str(" |\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("unknown subschemas: {:#?}", subschemas);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok((fn_docs, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(schemars::schema::SingleOrVec::Single(_string)) = &o.instance_type {
|
||||||
|
return Ok((Primitive::String.to_string(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::bail!("unknown type: {:#?}", o)
|
||||||
|
}
|
||||||
|
schemars::schema::Schema::Bool(_) => Ok((Primitive::Bool.to_string(), false)),
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
@ -13,7 +15,7 @@ use crate::{
|
|||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ProgramMemory {
|
pub struct ProgramMemory {
|
||||||
@ -66,7 +68,7 @@ impl Default for ProgramMemory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum ProgramReturn {
|
pub enum ProgramReturn {
|
||||||
@ -101,7 +103,7 @@ impl ProgramReturn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub enum MemoryItem {
|
pub enum MemoryItem {
|
||||||
@ -112,12 +114,7 @@ pub enum MemoryItem {
|
|||||||
},
|
},
|
||||||
SketchGroup(SketchGroup),
|
SketchGroup(SketchGroup),
|
||||||
ExtrudeGroup(ExtrudeGroup),
|
ExtrudeGroup(ExtrudeGroup),
|
||||||
ExtrudeTransform {
|
ExtrudeTransform(ExtrudeTransform),
|
||||||
position: Position,
|
|
||||||
rotation: Rotation,
|
|
||||||
#[serde(rename = "__meta")]
|
|
||||||
meta: Vec<Metadata>,
|
|
||||||
},
|
|
||||||
Function {
|
Function {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
func: Option<MemoryFunction>,
|
func: Option<MemoryFunction>,
|
||||||
@ -127,6 +124,16 @@ pub enum MemoryItem {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ExtrudeTransform {
|
||||||
|
pub position: Position,
|
||||||
|
pub rotation: Rotation,
|
||||||
|
#[serde(rename = "__meta")]
|
||||||
|
pub meta: Vec<Metadata>,
|
||||||
|
}
|
||||||
|
|
||||||
pub type MemoryFunction = fn(
|
pub type MemoryFunction = fn(
|
||||||
s: &[MemoryItem],
|
s: &[MemoryItem],
|
||||||
memory: &ProgramMemory,
|
memory: &ProgramMemory,
|
||||||
@ -141,9 +148,7 @@ impl From<MemoryItem> for Vec<SourceRange> {
|
|||||||
MemoryItem::UserVal { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
MemoryItem::UserVal { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||||
MemoryItem::SketchGroup(s) => s.meta.iter().map(|m| m.source_range).collect(),
|
MemoryItem::SketchGroup(s) => s.meta.iter().map(|m| m.source_range).collect(),
|
||||||
MemoryItem::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
MemoryItem::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||||
MemoryItem::ExtrudeTransform { meta, .. } => {
|
MemoryItem::ExtrudeTransform(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||||
meta.iter().map(|m| m.source_range).collect()
|
|
||||||
}
|
|
||||||
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,15 +195,22 @@ impl MemoryItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// A sketch group is a collection of paths.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SketchGroup {
|
pub struct SketchGroup {
|
||||||
|
/// The id of the sketch group.
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
|
/// The paths in the sketch group.
|
||||||
pub value: Vec<Path>,
|
pub value: Vec<Path>,
|
||||||
|
/// The starting path.
|
||||||
pub start: BasePath,
|
pub start: BasePath,
|
||||||
|
/// The position of the sketch group.
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
|
/// The rotation of the sketch group.
|
||||||
pub rotation: Rotation,
|
pub rotation: Rotation,
|
||||||
|
/// Metadata.
|
||||||
#[serde(rename = "__meta")]
|
#[serde(rename = "__meta")]
|
||||||
pub meta: Vec<Metadata>,
|
pub meta: Vec<Metadata>,
|
||||||
}
|
}
|
||||||
@ -238,15 +250,22 @@ impl SketchGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// An extrude group is a collection of extrude surfaces.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ExtrudeGroup {
|
pub struct ExtrudeGroup {
|
||||||
|
/// The id of the extrude group.
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
|
/// The extrude surfaces.
|
||||||
pub value: Vec<ExtrudeSurface>,
|
pub value: Vec<ExtrudeSurface>,
|
||||||
|
/// The height of the extrude group.
|
||||||
pub height: f64,
|
pub height: f64,
|
||||||
|
/// The position of the extrude group.
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
|
/// The rotation of the extrude group.
|
||||||
pub rotation: Rotation,
|
pub rotation: Rotation,
|
||||||
|
/// Metadata.
|
||||||
#[serde(rename = "__meta")]
|
#[serde(rename = "__meta")]
|
||||||
pub meta: Vec<Metadata>,
|
pub meta: Vec<Metadata>,
|
||||||
}
|
}
|
||||||
@ -261,7 +280,7 @@ impl ExtrudeGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum BodyType {
|
pub enum BodyType {
|
||||||
@ -270,19 +289,19 @@ pub enum BodyType {
|
|||||||
Block,
|
Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Position(pub [f64; 3]);
|
pub struct Position(pub [f64; 3]);
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Rotation(pub [f64; 4]);
|
pub struct Rotation(pub [f64; 4]);
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS)]
|
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct SourceRange(pub [usize; 2]);
|
pub struct SourceRange(pub [usize; 2]);
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Point2d {
|
pub struct Point2d {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
@ -301,7 +320,7 @@ impl From<Point2d> for [f64; 2] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Point3d {
|
pub struct Point3d {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
@ -309,10 +328,12 @@ pub struct Point3d {
|
|||||||
pub z: f64,
|
pub z: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Metadata.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
|
/// The source range.
|
||||||
pub source_range: SourceRange,
|
pub source_range: SourceRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,45 +343,61 @@ impl From<SourceRange> for Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// A base path.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BasePath {
|
pub struct BasePath {
|
||||||
|
/// The from point.
|
||||||
pub from: [f64; 2],
|
pub from: [f64; 2],
|
||||||
|
/// The to point.
|
||||||
pub to: [f64; 2],
|
pub to: [f64; 2],
|
||||||
|
/// The name of the path.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Metadata.
|
||||||
#[serde(rename = "__geoMeta")]
|
#[serde(rename = "__geoMeta")]
|
||||||
pub geo_meta: GeoMeta,
|
pub geo_meta: GeoMeta,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Geometry metadata.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GeoMeta {
|
pub struct GeoMeta {
|
||||||
|
/// The id of the geometry.
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
|
/// Metadata.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub metadata: Metadata,
|
pub metadata: Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// A path.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub enum Path {
|
pub enum Path {
|
||||||
|
/// A path that goes to a point.
|
||||||
ToPoint {
|
ToPoint {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
base: BasePath,
|
base: BasePath,
|
||||||
},
|
},
|
||||||
|
/// A path that is horizontal.
|
||||||
Horizontal {
|
Horizontal {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
base: BasePath,
|
base: BasePath,
|
||||||
|
/// The x coordinate.
|
||||||
x: f64,
|
x: f64,
|
||||||
},
|
},
|
||||||
|
/// An angled line to.
|
||||||
AngledLineTo {
|
AngledLineTo {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
base: BasePath,
|
base: BasePath,
|
||||||
|
/// The x coordinate.
|
||||||
x: Option<f64>,
|
x: Option<f64>,
|
||||||
|
/// The y coordinate.
|
||||||
y: Option<f64>,
|
y: Option<f64>,
|
||||||
},
|
},
|
||||||
|
/// A base path.
|
||||||
Base {
|
Base {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
base: BasePath,
|
base: BasePath,
|
||||||
@ -396,14 +433,20 @@ impl Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// An extrude surface.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub enum ExtrudeSurface {
|
pub enum ExtrudeSurface {
|
||||||
|
/// An extrude plane.
|
||||||
ExtrudePlane {
|
ExtrudePlane {
|
||||||
|
/// The position.
|
||||||
position: Position,
|
position: Position,
|
||||||
|
/// The rotation.
|
||||||
rotation: Rotation,
|
rotation: Rotation,
|
||||||
|
/// The name.
|
||||||
name: String,
|
name: String,
|
||||||
|
/// Metadata.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
geo_meta: GeoMeta,
|
geo_meta: GeoMeta,
|
||||||
},
|
},
|
||||||
@ -435,7 +478,7 @@ impl ExtrudeSurface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PipeInfo {
|
pub struct PipeInfo {
|
||||||
@ -470,6 +513,7 @@ fn execute(
|
|||||||
engine: &mut EngineConnection,
|
engine: &mut EngineConnection,
|
||||||
) -> Result<ProgramMemory, KclError> {
|
) -> Result<ProgramMemory, KclError> {
|
||||||
let mut pipe_info = PipeInfo::default();
|
let mut pipe_info = PipeInfo::default();
|
||||||
|
let stdlib = crate::std::StdLib::new();
|
||||||
|
|
||||||
// Iterate over the body of the program.
|
// Iterate over the body of the program.
|
||||||
for statement in &program.body {
|
for statement in &program.body {
|
||||||
@ -525,8 +569,12 @@ fn execute(
|
|||||||
memory.add(&var_name, value.clone(), source_range)?;
|
memory.add(&var_name, value.clone(), source_range)?;
|
||||||
}
|
}
|
||||||
Value::BinaryExpression(binary_expression) => {
|
Value::BinaryExpression(binary_expression) => {
|
||||||
let result =
|
let result = binary_expression.get_result(
|
||||||
binary_expression.get_result(memory, &mut pipe_info, engine)?;
|
memory,
|
||||||
|
&mut pipe_info,
|
||||||
|
&stdlib,
|
||||||
|
engine,
|
||||||
|
)?;
|
||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
Value::FunctionExpression(function_expression) => {
|
Value::FunctionExpression(function_expression) => {
|
||||||
@ -563,12 +611,17 @@ fn execute(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
let result = call_expression.execute(memory, &mut pipe_info, engine)?;
|
let result =
|
||||||
|
call_expression.execute(memory, &mut pipe_info, &stdlib, engine)?;
|
||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
Value::PipeExpression(pipe_expression) => {
|
Value::PipeExpression(pipe_expression) => {
|
||||||
let result =
|
let result = pipe_expression.get_result(
|
||||||
pipe_expression.get_result(memory, &mut pipe_info, engine)?;
|
memory,
|
||||||
|
&mut pipe_info,
|
||||||
|
&stdlib,
|
||||||
|
engine,
|
||||||
|
)?;
|
||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
Value::PipeSubstitution(pipe_substitution) => {
|
Value::PipeSubstitution(pipe_substitution) => {
|
||||||
@ -578,13 +631,21 @@ fn execute(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Value::ArrayExpression(array_expression) => {
|
Value::ArrayExpression(array_expression) => {
|
||||||
let result =
|
let result = array_expression.execute(
|
||||||
array_expression.execute(memory, &mut pipe_info, engine)?;
|
memory,
|
||||||
|
&mut pipe_info,
|
||||||
|
&stdlib,
|
||||||
|
engine,
|
||||||
|
)?;
|
||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
Value::ObjectExpression(object_expression) => {
|
Value::ObjectExpression(object_expression) => {
|
||||||
let result =
|
let result = object_expression.execute(
|
||||||
object_expression.execute(memory, &mut pipe_info, engine)?;
|
memory,
|
||||||
|
&mut pipe_info,
|
||||||
|
&stdlib,
|
||||||
|
engine,
|
||||||
|
)?;
|
||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
Value::MemberExpression(member_expression) => {
|
Value::MemberExpression(member_expression) => {
|
||||||
@ -592,8 +653,12 @@ fn execute(
|
|||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
Value::UnaryExpression(unary_expression) => {
|
Value::UnaryExpression(unary_expression) => {
|
||||||
let result =
|
let result = unary_expression.get_result(
|
||||||
unary_expression.get_result(memory, &mut pipe_info, engine)?;
|
memory,
|
||||||
|
&mut pipe_info,
|
||||||
|
&stdlib,
|
||||||
|
engine,
|
||||||
|
)?;
|
||||||
memory.add(&var_name, result, source_range)?;
|
memory.add(&var_name, result, source_range)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -601,7 +666,7 @@ fn execute(
|
|||||||
}
|
}
|
||||||
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
|
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
|
||||||
Value::BinaryExpression(bin_expr) => {
|
Value::BinaryExpression(bin_expr) => {
|
||||||
let result = bin_expr.get_result(memory, &mut pipe_info, engine)?;
|
let result = bin_expr.get_result(memory, &mut pipe_info, &stdlib, engine)?;
|
||||||
memory.return_ = Some(ProgramReturn::Value(result));
|
memory.return_ = Some(ProgramReturn::Value(result));
|
||||||
}
|
}
|
||||||
Value::Identifier(identifier) => {
|
Value::Identifier(identifier) => {
|
||||||
@ -671,7 +736,6 @@ mod tests {
|
|||||||
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
||||||
let tokens = crate::tokeniser::lexer(code);
|
let tokens = crate::tokeniser::lexer(code);
|
||||||
let program = crate::parser::abstract_syntax_tree(&tokens)?;
|
let program = crate::parser::abstract_syntax_tree(&tokens)?;
|
||||||
println!("{:#?}", program);
|
|
||||||
let mut mem: ProgramMemory = Default::default();
|
let mut mem: ProgramMemory = Default::default();
|
||||||
let mut engine = EngineConnection::new("dev.kittycad.io", "some-token", "").await?;
|
let mut engine = EngineConnection::new("dev.kittycad.io", "some-token", "").await?;
|
||||||
let memory = execute(program, &mut mem, BodyType::Root, &mut engine)?;
|
let memory = execute(program, &mut mem, BodyType::Root, &mut engine)?;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mod abstract_syntax_tree_types;
|
mod abstract_syntax_tree_types;
|
||||||
|
mod docs;
|
||||||
mod engine;
|
mod engine;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod executor;
|
mod executor;
|
||||||
|
@ -2,16 +2,32 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{ExtrudeGroup, MemoryItem},
|
executor::{ExtrudeGroup, ExtrudeTransform, MemoryItem, SketchGroup},
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use derive_docs::stdlib;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
/// Extrudes by a given amount.
|
/// Extrudes by a given amount.
|
||||||
pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (length, sketch_group) = args.get_number_sketch_group()?;
|
let (length, sketch_group) = args.get_number_sketch_group()?;
|
||||||
|
|
||||||
|
let result = inner_extrude(length, sketch_group, args)?;
|
||||||
|
|
||||||
|
Ok(MemoryItem::ExtrudeGroup(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extrudes by a given amount.
|
||||||
|
#[stdlib {
|
||||||
|
name = "extrude"
|
||||||
|
}]
|
||||||
|
fn inner_extrude(
|
||||||
|
length: f64,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<ExtrudeGroup, KclError> {
|
||||||
let id = uuid::Uuid::new_v4();
|
let id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
let cmd = kittycad::types::ModelingCmd::Extrude {
|
let cmd = kittycad::types::ModelingCmd::Extrude {
|
||||||
@ -21,7 +37,7 @@ pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
};
|
};
|
||||||
args.send_modeling_cmd(id, cmd)?;
|
args.send_modeling_cmd(id, cmd)?;
|
||||||
|
|
||||||
Ok(MemoryItem::ExtrudeGroup(ExtrudeGroup {
|
Ok(ExtrudeGroup {
|
||||||
id,
|
id,
|
||||||
// TODO, this is just an empty array now, should be deleted. This
|
// TODO, this is just an empty array now, should be deleted. This
|
||||||
// comment was originally in the JS code.
|
// comment was originally in the JS code.
|
||||||
@ -30,14 +46,27 @@ pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
position: sketch_group.position,
|
position: sketch_group.position,
|
||||||
rotation: sketch_group.rotation,
|
rotation: sketch_group.rotation,
|
||||||
meta: sketch_group.meta,
|
meta: sketch_group.meta,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the extrude wall transform.
|
/// Returns the extrude wall transform.
|
||||||
pub fn get_extrude_wall_transform(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn get_extrude_wall_transform(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (surface_name, extrude_group) = args.get_path_name_extrude_group()?;
|
let (surface_name, extrude_group) = args.get_path_name_extrude_group()?;
|
||||||
|
let result = inner_get_extrude_wall_transform(&surface_name, extrude_group, args)?;
|
||||||
|
Ok(MemoryItem::ExtrudeTransform(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the extrude wall transform.
|
||||||
|
#[stdlib {
|
||||||
|
name = "getExtrudeWallTransform"
|
||||||
|
}]
|
||||||
|
fn inner_get_extrude_wall_transform(
|
||||||
|
surface_name: &str,
|
||||||
|
extrude_group: ExtrudeGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<ExtrudeTransform, KclError> {
|
||||||
let surface = extrude_group
|
let surface = extrude_group
|
||||||
.get_path_by_name(&surface_name)
|
.get_path_by_name(surface_name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
message: format!(
|
message: format!(
|
||||||
@ -48,7 +77,7 @@ pub fn get_extrude_wall_transform(args: &mut Args) -> Result<MemoryItem, KclErro
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(MemoryItem::ExtrudeTransform {
|
Ok(ExtrudeTransform {
|
||||||
position: surface.get_position(),
|
position: surface.get_position(),
|
||||||
rotation: surface.get_rotation(),
|
rotation: surface.get_rotation(),
|
||||||
meta: extrude_group.meta,
|
meta: extrude_group.meta,
|
||||||
|
@ -10,69 +10,81 @@ mod utils;
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use derive_docs::stdlib;
|
||||||
|
use parse_display::{Display, FromStr};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_syntax_tree_types::parse_json_number_as_f64,
|
abstract_syntax_tree_types::parse_json_number_as_f64,
|
||||||
engine::EngineConnection,
|
engine::EngineConnection,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SourceRange},
|
executor::{ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SourceRange},
|
||||||
std::extrude::{extrude, get_extrude_wall_transform},
|
|
||||||
std::segment::{
|
|
||||||
angle_to_match_length_x, angle_to_match_length_y, last_segment_x, last_segment_y,
|
|
||||||
segment_angle, segment_end_x, segment_end_y, segment_length,
|
|
||||||
},
|
|
||||||
std::sketch::{
|
|
||||||
angled_line, angled_line_of_x_length, angled_line_of_y_length, angled_line_that_intersects,
|
|
||||||
angled_line_to_x, angled_line_to_y, close, line, line_to, start_sketch_at, x_line,
|
|
||||||
x_line_to, y_line, y_line_to,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
pub type FnMap = HashMap<String, StdFn>;
|
pub type FnMap = HashMap<String, StdFn>;
|
||||||
pub type StdFn = fn(&mut Args) -> Result<MemoryItem, KclError>;
|
pub type StdFn = fn(&mut Args) -> Result<MemoryItem, KclError>;
|
||||||
|
|
||||||
lazy_static! {
|
pub struct StdLib {
|
||||||
pub static ref INTERNAL_FNS: FnMap =
|
#[allow(dead_code)]
|
||||||
{
|
internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>>,
|
||||||
HashMap::from([
|
|
||||||
// Extrude functions.
|
|
||||||
("extrude".to_string(), extrude as StdFn),
|
|
||||||
("getExtrudeWallTransform".to_string(), get_extrude_wall_transform as StdFn),
|
|
||||||
|
|
||||||
("min".to_string(), min as StdFn),
|
pub fns: FnMap,
|
||||||
("legLen".to_string(), leg_length as StdFn),
|
}
|
||||||
("legAngX".to_string(),leg_angle_x as StdFn),
|
|
||||||
("legAngY".to_string(), leg_angle_y as StdFn),
|
|
||||||
// Sketch segment functions.
|
|
||||||
("segEndX".to_string(), segment_end_x as StdFn),
|
|
||||||
("segEndY".to_string(), segment_end_y as StdFn),
|
|
||||||
("lastSegX".to_string(), last_segment_x as StdFn),
|
|
||||||
("lastSegY".to_string(), last_segment_y as StdFn),
|
|
||||||
("segLen".to_string(), segment_length as StdFn),
|
|
||||||
("segAng".to_string(), segment_angle as StdFn),
|
|
||||||
("angleToMatchLengthX".to_string(), angle_to_match_length_x as StdFn),
|
|
||||||
("angleToMatchLengthY".to_string(), angle_to_match_length_y as StdFn),
|
|
||||||
|
|
||||||
// Sketch functions.
|
impl StdLib {
|
||||||
("lineTo".to_string(), line_to as StdFn),
|
pub fn new() -> Self {
|
||||||
("xLineTo".to_string(), x_line_to as StdFn),
|
let internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>> = vec![
|
||||||
("yLineTo".to_string(), y_line_to as StdFn),
|
Box::new(Min),
|
||||||
("line".to_string(), line as StdFn),
|
Box::new(LegLen),
|
||||||
("xLine".to_string(), x_line as StdFn),
|
Box::new(LegAngX),
|
||||||
("yLine".to_string(), y_line as StdFn),
|
Box::new(LegAngY),
|
||||||
("angledLine".to_string(), angled_line as StdFn),
|
Box::new(crate::std::extrude::Extrude),
|
||||||
("angledLineOfXLength".to_string(), angled_line_of_x_length as StdFn),
|
Box::new(crate::std::extrude::GetExtrudeWallTransform),
|
||||||
("angledLineToX".to_string(), angled_line_to_x as StdFn),
|
Box::new(crate::std::segment::SegEndX),
|
||||||
("angledLineOfYLength".to_string(), angled_line_of_y_length as StdFn),
|
Box::new(crate::std::segment::SegEndY),
|
||||||
("angledLineToY".to_string(), angled_line_to_y as StdFn),
|
Box::new(crate::std::segment::LastSegX),
|
||||||
("angledLineThatIntersects".to_string(), angled_line_that_intersects as StdFn),
|
Box::new(crate::std::segment::LastSegY),
|
||||||
("startSketchAt".to_string(), start_sketch_at as StdFn),
|
Box::new(crate::std::segment::SegLen),
|
||||||
("close".to_string(), close as StdFn),
|
Box::new(crate::std::segment::SegAng),
|
||||||
])
|
Box::new(crate::std::segment::AngleToMatchLengthX),
|
||||||
|
Box::new(crate::std::segment::AngleToMatchLengthY),
|
||||||
|
Box::new(crate::std::sketch::LineTo),
|
||||||
|
Box::new(crate::std::sketch::Line),
|
||||||
|
Box::new(crate::std::sketch::XLineTo),
|
||||||
|
Box::new(crate::std::sketch::XLine),
|
||||||
|
Box::new(crate::std::sketch::YLineTo),
|
||||||
|
Box::new(crate::std::sketch::YLine),
|
||||||
|
Box::new(crate::std::sketch::AngledLineToX),
|
||||||
|
Box::new(crate::std::sketch::AngledLineToY),
|
||||||
|
Box::new(crate::std::sketch::AngledLine),
|
||||||
|
Box::new(crate::std::sketch::AngledLineOfXLength),
|
||||||
|
Box::new(crate::std::sketch::AngledLineOfYLength),
|
||||||
|
Box::new(crate::std::sketch::AngledLineThatIntersects),
|
||||||
|
Box::new(crate::std::sketch::StartSketchAt),
|
||||||
|
Box::new(crate::std::sketch::Close),
|
||||||
|
];
|
||||||
|
|
||||||
};
|
let mut fns = HashMap::new();
|
||||||
|
for internal_fn_name in &internal_fn_names {
|
||||||
|
fns.insert(
|
||||||
|
internal_fn_name.name().to_string(),
|
||||||
|
internal_fn_name.std_lib_fn(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
internal_fn_names,
|
||||||
|
fns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StdLib {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -471,34 +483,202 @@ impl<'a> Args<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the minimum of the given arguments.
|
/// Returns the minimum of the given arguments.
|
||||||
|
/// TODO fix min
|
||||||
pub fn min(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn min(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
|
let nums = args.get_number_array()?;
|
||||||
|
let result = inner_min(nums);
|
||||||
|
|
||||||
|
args.make_user_val_from_f64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum of the given arguments.
|
||||||
|
#[stdlib {
|
||||||
|
name = "min",
|
||||||
|
}]
|
||||||
|
fn inner_min(args: Vec<f64>) -> f64 {
|
||||||
let mut min = std::f64::MAX;
|
let mut min = std::f64::MAX;
|
||||||
for arg in args.get_number_array()? {
|
for arg in args.iter() {
|
||||||
if arg < min {
|
if *arg < min {
|
||||||
min = arg;
|
min = *arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args.make_user_val_from_f64(min)
|
min
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the given leg.
|
/// Returns the length of the given leg.
|
||||||
pub fn leg_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn leg_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||||
let result = (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt();
|
let result = inner_leg_length(hypotenuse, leg);
|
||||||
args.make_user_val_from_f64(result)
|
args.make_user_val_from_f64(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the length of the given leg.
|
||||||
|
#[stdlib {
|
||||||
|
name = "legLen",
|
||||||
|
}]
|
||||||
|
fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
|
||||||
|
(hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the angle of the given leg for x.
|
/// Returns the angle of the given leg for x.
|
||||||
pub fn leg_angle_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn leg_angle_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||||
let result = (leg.min(hypotenuse) / hypotenuse).acos() * 180.0 / std::f64::consts::PI;
|
let result = inner_leg_angle_x(hypotenuse, leg);
|
||||||
args.make_user_val_from_f64(result)
|
args.make_user_val_from_f64(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the angle of the given leg for x.
|
||||||
|
#[stdlib {
|
||||||
|
name = "legAngX",
|
||||||
|
}]
|
||||||
|
fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
|
||||||
|
(leg.min(hypotenuse) / hypotenuse).acos() * 180.0 / std::f64::consts::PI
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the angle of the given leg for y.
|
/// Returns the angle of the given leg for y.
|
||||||
pub fn leg_angle_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn leg_angle_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||||
let result = (leg.min(hypotenuse) / hypotenuse).asin() * 180.0 / std::f64::consts::PI;
|
let result = inner_leg_angle_y(hypotenuse, leg);
|
||||||
args.make_user_val_from_f64(result)
|
args.make_user_val_from_f64(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the angle of the given leg for y.
|
||||||
|
#[stdlib {
|
||||||
|
name = "legAngY",
|
||||||
|
}]
|
||||||
|
fn inner_leg_angle_y(hypotenuse: f64, leg: f64) -> f64 {
|
||||||
|
(leg.min(hypotenuse) / hypotenuse).asin() * 180.0 / std::f64::consts::PI
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The primitive types that can be used in a KCL file.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[display(style = "lowercase")]
|
||||||
|
pub enum Primitive {
|
||||||
|
/// A boolean value.
|
||||||
|
Bool,
|
||||||
|
/// A number value.
|
||||||
|
Number,
|
||||||
|
/// A string value.
|
||||||
|
String,
|
||||||
|
/// A uuid value.
|
||||||
|
Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::std::StdLib;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_stdlib_markdown_docs() {
|
||||||
|
let stdlib = StdLib::new();
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
buf.push_str("<!--- DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED. -->\n\n");
|
||||||
|
|
||||||
|
buf.push_str("# KCL Standard Library\n\n");
|
||||||
|
|
||||||
|
// Generate a table of contents.
|
||||||
|
buf.push_str("## Table of Contents\n\n");
|
||||||
|
|
||||||
|
buf.push_str("* [Functions](#functions)\n");
|
||||||
|
|
||||||
|
for internal_fn in &stdlib.internal_fn_names {
|
||||||
|
if internal_fn.unpublished() || internal_fn.deprecated() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(&format!(
|
||||||
|
"\t* [`{}`](#{})\n",
|
||||||
|
internal_fn.name(),
|
||||||
|
internal_fn.name()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str("\n\n");
|
||||||
|
|
||||||
|
buf.push_str("## Functions\n\n");
|
||||||
|
|
||||||
|
for internal_fn in &stdlib.internal_fn_names {
|
||||||
|
if internal_fn.unpublished() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fn_docs = String::new();
|
||||||
|
|
||||||
|
if internal_fn.deprecated() {
|
||||||
|
fn_docs.push_str(&format!("### {} DEPRECATED\n\n", internal_fn.name()));
|
||||||
|
} else {
|
||||||
|
fn_docs.push_str(&format!("### {}\n\n", internal_fn.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_docs.push_str(&format!("{}\n\n", internal_fn.summary()));
|
||||||
|
fn_docs.push_str(&format!("{}\n\n", internal_fn.description()));
|
||||||
|
|
||||||
|
fn_docs.push_str("```\n");
|
||||||
|
fn_docs.push_str(&format!("{}(", internal_fn.name()));
|
||||||
|
for (i, arg) in internal_fn.args().iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
fn_docs.push_str(", ");
|
||||||
|
}
|
||||||
|
fn_docs.push_str(&format!("{}: {}", arg.name, arg.type_));
|
||||||
|
}
|
||||||
|
fn_docs.push_str(") -> ");
|
||||||
|
fn_docs.push_str(&internal_fn.return_value().type_);
|
||||||
|
fn_docs.push_str("\n```\n\n");
|
||||||
|
|
||||||
|
fn_docs.push_str("#### Arguments\n\n");
|
||||||
|
for arg in internal_fn.args() {
|
||||||
|
let (format, should_be_indented) = arg.get_type_string().unwrap();
|
||||||
|
if let Some(description) = arg.description() {
|
||||||
|
fn_docs.push_str(&format!(
|
||||||
|
"* `{}`: `{}` - {}\n",
|
||||||
|
arg.name, arg.type_, description
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
fn_docs.push_str(&format!("* `{}`: `{}`\n", arg.name, arg.type_));
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_be_indented {
|
||||||
|
fn_docs.push_str(&format!("```\n{}\n```\n", format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_docs.push_str("\n#### Returns\n\n");
|
||||||
|
let return_type = internal_fn.return_value();
|
||||||
|
if let Some(description) = return_type.description() {
|
||||||
|
fn_docs.push_str(&format!("* `{}` - {}\n", return_type.type_, description));
|
||||||
|
} else {
|
||||||
|
fn_docs.push_str(&format!("* `{}`\n", return_type.type_));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (format, should_be_indented) = return_type.get_type_string().unwrap();
|
||||||
|
if should_be_indented {
|
||||||
|
fn_docs.push_str(&format!("```\n{}\n```\n", format));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_docs.push_str("\n\n\n");
|
||||||
|
|
||||||
|
buf.push_str(&fn_docs);
|
||||||
|
}
|
||||||
|
|
||||||
|
expectorate::assert_contents("../../docs/kcl.md", &buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_stdlib_json_schema() {
|
||||||
|
let stdlib = StdLib::new();
|
||||||
|
|
||||||
|
let mut json_data = vec![];
|
||||||
|
|
||||||
|
for internal_fn in &stdlib.internal_fn_names {
|
||||||
|
json_data.push(internal_fn.to_json().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
expectorate::assert_contents(
|
||||||
|
"../../docs/kcl.json",
|
||||||
|
&serde_json::to_string_pretty(&json_data).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,17 +2,33 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::MemoryItem,
|
executor::{MemoryItem, SketchGroup},
|
||||||
std::{utils::get_angle, Args},
|
std::{utils::get_angle, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use derive_docs::stdlib;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
/// Returns the segment end of x.
|
/// Returns the segment end of x.
|
||||||
pub fn segment_end_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn segment_end_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
||||||
|
let result = inner_segment_end_x(&segment_name, sketch_group, args)?;
|
||||||
|
|
||||||
|
args.make_user_val_from_f64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the segment end of x.
|
||||||
|
#[stdlib {
|
||||||
|
name = "segEndX",
|
||||||
|
}]
|
||||||
|
fn inner_segment_end_x(
|
||||||
|
segment_name: &str,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<f64, KclError> {
|
||||||
let line = sketch_group
|
let line = sketch_group
|
||||||
.get_base_by_name_or_start(&segment_name)
|
.get_base_by_name_or_start(segment_name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
message: format!(
|
message: format!(
|
||||||
@ -23,14 +39,28 @@ pub fn segment_end_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
args.make_user_val_from_f64(line.to[0])
|
Ok(line.to[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the segment end of y.
|
/// Returns the segment end of y.
|
||||||
pub fn segment_end_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn segment_end_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
||||||
|
let result = inner_segment_end_y(&segment_name, sketch_group, args)?;
|
||||||
|
|
||||||
|
args.make_user_val_from_f64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the segment end of y.
|
||||||
|
#[stdlib {
|
||||||
|
name = "segEndY",
|
||||||
|
}]
|
||||||
|
fn inner_segment_end_y(
|
||||||
|
segment_name: &str,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<f64, KclError> {
|
||||||
let line = sketch_group
|
let line = sketch_group
|
||||||
.get_base_by_name_or_start(&segment_name)
|
.get_base_by_name_or_start(segment_name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
message: format!(
|
message: format!(
|
||||||
@ -41,12 +71,22 @@ pub fn segment_end_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
args.make_user_val_from_f64(line.to[1])
|
Ok(line.to[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the last segment of x.
|
/// Returns the last segment of x.
|
||||||
pub fn last_segment_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn last_segment_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let sketch_group = args.get_sketch_group()?;
|
let sketch_group = args.get_sketch_group()?;
|
||||||
|
let result = inner_last_segment_x(sketch_group, args)?;
|
||||||
|
|
||||||
|
args.make_user_val_from_f64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the last segment of x.
|
||||||
|
#[stdlib {
|
||||||
|
name = "lastSegX",
|
||||||
|
}]
|
||||||
|
fn inner_last_segment_x(sketch_group: SketchGroup, args: &mut Args) -> Result<f64, KclError> {
|
||||||
let last_line = sketch_group
|
let last_line = sketch_group
|
||||||
.value
|
.value
|
||||||
.last()
|
.last()
|
||||||
@ -61,12 +101,22 @@ pub fn last_segment_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
})?
|
})?
|
||||||
.get_base();
|
.get_base();
|
||||||
|
|
||||||
args.make_user_val_from_f64(last_line.to[0])
|
Ok(last_line.to[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the last segment of y.
|
/// Returns the last segment of y.
|
||||||
pub fn last_segment_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn last_segment_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let sketch_group = args.get_sketch_group()?;
|
let sketch_group = args.get_sketch_group()?;
|
||||||
|
let result = inner_last_segment_y(sketch_group, args)?;
|
||||||
|
|
||||||
|
args.make_user_val_from_f64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the last segment of y.
|
||||||
|
#[stdlib {
|
||||||
|
name = "lastSegY",
|
||||||
|
}]
|
||||||
|
fn inner_last_segment_y(sketch_group: SketchGroup, args: &mut Args) -> Result<f64, KclError> {
|
||||||
let last_line = sketch_group
|
let last_line = sketch_group
|
||||||
.value
|
.value
|
||||||
.last()
|
.last()
|
||||||
@ -81,63 +131,100 @@ pub fn last_segment_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
})?
|
})?
|
||||||
.get_base();
|
.get_base();
|
||||||
|
|
||||||
args.make_user_val_from_f64(last_line.to[1])
|
Ok(last_line.to[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the segment.
|
/// Returns the length of the segment.
|
||||||
pub fn segment_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn segment_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
||||||
let path = sketch_group
|
let result = inner_segment_length(&segment_name, sketch_group, args)?;
|
||||||
.get_path_by_name(&segment_name)
|
args.make_user_val_from_f64(result)
|
||||||
.ok_or_else(|| {
|
}
|
||||||
KclError::Type(KclErrorDetails {
|
|
||||||
message: format!(
|
/// Returns the length of the segment.
|
||||||
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
#[stdlib {
|
||||||
segment_name
|
name = "segLen",
|
||||||
),
|
}]
|
||||||
source_ranges: vec![args.source_range],
|
fn inner_segment_length(
|
||||||
})
|
segment_name: &str,
|
||||||
})?;
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<f64, KclError> {
|
||||||
|
let path = sketch_group.get_path_by_name(segment_name).ok_or_else(|| {
|
||||||
|
KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
||||||
|
segment_name
|
||||||
|
),
|
||||||
|
source_ranges: vec![args.source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
let line = path.get_base();
|
let line = path.get_base();
|
||||||
|
|
||||||
let result = ((line.from[1] - line.to[1]).powi(2) + (line.from[0] - line.to[0]).powi(2)).sqrt();
|
let result = ((line.from[1] - line.to[1]).powi(2) + (line.from[0] - line.to[0]).powi(2)).sqrt();
|
||||||
args.make_user_val_from_f64(result)
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the angle of the segment.
|
/// Returns the angle of the segment.
|
||||||
pub fn segment_angle(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn segment_angle(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
||||||
let path = sketch_group
|
|
||||||
.get_path_by_name(&segment_name)
|
let result = inner_segment_angle(&segment_name, sketch_group, args)?;
|
||||||
.ok_or_else(|| {
|
args.make_user_val_from_f64(result)
|
||||||
KclError::Type(KclErrorDetails {
|
}
|
||||||
message: format!(
|
|
||||||
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
/// Returns the angle of the segment.
|
||||||
segment_name
|
#[stdlib {
|
||||||
),
|
name = "segAng",
|
||||||
source_ranges: vec![args.source_range],
|
}]
|
||||||
})
|
fn inner_segment_angle(
|
||||||
})?;
|
segment_name: &str,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<f64, KclError> {
|
||||||
|
let path = sketch_group.get_path_by_name(segment_name).ok_or_else(|| {
|
||||||
|
KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
||||||
|
segment_name
|
||||||
|
),
|
||||||
|
source_ranges: vec![args.source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
let line = path.get_base();
|
let line = path.get_base();
|
||||||
|
|
||||||
let result = get_angle(&line.from, &line.to);
|
let result = get_angle(&line.from, &line.to);
|
||||||
args.make_user_val_from_f64(result)
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the angle to match the given length for x.
|
/// Returns the angle to match the given length for x.
|
||||||
pub fn angle_to_match_length_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angle_to_match_length_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, to, sketch_group) = args.get_segment_name_to_number_sketch_group()?;
|
let (segment_name, to, sketch_group) = args.get_segment_name_to_number_sketch_group()?;
|
||||||
let path = sketch_group
|
let result = inner_angle_to_match_length_x(&segment_name, to, sketch_group, args)?;
|
||||||
.get_path_by_name(&segment_name)
|
args.make_user_val_from_f64(result)
|
||||||
.ok_or_else(|| {
|
}
|
||||||
KclError::Type(KclErrorDetails {
|
|
||||||
message: format!(
|
/// Returns the angle to match the given length for x.
|
||||||
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
#[stdlib {
|
||||||
segment_name
|
name = "angleToMatchLengthX",
|
||||||
),
|
}]
|
||||||
source_ranges: vec![args.source_range],
|
fn inner_angle_to_match_length_x(
|
||||||
})
|
segment_name: &str,
|
||||||
})?;
|
to: f64,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<f64, KclError> {
|
||||||
|
let path = sketch_group.get_path_by_name(segment_name).ok_or_else(|| {
|
||||||
|
KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
||||||
|
segment_name
|
||||||
|
),
|
||||||
|
source_ranges: vec![args.source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
let line = path.get_base();
|
let line = path.get_base();
|
||||||
|
|
||||||
let length = ((line.from[1] - line.to[1]).powi(2) + (line.from[0] - line.to[0]).powi(2)).sqrt();
|
let length = ((line.from[1] - line.to[1]).powi(2) + (line.from[0] - line.to[0]).powi(2)).sqrt();
|
||||||
@ -161,26 +248,38 @@ pub fn angle_to_match_length_x(args: &mut Args) -> Result<MemoryItem, KclError>
|
|||||||
let angle_r = diff / length.acos();
|
let angle_r = diff / length.acos();
|
||||||
|
|
||||||
if diff > length {
|
if diff > length {
|
||||||
args.make_user_val_from_f64(0.0)
|
Ok(0.0)
|
||||||
} else {
|
} else {
|
||||||
args.make_user_val_from_f64(angle_r * 180.0 / std::f64::consts::PI)
|
Ok(angle_r * 180.0 / std::f64::consts::PI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the angle to match the given length for y.
|
/// Returns the angle to match the given length for y.
|
||||||
pub fn angle_to_match_length_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angle_to_match_length_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, to, sketch_group) = args.get_segment_name_to_number_sketch_group()?;
|
let (segment_name, to, sketch_group) = args.get_segment_name_to_number_sketch_group()?;
|
||||||
let path = sketch_group
|
let result = inner_angle_to_match_length_y(&segment_name, to, sketch_group, args)?;
|
||||||
.get_path_by_name(&segment_name)
|
args.make_user_val_from_f64(result)
|
||||||
.ok_or_else(|| {
|
}
|
||||||
KclError::Type(KclErrorDetails {
|
|
||||||
message: format!(
|
/// Returns the angle to match the given length for y.
|
||||||
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
#[stdlib {
|
||||||
segment_name
|
name = "angleToMatchLengthY",
|
||||||
),
|
}]
|
||||||
source_ranges: vec![args.source_range],
|
fn inner_angle_to_match_length_y(
|
||||||
})
|
segment_name: &str,
|
||||||
})?;
|
to: f64,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<f64, KclError> {
|
||||||
|
let path = sketch_group.get_path_by_name(segment_name).ok_or_else(|| {
|
||||||
|
KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected a segment name that exists in the given SketchGroup, found `{}`",
|
||||||
|
segment_name
|
||||||
|
),
|
||||||
|
source_ranges: vec![args.source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
let line = path.get_base();
|
let line = path.get_base();
|
||||||
|
|
||||||
let length = ((line.from[1] - line.to[1]).powi(2) + (line.from[0] - line.to[0]).powi(2)).sqrt();
|
let length = ((line.from[1] - line.to[1]).powi(2) + (line.from[0] - line.to[0]).powi(2)).sqrt();
|
||||||
@ -204,8 +303,8 @@ pub fn angle_to_match_length_y(args: &mut Args) -> Result<MemoryItem, KclError>
|
|||||||
let angle_r = diff / length.asin();
|
let angle_r = diff / length.asin();
|
||||||
|
|
||||||
if diff > length {
|
if diff > length {
|
||||||
args.make_user_val_from_f64(0.0)
|
Ok(0.0)
|
||||||
} else {
|
} else {
|
||||||
args.make_user_val_from_f64(angle_r * 180.0 / std::f64::consts::PI)
|
Ok(angle_r * 180.0 / std::f64::consts::PI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
//! Functions related to sketching.
|
//! Functions related to sketching.
|
||||||
|
|
||||||
|
use derive_docs::stdlib;
|
||||||
use kittycad::types::{ModelingCmd, Point3D};
|
use kittycad::types::{ModelingCmd, Point3D};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -14,11 +16,19 @@ use crate::{
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Data to draw a line to a point.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum LineToData {
|
pub enum LineToData {
|
||||||
PointWithTag { to: [f64; 2], tag: String },
|
/// A point with a tag.
|
||||||
|
PointWithTag {
|
||||||
|
/// The to point.
|
||||||
|
to: [f64; 2],
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// A point.
|
||||||
Point([f64; 2]),
|
Point([f64; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +41,9 @@ pub fn line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a line to a point.
|
/// Draw a line to a point.
|
||||||
|
#[stdlib {
|
||||||
|
name = "lineTo",
|
||||||
|
}]
|
||||||
fn inner_line_to(
|
fn inner_line_to(
|
||||||
data: LineToData,
|
data: LineToData,
|
||||||
sketch_group: SketchGroup,
|
sketch_group: SketchGroup,
|
||||||
@ -65,17 +78,39 @@ fn inner_line_to(
|
|||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Data to draw a line to a point on an axis.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum AxisLineToData {
|
pub enum AxisLineToData {
|
||||||
PointWithTag { to: f64, tag: String },
|
/// A point with a tag.
|
||||||
|
PointWithTag {
|
||||||
|
/// The to point.
|
||||||
|
to: f64,
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// A point.
|
||||||
Point(f64),
|
Point(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a line to a point on the x-axis.
|
/// Draw a line to a point on the x-axis.
|
||||||
pub fn x_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn x_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AxisLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AxisLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_x_line_to(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw a line to a point on the x-axis.
|
||||||
|
#[stdlib {
|
||||||
|
name = "xLineTo",
|
||||||
|
}]
|
||||||
|
fn inner_x_line_to(
|
||||||
|
data: AxisLineToData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
let line_to_data = match data {
|
let line_to_data = match data {
|
||||||
@ -87,12 +122,27 @@ pub fn x_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
|
||||||
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a line to a point on the y-axis.
|
/// Draw a line to a point on the y-axis.
|
||||||
pub fn y_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn y_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AxisLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AxisLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_y_line_to(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw a line to a point on the y-axis.
|
||||||
|
#[stdlib {
|
||||||
|
name = "yLineTo",
|
||||||
|
}]
|
||||||
|
fn inner_y_line_to(
|
||||||
|
data: AxisLineToData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
let line_to_data = match data {
|
let line_to_data = match data {
|
||||||
@ -104,23 +154,35 @@ pub fn y_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Data to draw a line.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum LineData {
|
pub enum LineData {
|
||||||
PointWithTag { to: PointOrDefault, tag: String },
|
/// A point with a tag.
|
||||||
|
PointWithTag {
|
||||||
|
/// The to point.
|
||||||
|
to: PointOrDefault,
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// A point.
|
||||||
Point([f64; 2]),
|
Point([f64; 2]),
|
||||||
|
/// A string like `default`.
|
||||||
Default(String),
|
Default(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// A point or a default value.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum PointOrDefault {
|
pub enum PointOrDefault {
|
||||||
|
/// A point.
|
||||||
Point([f64; 2]),
|
Point([f64; 2]),
|
||||||
|
/// A string like `default`.
|
||||||
Default(String),
|
Default(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +203,10 @@ pub fn line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw a line.
|
||||||
|
#[stdlib {
|
||||||
|
name = "line",
|
||||||
|
}]
|
||||||
fn inner_line(
|
fn inner_line(
|
||||||
data: LineData,
|
data: LineData,
|
||||||
sketch_group: SketchGroup,
|
sketch_group: SketchGroup,
|
||||||
@ -195,55 +261,95 @@ fn inner_line(
|
|||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Data to draw a line on an axis.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum AxisLineData {
|
pub enum AxisLineData {
|
||||||
PointWithTag { length: f64, tag: String },
|
/// The length with a tag.
|
||||||
Point(f64),
|
LengthWithTag {
|
||||||
|
/// The length of the line.
|
||||||
|
length: f64,
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// The length.
|
||||||
|
Length(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a line on the x-axis.
|
/// Draw a line on the x-axis.
|
||||||
pub fn x_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn x_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AxisLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AxisLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_x_line(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw a line on the x-axis.
|
||||||
|
#[stdlib {
|
||||||
|
name = "xLine",
|
||||||
|
}]
|
||||||
|
fn inner_x_line(
|
||||||
|
data: AxisLineData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let line_data = match data {
|
let line_data = match data {
|
||||||
AxisLineData::PointWithTag { length, tag } => LineData::PointWithTag {
|
AxisLineData::LengthWithTag { length, tag } => LineData::PointWithTag {
|
||||||
to: PointOrDefault::Point([length, 0.0]),
|
to: PointOrDefault::Point([length, 0.0]),
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
AxisLineData::Point(length) => LineData::Point([length, 0.0]),
|
AxisLineData::Length(length) => LineData::Point([length, 0.0]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_sketch_group = inner_line(line_data, sketch_group, args)?;
|
let new_sketch_group = inner_line(line_data, sketch_group, args)?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a line on the y-axis.
|
/// Draw a line on the y-axis.
|
||||||
pub fn y_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn y_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AxisLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AxisLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
let line_data = match data {
|
let new_sketch_group = inner_y_line(data, sketch_group, args)?;
|
||||||
AxisLineData::PointWithTag { length, tag } => LineData::PointWithTag {
|
|
||||||
to: PointOrDefault::Point([0.0, length]),
|
|
||||||
tag,
|
|
||||||
},
|
|
||||||
AxisLineData::Point(length) => LineData::Point([0.0, length]),
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_sketch_group = inner_line(line_data, sketch_group, args)?;
|
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Draw a line on the y-axis.
|
||||||
|
#[stdlib {
|
||||||
|
name = "yLine",
|
||||||
|
}]
|
||||||
|
fn inner_y_line(
|
||||||
|
data: AxisLineData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
|
let line_data = match data {
|
||||||
|
AxisLineData::LengthWithTag { length, tag } => LineData::PointWithTag {
|
||||||
|
to: PointOrDefault::Point([0.0, length]),
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
AxisLineData::Length(length) => LineData::Point([0.0, length]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_sketch_group = inner_line(line_data, sketch_group, args)?;
|
||||||
|
Ok(new_sketch_group)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data to draw an angled line.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum AngledLineData {
|
pub enum AngledLineData {
|
||||||
|
/// An angle and length with a tag.
|
||||||
AngleWithTag {
|
AngleWithTag {
|
||||||
|
/// The angle of the line.
|
||||||
angle: f64,
|
angle: f64,
|
||||||
|
/// The length of the line.
|
||||||
length: f64,
|
length: f64,
|
||||||
|
/// The tag.
|
||||||
tag: String,
|
tag: String,
|
||||||
},
|
},
|
||||||
|
/// An angle and length.
|
||||||
AngleAndLength([f64; 2]),
|
AngleAndLength([f64; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +357,19 @@ pub enum AngledLineData {
|
|||||||
pub fn angled_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angled_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_angled_line(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an angled line.
|
||||||
|
#[stdlib {
|
||||||
|
name = "angledLine",
|
||||||
|
}]
|
||||||
|
fn inner_angled_line(
|
||||||
|
data: AngledLineData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
let (angle, length) = match &data {
|
let (angle, length) = match &data {
|
||||||
AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
|
AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
|
||||||
@ -283,13 +402,26 @@ pub fn angled_line(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
|
|
||||||
let mut new_sketch_group = sketch_group.clone();
|
let mut new_sketch_group = sketch_group.clone();
|
||||||
new_sketch_group.value.push(current_path);
|
new_sketch_group.value.push(current_path);
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw an angled line of a given x length.
|
/// Draw an angled line of a given x length.
|
||||||
pub fn angled_line_of_x_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angled_line_of_x_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_angled_line_of_x_length(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an angled line of a given x length.
|
||||||
|
#[stdlib {
|
||||||
|
name = "angledLineOfXLength",
|
||||||
|
}]
|
||||||
|
fn inner_angled_line_of_x_length(
|
||||||
|
data: AngledLineData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let (angle, length) = match &data {
|
let (angle, length) = match &data {
|
||||||
AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
|
AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
|
||||||
AngledLineData::AngleAndLength(angle_and_length) => {
|
AngledLineData::AngleAndLength(angle_and_length) => {
|
||||||
@ -312,14 +444,24 @@ pub fn angled_line_of_x_length(args: &mut Args) -> Result<MemoryItem, KclError>
|
|||||||
args,
|
args,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Data to draw an angled line to a point.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum AngledLineToData {
|
pub enum AngledLineToData {
|
||||||
AngleWithTag { angle: f64, to: f64, tag: String },
|
/// An angle and point with a tag.
|
||||||
|
AngleWithTag {
|
||||||
|
/// The angle of the line.
|
||||||
|
angle: f64,
|
||||||
|
/// The point to draw to.
|
||||||
|
to: f64,
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// An angle and point to draw to.
|
||||||
AngleAndPoint([f64; 2]),
|
AngleAndPoint([f64; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +469,19 @@ pub enum AngledLineToData {
|
|||||||
pub fn angled_line_to_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angled_line_to_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_angled_line_to_x(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an angled line to a given x coordinate.
|
||||||
|
#[stdlib {
|
||||||
|
name = "angledLineToX",
|
||||||
|
}]
|
||||||
|
fn inner_angled_line_to_x(
|
||||||
|
data: AngledLineToData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
let (angle, x_to) = match &data {
|
let (angle, x_to) = match &data {
|
||||||
AngledLineToData::AngleWithTag { angle, to, .. } => (*angle, *to),
|
AngledLineToData::AngleWithTag { angle, to, .. } => (*angle, *to),
|
||||||
@ -349,13 +504,27 @@ pub fn angled_line_to_x(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
sketch_group,
|
sketch_group,
|
||||||
args,
|
args,
|
||||||
)?;
|
)?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw an angled line of a given y length.
|
/// Draw an angled line of a given y length.
|
||||||
pub fn angled_line_of_y_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angled_line_of_y_length(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_angled_line_of_y_length(data, sketch_group, args)?;
|
||||||
|
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an angled line of a given y length.
|
||||||
|
#[stdlib {
|
||||||
|
name = "angledLineOfYLength",
|
||||||
|
}]
|
||||||
|
fn inner_angled_line_of_y_length(
|
||||||
|
data: AngledLineData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let (angle, length) = match &data {
|
let (angle, length) = match &data {
|
||||||
AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
|
AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
|
||||||
AngledLineData::AngleAndLength(angle_and_length) => {
|
AngledLineData::AngleAndLength(angle_and_length) => {
|
||||||
@ -378,13 +547,26 @@ pub fn angled_line_of_y_length(args: &mut Args) -> Result<MemoryItem, KclError>
|
|||||||
args,
|
args,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw an angled line to a given y coordinate.
|
/// Draw an angled line to a given y coordinate.
|
||||||
pub fn angled_line_to_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angled_line_to_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineToData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_angled_line_to_y(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an angled line to a given y coordinate.
|
||||||
|
#[stdlib {
|
||||||
|
name = "angledLineToY",
|
||||||
|
}]
|
||||||
|
fn inner_angled_line_to_y(
|
||||||
|
data: AngledLineToData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
let (angle, y_to) = match &data {
|
let (angle, y_to) = match &data {
|
||||||
AngledLineToData::AngleWithTag { angle, to, .. } => (*angle, *to),
|
AngledLineToData::AngleWithTag { angle, to, .. } => (*angle, *to),
|
||||||
@ -407,10 +589,11 @@ pub fn angled_line_to_y(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
sketch_group,
|
sketch_group,
|
||||||
args,
|
args,
|
||||||
)?;
|
)?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
/// Data for drawing an angled line that intersects with a given line.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
// TODO: make sure the docs on the args below are correct.
|
// TODO: make sure the docs on the args below are correct.
|
||||||
@ -429,6 +612,19 @@ pub struct AngeledLineThatIntersectsData {
|
|||||||
pub fn angled_line_that_intersects(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn angled_line_that_intersects(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngeledLineThatIntersectsData, SketchGroup) =
|
let (data, sketch_group): (AngeledLineThatIntersectsData, SketchGroup) =
|
||||||
args.get_data_and_sketch_group()?;
|
args.get_data_and_sketch_group()?;
|
||||||
|
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an angled line that intersects with a given line.
|
||||||
|
#[stdlib {
|
||||||
|
name = "angledLineThatIntersects",
|
||||||
|
}]
|
||||||
|
fn inner_angled_line_that_intersects(
|
||||||
|
data: AngeledLineThatIntersectsData,
|
||||||
|
sketch_group: SketchGroup,
|
||||||
|
args: &mut Args,
|
||||||
|
) -> Result<SketchGroup, KclError> {
|
||||||
let intersect_path = sketch_group
|
let intersect_path = sketch_group
|
||||||
.get_path_by_name(&data.intersect_tag)
|
.get_path_by_name(&data.intersect_tag)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
@ -457,13 +653,22 @@ pub fn angled_line_that_intersects(args: &mut Args) -> Result<MemoryItem, KclErr
|
|||||||
};
|
};
|
||||||
|
|
||||||
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a sketch at a given point.
|
/// Start a sketch at a given point.
|
||||||
pub fn start_sketch_at(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn start_sketch_at(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let data: LineData = args.get_data()?;
|
let data: LineData = args.get_data()?;
|
||||||
|
|
||||||
|
let sketch_group = inner_start_sketch_at(data, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start a sketch at a given point.
|
||||||
|
#[stdlib {
|
||||||
|
name = "startSketchAt",
|
||||||
|
}]
|
||||||
|
fn inner_start_sketch_at(data: LineData, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
let default = [0.0, 0.0];
|
let default = [0.0, 0.0];
|
||||||
let to = match &data {
|
let to = match &data {
|
||||||
LineData::PointWithTag { to, .. } => to.get_point_with_default(default),
|
LineData::PointWithTag { to, .. } => to.get_point_with_default(default),
|
||||||
@ -509,12 +714,23 @@ pub fn start_sketch_at(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
start: current_path,
|
start: current_path,
|
||||||
meta: vec![args.source_range.into()],
|
meta: vec![args.source_range.into()],
|
||||||
};
|
};
|
||||||
Ok(MemoryItem::SketchGroup(sketch_group))
|
Ok(sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the current sketch.
|
/// Close the current sketch.
|
||||||
pub fn close(args: &mut Args) -> Result<MemoryItem, KclError> {
|
pub fn close(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
let sketch_group = args.get_sketch_group()?;
|
let sketch_group = args.get_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_close(sketch_group, args)?;
|
||||||
|
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the current sketch.
|
||||||
|
#[stdlib {
|
||||||
|
name = "close",
|
||||||
|
}]
|
||||||
|
fn inner_close(sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
let to: Point2d = sketch_group.start.from.into();
|
let to: Point2d = sketch_group.start.from.into();
|
||||||
|
|
||||||
@ -541,7 +757,7 @@ pub fn close(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Reference in New Issue
Block a user