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"
 | 
			
		||||
backtrace = "0.3"
 | 
			
		||||
bincode = "1.3.3"
 | 
			
		||||
derive-docs = { path = "derive-docs" }
 | 
			
		||||
futures = { version = "0.3.28", optional = true }
 | 
			
		||||
gloo-file = { version = "0.3.0", optional = true }
 | 
			
		||||
gloo-utils = "0.2.0"
 | 
			
		||||
@ -19,7 +20,9 @@ httparse = { version = "1.8.0", optional = true }
 | 
			
		||||
js-sys = { version = "0.3.64", optional = true }
 | 
			
		||||
kittycad = { version = "0.2.15", default-features = false, features = ["js"] }
 | 
			
		||||
lazy_static = "1.4.0"
 | 
			
		||||
parse-display = "0.8.2"
 | 
			
		||||
regex = "1.7.1"
 | 
			
		||||
schemars = { version = "0.8", features = ["url", "uuid1"] }
 | 
			
		||||
serde = {version = "1.0.152", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.93"
 | 
			
		||||
thiserror = "1.0.47"
 | 
			
		||||
@ -35,6 +38,7 @@ panic = "abort"
 | 
			
		||||
debug = true
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
expectorate = "1.0.7"
 | 
			
		||||
pretty_assertions = "1.4.0"
 | 
			
		||||
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"]
 | 
			
		||||
web = ["dep:gloo-file", "dep:js-sys"]
 | 
			
		||||
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 schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use serde_json::Map;
 | 
			
		||||
 | 
			
		||||
@ -11,7 +12,7 @@ use crate::{
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct Program {
 | 
			
		||||
@ -21,7 +22,7 @@ pub struct Program {
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum BodyItem {
 | 
			
		||||
@ -30,7 +31,7 @@ pub enum BodyItem {
 | 
			
		||||
    ReturnStatement(ReturnStatement),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum BinaryPart {
 | 
			
		||||
@ -178,6 +179,7 @@ impl BinaryPart {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        pipe_info.is_in_pipe = false;
 | 
			
		||||
@ -188,10 +190,10 @@ impl BinaryPart {
 | 
			
		||||
                Ok(value.clone())
 | 
			
		||||
            }
 | 
			
		||||
            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) => {
 | 
			
		||||
                call_expression.execute(memory, pipe_info, engine)
 | 
			
		||||
                call_expression.execute(memory, pipe_info, stdlib, engine)
 | 
			
		||||
            }
 | 
			
		||||
            BinaryPart::UnaryExpression(unary_expression) => {
 | 
			
		||||
                // 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct NoneCodeNode {
 | 
			
		||||
@ -216,7 +218,7 @@ pub struct NoneCodeNode {
 | 
			
		||||
    pub value: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct ExpressionStatement {
 | 
			
		||||
@ -261,7 +263,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct CallExpression {
 | 
			
		||||
@ -279,6 +281,7 @@ impl CallExpression {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        let fn_name = self.callee.name.clone();
 | 
			
		||||
@ -293,20 +296,20 @@ impl CallExpression {
 | 
			
		||||
                    value.clone()
 | 
			
		||||
                }
 | 
			
		||||
                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) => {
 | 
			
		||||
                    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) => {
 | 
			
		||||
                    unary_expression.get_result(memory, pipe_info, engine)?
 | 
			
		||||
                    unary_expression.get_result(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::ObjectExpression(object_expression) => {
 | 
			
		||||
                    object_expression.execute(memory, pipe_info, engine)?
 | 
			
		||||
                    object_expression.execute(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::ArrayExpression(array_expression) => {
 | 
			
		||||
                    array_expression.execute(memory, pipe_info, engine)?
 | 
			
		||||
                    array_expression.execute(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::PipeExpression(pipe_expression) => {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
@ -353,7 +356,7 @@ impl CallExpression {
 | 
			
		||||
            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.
 | 
			
		||||
            let mut args = crate::std::Args::new(fn_args, self.into(), engine);
 | 
			
		||||
            let result = func(&mut args)?;
 | 
			
		||||
@ -365,6 +368,7 @@ impl CallExpression {
 | 
			
		||||
                    &pipe_info.body.clone(),
 | 
			
		||||
                    pipe_info,
 | 
			
		||||
                    self.into(),
 | 
			
		||||
                    stdlib,
 | 
			
		||||
                    engine,
 | 
			
		||||
                )
 | 
			
		||||
            } else {
 | 
			
		||||
@ -391,6 +395,7 @@ impl CallExpression {
 | 
			
		||||
                    &pipe_info.body.clone(),
 | 
			
		||||
                    pipe_info,
 | 
			
		||||
                    self.into(),
 | 
			
		||||
                    stdlib,
 | 
			
		||||
                    engine,
 | 
			
		||||
                )
 | 
			
		||||
            } 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct VariableDeclaration {
 | 
			
		||||
@ -412,7 +417,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct VariableDeclarator {
 | 
			
		||||
@ -424,7 +429,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct Identifier {
 | 
			
		||||
@ -469,7 +474,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct PipeSubstitution {
 | 
			
		||||
@ -479,7 +484,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct ArrayExpression {
 | 
			
		||||
@ -495,6 +500,7 @@ impl ArrayExpression {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        let mut results = Vec::with_capacity(self.elements.len());
 | 
			
		||||
@ -507,23 +513,23 @@ impl ArrayExpression {
 | 
			
		||||
                    value.clone()
 | 
			
		||||
                }
 | 
			
		||||
                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) => {
 | 
			
		||||
                    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) => {
 | 
			
		||||
                    unary_expression.get_result(memory, pipe_info, engine)?
 | 
			
		||||
                    unary_expression.get_result(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::ObjectExpression(object_expression) => {
 | 
			
		||||
                    object_expression.execute(memory, pipe_info, engine)?
 | 
			
		||||
                    object_expression.execute(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::ArrayExpression(array_expression) => {
 | 
			
		||||
                    array_expression.execute(memory, pipe_info, engine)?
 | 
			
		||||
                    array_expression.execute(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                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) => {
 | 
			
		||||
                    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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct ObjectExpression {
 | 
			
		||||
@ -581,6 +587,7 @@ impl ObjectExpression {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        let mut object = Map::new();
 | 
			
		||||
@ -592,23 +599,23 @@ impl ObjectExpression {
 | 
			
		||||
                    value.clone()
 | 
			
		||||
                }
 | 
			
		||||
                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) => {
 | 
			
		||||
                    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) => {
 | 
			
		||||
                    unary_expression.get_result(memory, pipe_info, engine)?
 | 
			
		||||
                    unary_expression.get_result(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::ObjectExpression(object_expression) => {
 | 
			
		||||
                    object_expression.execute(memory, pipe_info, engine)?
 | 
			
		||||
                    object_expression.execute(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                Value::ArrayExpression(array_expression) => {
 | 
			
		||||
                    array_expression.execute(memory, pipe_info, engine)?
 | 
			
		||||
                    array_expression.execute(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                }
 | 
			
		||||
                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) => {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
@ -653,7 +660,7 @@ impl 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct ObjectProperty {
 | 
			
		||||
@ -665,7 +672,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum MemberObject {
 | 
			
		||||
@ -673,7 +680,7 @@ pub enum MemberObject {
 | 
			
		||||
    Identifier(Box<Identifier>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum LiteralIdentifier {
 | 
			
		||||
@ -681,7 +688,7 @@ pub enum LiteralIdentifier {
 | 
			
		||||
    Literal(Box<Literal>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
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)]
 | 
			
		||||
pub struct ObjectKeyInfo {
 | 
			
		||||
    pub key: LiteralIdentifier,
 | 
			
		||||
@ -755,7 +762,7 @@ pub struct ObjectKeyInfo {
 | 
			
		||||
    pub computed: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct BinaryExpression {
 | 
			
		||||
@ -774,17 +781,18 @@ impl BinaryExpression {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        pipe_info.is_in_pipe = false;
 | 
			
		||||
 | 
			
		||||
        let left_json_value = self
 | 
			
		||||
            .left
 | 
			
		||||
            .get_result(memory, pipe_info, engine)?
 | 
			
		||||
            .get_result(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
            .get_json_value()?;
 | 
			
		||||
        let right_json_value = self
 | 
			
		||||
            .right
 | 
			
		||||
            .get_result(memory, pipe_info, engine)?
 | 
			
		||||
            .get_result(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
            .get_json_value()?;
 | 
			
		||||
 | 
			
		||||
        // 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct UnaryExpression {
 | 
			
		||||
@ -873,6 +881,7 @@ impl UnaryExpression {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        pipe_info.is_in_pipe = false;
 | 
			
		||||
@ -880,7 +889,7 @@ impl UnaryExpression {
 | 
			
		||||
        let num = parse_json_number_as_f64(
 | 
			
		||||
            &self
 | 
			
		||||
                .argument
 | 
			
		||||
                .get_result(memory, pipe_info, engine)?
 | 
			
		||||
                .get_result(memory, pipe_info, stdlib, engine)?
 | 
			
		||||
                .get_json_value()?,
 | 
			
		||||
            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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", tag = "type")]
 | 
			
		||||
pub struct PipeExpression {
 | 
			
		||||
@ -910,12 +919,13 @@ impl PipeExpression {
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        pipe_info: &mut PipeInfo,
 | 
			
		||||
        stdlib: &crate::std::StdLib,
 | 
			
		||||
        engine: &mut EngineConnection,
 | 
			
		||||
    ) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        // Reset the previous results.
 | 
			
		||||
        pipe_info.previous_results = vec![];
 | 
			
		||||
        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],
 | 
			
		||||
    pipe_info: &mut PipeInfo,
 | 
			
		||||
    source_range: SourceRange,
 | 
			
		||||
    stdlib: &crate::std::StdLib,
 | 
			
		||||
    engine: &mut EngineConnection,
 | 
			
		||||
) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    if pipe_info.index == body.len() {
 | 
			
		||||
@ -949,15 +960,15 @@ fn execute_pipe_body(
 | 
			
		||||
 | 
			
		||||
    match 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.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) => {
 | 
			
		||||
            pipe_info.is_in_pipe = true;
 | 
			
		||||
            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.
 | 
			
		||||
@ -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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct FunctionExpression {
 | 
			
		||||
@ -982,7 +993,7 @@ pub struct 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)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
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 anyhow::Result;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[cfg(not(test))]
 | 
			
		||||
use wasm_bindgen::prelude::*;
 | 
			
		||||
 | 
			
		||||
@ -13,7 +15,7 @@ use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
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)]
 | 
			
		||||
#[serde(tag = "type", rename_all = "camelCase")]
 | 
			
		||||
pub enum MemoryItem {
 | 
			
		||||
@ -112,12 +114,7 @@ pub enum MemoryItem {
 | 
			
		||||
    },
 | 
			
		||||
    SketchGroup(SketchGroup),
 | 
			
		||||
    ExtrudeGroup(ExtrudeGroup),
 | 
			
		||||
    ExtrudeTransform {
 | 
			
		||||
        position: Position,
 | 
			
		||||
        rotation: Rotation,
 | 
			
		||||
        #[serde(rename = "__meta")]
 | 
			
		||||
        meta: Vec<Metadata>,
 | 
			
		||||
    },
 | 
			
		||||
    ExtrudeTransform(ExtrudeTransform),
 | 
			
		||||
    Function {
 | 
			
		||||
        #[serde(skip)]
 | 
			
		||||
        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(
 | 
			
		||||
    s: &[MemoryItem],
 | 
			
		||||
    memory: &ProgramMemory,
 | 
			
		||||
@ -141,9 +148,7 @@ impl From<MemoryItem> for Vec<SourceRange> {
 | 
			
		||||
            MemoryItem::UserVal { meta, .. } => 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::ExtrudeTransform { meta, .. } => {
 | 
			
		||||
                meta.iter().map(|m| m.source_range).collect()
 | 
			
		||||
            }
 | 
			
		||||
            MemoryItem::ExtrudeTransform(e) => e.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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct SketchGroup {
 | 
			
		||||
    /// The id of the sketch group.
 | 
			
		||||
    pub id: uuid::Uuid,
 | 
			
		||||
    /// The paths in the sketch group.
 | 
			
		||||
    pub value: Vec<Path>,
 | 
			
		||||
    /// The starting path.
 | 
			
		||||
    pub start: BasePath,
 | 
			
		||||
    /// The position of the sketch group.
 | 
			
		||||
    pub position: Position,
 | 
			
		||||
    /// The rotation of the sketch group.
 | 
			
		||||
    pub rotation: Rotation,
 | 
			
		||||
    /// Metadata.
 | 
			
		||||
    #[serde(rename = "__meta")]
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct ExtrudeGroup {
 | 
			
		||||
    /// The id of the extrude group.
 | 
			
		||||
    pub id: uuid::Uuid,
 | 
			
		||||
    /// The extrude surfaces.
 | 
			
		||||
    pub value: Vec<ExtrudeSurface>,
 | 
			
		||||
    /// The height of the extrude group.
 | 
			
		||||
    pub height: f64,
 | 
			
		||||
    /// The position of the extrude group.
 | 
			
		||||
    pub position: Position,
 | 
			
		||||
    /// The rotation of the extrude group.
 | 
			
		||||
    pub rotation: Rotation,
 | 
			
		||||
    /// Metadata.
 | 
			
		||||
    #[serde(rename = "__meta")]
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub enum BodyType {
 | 
			
		||||
@ -270,19 +289,19 @@ pub enum BodyType {
 | 
			
		||||
    Block,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS)]
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
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)]
 | 
			
		||||
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)]
 | 
			
		||||
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)]
 | 
			
		||||
pub struct Point2d {
 | 
			
		||||
    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)]
 | 
			
		||||
pub struct Point3d {
 | 
			
		||||
    pub x: f64,
 | 
			
		||||
@ -309,10 +328,12 @@ pub struct Point3d {
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct Metadata {
 | 
			
		||||
    /// The source range.
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct BasePath {
 | 
			
		||||
    /// The from point.
 | 
			
		||||
    pub from: [f64; 2],
 | 
			
		||||
    /// The to point.
 | 
			
		||||
    pub to: [f64; 2],
 | 
			
		||||
    /// The name of the path.
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    /// Metadata.
 | 
			
		||||
    #[serde(rename = "__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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct GeoMeta {
 | 
			
		||||
    /// The id of the geometry.
 | 
			
		||||
    pub id: uuid::Uuid,
 | 
			
		||||
    /// Metadata.
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(tag = "type", rename_all = "camelCase")]
 | 
			
		||||
pub enum Path {
 | 
			
		||||
    /// A path that goes to a point.
 | 
			
		||||
    ToPoint {
 | 
			
		||||
        #[serde(flatten)]
 | 
			
		||||
        base: BasePath,
 | 
			
		||||
    },
 | 
			
		||||
    /// A path that is horizontal.
 | 
			
		||||
    Horizontal {
 | 
			
		||||
        #[serde(flatten)]
 | 
			
		||||
        base: BasePath,
 | 
			
		||||
        /// The x coordinate.
 | 
			
		||||
        x: f64,
 | 
			
		||||
    },
 | 
			
		||||
    /// An angled line to.
 | 
			
		||||
    AngledLineTo {
 | 
			
		||||
        #[serde(flatten)]
 | 
			
		||||
        base: BasePath,
 | 
			
		||||
        /// The x coordinate.
 | 
			
		||||
        x: Option<f64>,
 | 
			
		||||
        /// The y coordinate.
 | 
			
		||||
        y: Option<f64>,
 | 
			
		||||
    },
 | 
			
		||||
    /// A base path.
 | 
			
		||||
    Base {
 | 
			
		||||
        #[serde(flatten)]
 | 
			
		||||
        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)]
 | 
			
		||||
#[serde(tag = "type", rename_all = "camelCase")]
 | 
			
		||||
pub enum ExtrudeSurface {
 | 
			
		||||
    /// An extrude plane.
 | 
			
		||||
    ExtrudePlane {
 | 
			
		||||
        /// The position.
 | 
			
		||||
        position: Position,
 | 
			
		||||
        /// The rotation.
 | 
			
		||||
        rotation: Rotation,
 | 
			
		||||
        /// The name.
 | 
			
		||||
        name: String,
 | 
			
		||||
        /// Metadata.
 | 
			
		||||
        #[serde(flatten)]
 | 
			
		||||
        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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct PipeInfo {
 | 
			
		||||
@ -470,6 +513,7 @@ fn execute(
 | 
			
		||||
    engine: &mut EngineConnection,
 | 
			
		||||
) -> Result<ProgramMemory, KclError> {
 | 
			
		||||
    let mut pipe_info = PipeInfo::default();
 | 
			
		||||
    let stdlib = crate::std::StdLib::new();
 | 
			
		||||
 | 
			
		||||
    // Iterate over the body of the program.
 | 
			
		||||
    for statement in &program.body {
 | 
			
		||||
@ -525,8 +569,12 @@ fn execute(
 | 
			
		||||
                            memory.add(&var_name, value.clone(), source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::BinaryExpression(binary_expression) => {
 | 
			
		||||
                            let result =
 | 
			
		||||
                                binary_expression.get_result(memory, &mut pipe_info, engine)?;
 | 
			
		||||
                            let result = binary_expression.get_result(
 | 
			
		||||
                                memory,
 | 
			
		||||
                                &mut pipe_info,
 | 
			
		||||
                                &stdlib,
 | 
			
		||||
                                engine,
 | 
			
		||||
                            )?;
 | 
			
		||||
                            memory.add(&var_name, result, source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::FunctionExpression(function_expression) => {
 | 
			
		||||
@ -563,12 +611,17 @@ fn execute(
 | 
			
		||||
                            )?;
 | 
			
		||||
                        }
 | 
			
		||||
                        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)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::PipeExpression(pipe_expression) => {
 | 
			
		||||
                            let result =
 | 
			
		||||
                                pipe_expression.get_result(memory, &mut pipe_info, engine)?;
 | 
			
		||||
                            let result = pipe_expression.get_result(
 | 
			
		||||
                                memory,
 | 
			
		||||
                                &mut pipe_info,
 | 
			
		||||
                                &stdlib,
 | 
			
		||||
                                engine,
 | 
			
		||||
                            )?;
 | 
			
		||||
                            memory.add(&var_name, result, source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::PipeSubstitution(pipe_substitution) => {
 | 
			
		||||
@ -578,13 +631,21 @@ fn execute(
 | 
			
		||||
                            }));
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::ArrayExpression(array_expression) => {
 | 
			
		||||
                            let result =
 | 
			
		||||
                                array_expression.execute(memory, &mut pipe_info, engine)?;
 | 
			
		||||
                            let result = array_expression.execute(
 | 
			
		||||
                                memory,
 | 
			
		||||
                                &mut pipe_info,
 | 
			
		||||
                                &stdlib,
 | 
			
		||||
                                engine,
 | 
			
		||||
                            )?;
 | 
			
		||||
                            memory.add(&var_name, result, source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::ObjectExpression(object_expression) => {
 | 
			
		||||
                            let result =
 | 
			
		||||
                                object_expression.execute(memory, &mut pipe_info, engine)?;
 | 
			
		||||
                            let result = object_expression.execute(
 | 
			
		||||
                                memory,
 | 
			
		||||
                                &mut pipe_info,
 | 
			
		||||
                                &stdlib,
 | 
			
		||||
                                engine,
 | 
			
		||||
                            )?;
 | 
			
		||||
                            memory.add(&var_name, result, source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::MemberExpression(member_expression) => {
 | 
			
		||||
@ -592,8 +653,12 @@ fn execute(
 | 
			
		||||
                            memory.add(&var_name, result, source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                        Value::UnaryExpression(unary_expression) => {
 | 
			
		||||
                            let result =
 | 
			
		||||
                                unary_expression.get_result(memory, &mut pipe_info, engine)?;
 | 
			
		||||
                            let result = unary_expression.get_result(
 | 
			
		||||
                                memory,
 | 
			
		||||
                                &mut pipe_info,
 | 
			
		||||
                                &stdlib,
 | 
			
		||||
                                engine,
 | 
			
		||||
                            )?;
 | 
			
		||||
                            memory.add(&var_name, result, source_range)?;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@ -601,7 +666,7 @@ fn execute(
 | 
			
		||||
            }
 | 
			
		||||
            BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
 | 
			
		||||
                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));
 | 
			
		||||
                }
 | 
			
		||||
                Value::Identifier(identifier) => {
 | 
			
		||||
@ -671,7 +736,6 @@ mod tests {
 | 
			
		||||
    pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
 | 
			
		||||
        let tokens = crate::tokeniser::lexer(code);
 | 
			
		||||
        let program = crate::parser::abstract_syntax_tree(&tokens)?;
 | 
			
		||||
        println!("{:#?}", program);
 | 
			
		||||
        let mut mem: ProgramMemory = Default::default();
 | 
			
		||||
        let mut engine = EngineConnection::new("dev.kittycad.io", "some-token", "").await?;
 | 
			
		||||
        let memory = execute(program, &mut mem, BodyType::Root, &mut engine)?;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
mod abstract_syntax_tree_types;
 | 
			
		||||
mod docs;
 | 
			
		||||
mod engine;
 | 
			
		||||
mod errors;
 | 
			
		||||
mod executor;
 | 
			
		||||
 | 
			
		||||
@ -2,16 +2,32 @@
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExtrudeGroup, MemoryItem},
 | 
			
		||||
    executor::{ExtrudeGroup, ExtrudeTransform, MemoryItem, SketchGroup},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
/// Extrudes by a given amount.
 | 
			
		||||
pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 cmd = kittycad::types::ModelingCmd::Extrude {
 | 
			
		||||
@ -21,7 +37,7 @@ pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    };
 | 
			
		||||
    args.send_modeling_cmd(id, cmd)?;
 | 
			
		||||
 | 
			
		||||
    Ok(MemoryItem::ExtrudeGroup(ExtrudeGroup {
 | 
			
		||||
    Ok(ExtrudeGroup {
 | 
			
		||||
        id,
 | 
			
		||||
        // TODO, this is just an empty array now, should be deleted. This
 | 
			
		||||
        // comment was originally in the JS code.
 | 
			
		||||
@ -30,14 +46,27 @@ pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        position: sketch_group.position,
 | 
			
		||||
        rotation: sketch_group.rotation,
 | 
			
		||||
        meta: sketch_group.meta,
 | 
			
		||||
    }))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the extrude wall transform.
 | 
			
		||||
pub fn get_extrude_wall_transform(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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
 | 
			
		||||
        .get_path_by_name(&surface_name)
 | 
			
		||||
        .get_path_by_name(surface_name)
 | 
			
		||||
        .ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                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(),
 | 
			
		||||
        rotation: surface.get_rotation(),
 | 
			
		||||
        meta: extrude_group.meta,
 | 
			
		||||
 | 
			
		||||
@ -10,69 +10,81 @@ mod utils;
 | 
			
		||||
 | 
			
		||||
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::{
 | 
			
		||||
    abstract_syntax_tree_types::parse_json_number_as_f64,
 | 
			
		||||
    engine::EngineConnection,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    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 StdFn = fn(&mut Args) -> Result<MemoryItem, KclError>;
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
   pub static ref INTERNAL_FNS: FnMap =
 | 
			
		||||
        {
 | 
			
		||||
            HashMap::from([
 | 
			
		||||
                // Extrude functions.
 | 
			
		||||
                ("extrude".to_string(), extrude as StdFn),
 | 
			
		||||
                ("getExtrudeWallTransform".to_string(), get_extrude_wall_transform as StdFn),
 | 
			
		||||
pub struct StdLib {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>>,
 | 
			
		||||
 | 
			
		||||
                ("min".to_string(), min as StdFn),
 | 
			
		||||
                ("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),
 | 
			
		||||
    pub fns: FnMap,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
                // Sketch functions.
 | 
			
		||||
                ("lineTo".to_string(), line_to as StdFn),
 | 
			
		||||
                ("xLineTo".to_string(), x_line_to as StdFn),
 | 
			
		||||
                ("yLineTo".to_string(), y_line_to as StdFn),
 | 
			
		||||
                ("line".to_string(), line as StdFn),
 | 
			
		||||
                ("xLine".to_string(), x_line as StdFn),
 | 
			
		||||
                ("yLine".to_string(), y_line as StdFn),
 | 
			
		||||
                ("angledLine".to_string(), angled_line as StdFn),
 | 
			
		||||
                ("angledLineOfXLength".to_string(), angled_line_of_x_length as StdFn),
 | 
			
		||||
                ("angledLineToX".to_string(), angled_line_to_x as StdFn),
 | 
			
		||||
                ("angledLineOfYLength".to_string(), angled_line_of_y_length as StdFn),
 | 
			
		||||
                ("angledLineToY".to_string(), angled_line_to_y as StdFn),
 | 
			
		||||
                ("angledLineThatIntersects".to_string(), angled_line_that_intersects as StdFn),
 | 
			
		||||
                ("startSketchAt".to_string(), start_sketch_at as StdFn),
 | 
			
		||||
                ("close".to_string(), close as StdFn),
 | 
			
		||||
            ])
 | 
			
		||||
impl StdLib {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        let internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>> = vec![
 | 
			
		||||
            Box::new(Min),
 | 
			
		||||
            Box::new(LegLen),
 | 
			
		||||
            Box::new(LegAngX),
 | 
			
		||||
            Box::new(LegAngY),
 | 
			
		||||
            Box::new(crate::std::extrude::Extrude),
 | 
			
		||||
            Box::new(crate::std::extrude::GetExtrudeWallTransform),
 | 
			
		||||
            Box::new(crate::std::segment::SegEndX),
 | 
			
		||||
            Box::new(crate::std::segment::SegEndY),
 | 
			
		||||
            Box::new(crate::std::segment::LastSegX),
 | 
			
		||||
            Box::new(crate::std::segment::LastSegY),
 | 
			
		||||
            Box::new(crate::std::segment::SegLen),
 | 
			
		||||
            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)]
 | 
			
		||||
@ -471,34 +483,202 @@ impl<'a> Args<'a> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the minimum of the given arguments.
 | 
			
		||||
/// TODO fix min
 | 
			
		||||
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;
 | 
			
		||||
    for arg in args.get_number_array()? {
 | 
			
		||||
        if arg < min {
 | 
			
		||||
            min = arg;
 | 
			
		||||
    for arg in args.iter() {
 | 
			
		||||
        if *arg < min {
 | 
			
		||||
            min = *arg;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(min)
 | 
			
		||||
    min
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the length of the given leg.
 | 
			
		||||
pub fn leg_length(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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.
 | 
			
		||||
pub fn leg_angle_x(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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.
 | 
			
		||||
pub fn leg_angle_y(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::MemoryItem,
 | 
			
		||||
    executor::{MemoryItem, SketchGroup},
 | 
			
		||||
    std::{utils::get_angle, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
/// Returns the segment end of x.
 | 
			
		||||
pub fn segment_end_x(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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
 | 
			
		||||
        .get_base_by_name_or_start(&segment_name)
 | 
			
		||||
        .get_base_by_name_or_start(segment_name)
 | 
			
		||||
        .ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                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.
 | 
			
		||||
pub fn segment_end_y(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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
 | 
			
		||||
        .get_base_by_name_or_start(&segment_name)
 | 
			
		||||
        .get_base_by_name_or_start(segment_name)
 | 
			
		||||
        .ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                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.
 | 
			
		||||
pub fn last_segment_x(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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
 | 
			
		||||
        .value
 | 
			
		||||
        .last()
 | 
			
		||||
@ -61,12 +101,22 @@ pub fn last_segment_x(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        })?
 | 
			
		||||
        .get_base();
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(last_line.to[0])
 | 
			
		||||
    Ok(last_line.to[0])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the last segment of y.
 | 
			
		||||
pub fn last_segment_y(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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
 | 
			
		||||
        .value
 | 
			
		||||
        .last()
 | 
			
		||||
@ -81,63 +131,100 @@ pub fn last_segment_y(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        })?
 | 
			
		||||
        .get_base();
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(last_line.to[1])
 | 
			
		||||
    Ok(last_line.to[1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the length of the segment.
 | 
			
		||||
pub fn segment_length(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
 | 
			
		||||
    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 result = inner_segment_length(&segment_name, sketch_group, args)?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the length of the segment.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "segLen",
 | 
			
		||||
}]
 | 
			
		||||
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 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.
 | 
			
		||||
pub fn segment_angle(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
 | 
			
		||||
    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 result = inner_segment_angle(&segment_name, sketch_group, args)?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the angle of the segment.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "segAng",
 | 
			
		||||
}]
 | 
			
		||||
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 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.
 | 
			
		||||
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 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 result = inner_angle_to_match_length_x(&segment_name, to, sketch_group, args)?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the angle to match the given length for x.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "angleToMatchLengthX",
 | 
			
		||||
}]
 | 
			
		||||
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 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();
 | 
			
		||||
 | 
			
		||||
    if diff > length {
 | 
			
		||||
        args.make_user_val_from_f64(0.0)
 | 
			
		||||
        Ok(0.0)
 | 
			
		||||
    } 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.
 | 
			
		||||
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 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 result = inner_angle_to_match_length_y(&segment_name, to, sketch_group, args)?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the angle to match the given length for y.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "angleToMatchLengthY",
 | 
			
		||||
}]
 | 
			
		||||
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 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();
 | 
			
		||||
 | 
			
		||||
    if diff > length {
 | 
			
		||||
        args.make_user_val_from_f64(0.0)
 | 
			
		||||
        Ok(0.0)
 | 
			
		||||
    } 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.
 | 
			
		||||
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use kittycad::types::{ModelingCmd, Point3D};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
@ -14,11 +16,19 @@ use crate::{
 | 
			
		||||
 | 
			
		||||
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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
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]),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -31,6 +41,9 @@ pub fn line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line to a point.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "lineTo",
 | 
			
		||||
}]
 | 
			
		||||
fn inner_line_to(
 | 
			
		||||
    data: LineToData,
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
@ -65,17 +78,39 @@ fn inner_line_to(
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
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),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line to a point on the x-axis.
 | 
			
		||||
pub fn x_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 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)?;
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
 | 
			
		||||
    Ok(new_sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line to a point on the y-axis.
 | 
			
		||||
pub fn y_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 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)?;
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
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]),
 | 
			
		||||
    /// A string like `default`.
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
pub enum PointOrDefault {
 | 
			
		||||
    /// A point.
 | 
			
		||||
    Point([f64; 2]),
 | 
			
		||||
    /// A string like `default`.
 | 
			
		||||
    Default(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -141,6 +203,10 @@ pub fn line(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "line",
 | 
			
		||||
}]
 | 
			
		||||
fn inner_line(
 | 
			
		||||
    data: LineData,
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
@ -195,55 +261,95 @@ fn inner_line(
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
pub enum AxisLineData {
 | 
			
		||||
    PointWithTag { length: f64, tag: String },
 | 
			
		||||
    Point(f64),
 | 
			
		||||
    /// The length with a tag.
 | 
			
		||||
    LengthWithTag {
 | 
			
		||||
        /// The length of the line.
 | 
			
		||||
        length: f64,
 | 
			
		||||
        /// The tag.
 | 
			
		||||
        tag: String,
 | 
			
		||||
    },
 | 
			
		||||
    /// The length.
 | 
			
		||||
    Length(f64),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line on the x-axis.
 | 
			
		||||
pub fn x_line(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 {
 | 
			
		||||
        AxisLineData::PointWithTag { length, tag } => LineData::PointWithTag {
 | 
			
		||||
        AxisLineData::LengthWithTag { length, tag } => LineData::PointWithTag {
 | 
			
		||||
            to: PointOrDefault::Point([length, 0.0]),
 | 
			
		||||
            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)?;
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
    Ok(new_sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line on the y-axis.
 | 
			
		||||
pub fn y_line(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    let (data, sketch_group): (AxisLineData, SketchGroup) = args.get_data_and_sketch_group()?;
 | 
			
		||||
 | 
			
		||||
    let line_data = match data {
 | 
			
		||||
        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)?;
 | 
			
		||||
    let new_sketch_group = inner_y_line(data, sketch_group, args)?;
 | 
			
		||||
    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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
pub enum AngledLineData {
 | 
			
		||||
    /// An angle and length with a tag.
 | 
			
		||||
    AngleWithTag {
 | 
			
		||||
        /// The angle of the line.
 | 
			
		||||
        angle: f64,
 | 
			
		||||
        /// The length of the line.
 | 
			
		||||
        length: f64,
 | 
			
		||||
        /// The tag.
 | 
			
		||||
        tag: String,
 | 
			
		||||
    },
 | 
			
		||||
    /// An angle and length.
 | 
			
		||||
    AngleAndLength([f64; 2]),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -251,6 +357,19 @@ pub enum AngledLineData {
 | 
			
		||||
pub fn angled_line(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 (angle, length) = match &data {
 | 
			
		||||
        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();
 | 
			
		||||
    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.
 | 
			
		||||
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 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 {
 | 
			
		||||
        AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
 | 
			
		||||
        AngledLineData::AngleAndLength(angle_and_length) => {
 | 
			
		||||
@ -312,14 +444,24 @@ pub fn angled_line_of_x_length(args: &mut Args) -> Result<MemoryItem, KclError>
 | 
			
		||||
        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)]
 | 
			
		||||
#[serde(rename_all = "camelCase", untagged)]
 | 
			
		||||
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]),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -327,6 +469,19 @@ pub enum AngledLineToData {
 | 
			
		||||
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 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 (angle, x_to) = match &data {
 | 
			
		||||
        AngledLineToData::AngleWithTag { angle, to, .. } => (*angle, *to),
 | 
			
		||||
@ -349,13 +504,27 @@ pub fn angled_line_to_x(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        sketch_group,
 | 
			
		||||
        args,
 | 
			
		||||
    )?;
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
    Ok(new_sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line of a given y length.
 | 
			
		||||
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 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 {
 | 
			
		||||
        AngledLineData::AngleWithTag { angle, length, .. } => (*angle, *length),
 | 
			
		||||
        AngledLineData::AngleAndLength(angle_and_length) => {
 | 
			
		||||
@ -378,13 +547,26 @@ pub fn angled_line_of_y_length(args: &mut Args) -> Result<MemoryItem, KclError>
 | 
			
		||||
        args,
 | 
			
		||||
    )?;
 | 
			
		||||
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
    Ok(new_sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line to a given y coordinate.
 | 
			
		||||
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 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 (angle, y_to) = match &data {
 | 
			
		||||
        AngledLineToData::AngleWithTag { angle, to, .. } => (*angle, *to),
 | 
			
		||||
@ -407,10 +589,11 @@ pub fn angled_line_to_y(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
        sketch_group,
 | 
			
		||||
        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)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
// 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> {
 | 
			
		||||
    let (data, sketch_group): (AngeledLineThatIntersectsData, SketchGroup) =
 | 
			
		||||
        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
 | 
			
		||||
        .get_path_by_name(&data.intersect_tag)
 | 
			
		||||
        .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)?;
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
    Ok(new_sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start a sketch at a given point.
 | 
			
		||||
pub fn start_sketch_at(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 to = match &data {
 | 
			
		||||
        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,
 | 
			
		||||
        meta: vec![args.source_range.into()],
 | 
			
		||||
    };
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(sketch_group))
 | 
			
		||||
    Ok(sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Close the current sketch.
 | 
			
		||||
pub fn close(args: &mut Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    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 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)]
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user