Type check and coerce arguments to user functions and return values from std Rust functions (#6958)
* Shuffle around function call code Signed-off-by: Nick Cameron <nrc@ncameron.org> * Refactor function calls to share more code Signed-off-by: Nick Cameron <nrc@ncameron.org> * Hack to leave the result of revolve as a singleton rather than array Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -3,7 +3,7 @@ use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{types::NumericType, ArtifactId, KclValue};
|
||||
use crate::{docs::StdLibFn, ModuleId, SourceRange};
|
||||
use crate::{ModuleId, SourceRange};
|
||||
|
||||
/// A CAD modeling operation for display in the feature tree, AKA operations
|
||||
/// timeline.
|
||||
@ -13,21 +13,6 @@ use crate::{docs::StdLibFn, ModuleId, SourceRange};
|
||||
pub enum Operation {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
StdLibCall {
|
||||
/// The standard library function being called.
|
||||
#[serde(flatten)]
|
||||
std_lib_fn: StdLibFnRef,
|
||||
/// The unlabeled argument to the function.
|
||||
unlabeled_arg: Option<OpArg>,
|
||||
/// The labeled keyword arguments to the function.
|
||||
labeled_args: IndexMap<String, OpArg>,
|
||||
/// The source range of the operation in the source code.
|
||||
source_range: SourceRange,
|
||||
/// True if the operation resulted in an error.
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
is_error: bool,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
KclStdLibCall {
|
||||
name: String,
|
||||
/// The unlabeled argument to the function.
|
||||
unlabeled_arg: Option<OpArg>,
|
||||
@ -57,19 +42,12 @@ impl PartialOrd for Operation {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(match (self, other) {
|
||||
(Self::StdLibCall { source_range: a, .. }, Self::StdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::StdLibCall { source_range: a, .. }, Self::KclStdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::StdLibCall { source_range: a, .. }, Self::GroupBegin { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::StdLibCall { .. }, Self::GroupEnd) => std::cmp::Ordering::Less,
|
||||
(Self::KclStdLibCall { source_range: a, .. }, Self::KclStdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::KclStdLibCall { source_range: a, .. }, Self::StdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::KclStdLibCall { source_range: a, .. }, Self::GroupBegin { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::KclStdLibCall { .. }, Self::GroupEnd) => std::cmp::Ordering::Less,
|
||||
(Self::GroupBegin { source_range: a, .. }, Self::GroupBegin { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::GroupBegin { source_range: a, .. }, Self::StdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::GroupBegin { source_range: a, .. }, Self::KclStdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::GroupBegin { .. }, Self::GroupEnd) => std::cmp::Ordering::Less,
|
||||
(Self::GroupEnd, Self::StdLibCall { .. }) => std::cmp::Ordering::Greater,
|
||||
(Self::GroupEnd, Self::KclStdLibCall { .. }) => std::cmp::Ordering::Greater,
|
||||
(Self::GroupEnd, Self::GroupBegin { .. }) => std::cmp::Ordering::Greater,
|
||||
(Self::GroupEnd, Self::GroupEnd) => std::cmp::Ordering::Equal,
|
||||
})
|
||||
@ -81,7 +59,6 @@ impl Operation {
|
||||
pub(crate) fn set_std_lib_call_is_error(&mut self, is_err: bool) {
|
||||
match self {
|
||||
Self::StdLibCall { ref mut is_error, .. } => *is_error = is_err,
|
||||
Self::KclStdLibCall { ref mut is_error, .. } => *is_error = is_err,
|
||||
Self::GroupBegin { .. } | Self::GroupEnd => {}
|
||||
}
|
||||
}
|
||||
@ -107,6 +84,7 @@ pub enum Group {
|
||||
labeled_args: IndexMap<String, OpArg>,
|
||||
},
|
||||
/// A whole-module import use.
|
||||
#[allow(dead_code)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ModuleInstance {
|
||||
/// The name of the module being used.
|
||||
@ -135,54 +113,6 @@ impl OpArg {
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a standard library function. This exists to implement
|
||||
/// `PartialEq` and `Eq` for `Operation`.
|
||||
#[derive(Debug, Clone, Serialize, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export_to = "Operation.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StdLibFnRef {
|
||||
// The following doc comment gets inlined into Operation, overriding what's
|
||||
// there, in the generated TS. We serialize to its name. Renaming the
|
||||
// field to "name" allows it to match the other variant.
|
||||
/// The standard library function being called.
|
||||
#[serde(
|
||||
rename = "name",
|
||||
serialize_with = "std_lib_fn_name",
|
||||
deserialize_with = "std_lib_fn_from_name"
|
||||
)]
|
||||
#[ts(type = "string", rename = "name")]
|
||||
pub std_lib_fn: Box<dyn StdLibFn>,
|
||||
}
|
||||
|
||||
impl StdLibFnRef {
|
||||
pub(crate) fn new(std_lib_fn: Box<dyn StdLibFn>) -> Self {
|
||||
Self { std_lib_fn }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Box<dyn StdLibFn>> for StdLibFnRef {
|
||||
fn from(std_lib_fn: &Box<dyn StdLibFn>) -> Self {
|
||||
Self::new(std_lib_fn.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for StdLibFnRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.std_lib_fn.name() == other.std_lib_fn.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for StdLibFnRef {}
|
||||
|
||||
#[expect(clippy::borrowed_box, reason = "Explicit Box is needed for serde")]
|
||||
fn std_lib_fn_name<S>(std_lib_fn: &Box<dyn StdLibFn>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let name = std_lib_fn.name();
|
||||
serializer.serialize_str(&name)
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!*b
|
||||
}
|
||||
|
Reference in New Issue
Block a user