Skip to content

Commit

Permalink
Impl execution_plan::Value for many modeling command types (#45)
Browse files Browse the repository at this point in the history
Finally, the macro is advanced enough to derive Value for the huge `OkModelingCmdResponse` enum! The only missing piece was that Vec<T: Value> didn't impl Value, but I've added that now.
  • Loading branch information
adamchalmers authored Dec 21, 2023
1 parent e8d8787 commit 8d55e78
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 122 deletions.
10 changes: 5 additions & 5 deletions execution-plan-macros/tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ fn test_derive_on_enum() {
(
"named fields",
FooEnum::A { x: 3 },
vec![Primitive::from("A".to_owned()), Primitive::from(3)],
vec![Primitive::from("A".to_owned()), Primitive::from(3u32)],
),
(
"named fields with Option::Some",
FooEnum::B { y: Some(3) },
vec![
Primitive::from("B".to_owned()),
Primitive::from("Some".to_owned()),
Primitive::from(3),
Primitive::from(3u32),
],
),
(
Expand All @@ -66,7 +66,7 @@ fn test_derive_on_enum() {
FooEnum::C(4, "hello".to_owned()),
vec![
Primitive::from("C".to_owned()),
Primitive::from(4),
Primitive::from(4u32),
Primitive::from("hello".to_owned()),
],
),
Expand Down Expand Up @@ -106,7 +106,7 @@ fn test_derive_on_struct() {
},
vec![
Primitive::from(1.2),
Primitive::from(2),
Primitive::from(2u32),
Primitive::from("Some".to_owned()),
Primitive::from(true),
Primitive::from("hello".to_owned()),
Expand All @@ -122,7 +122,7 @@ fn test_derive_on_struct() {
},
vec![
Primitive::from(1.2),
Primitive::from(2),
Primitive::from(2u32),
Primitive::from("None".to_owned()),
Primitive::from("hello".to_owned()),
],
Expand Down
28 changes: 28 additions & 0 deletions execution-plan-traits/src/containers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{MemoryError, Primitive, Value};
const NONE: &str = "None";
const SOME: &str = "Some";

/// Use the standard enum convention (a string for the variant tag, then all fields of the variant)
impl<T> Value for Option<T>
where
T: Value,
Expand Down Expand Up @@ -42,3 +43,30 @@ where
}
}
}

/// Store the vec's length as the first primitive, then lay out all elements.
impl<T> Value for Vec<T>
where
T: Value,
{
fn into_parts(self) -> Vec<Primitive> {
let mut parts: Vec<Primitive> = Vec::with_capacity(self.len() + 1);
parts.push(self.len().into());
parts.extend(self.into_iter().flat_map(|part| part.into_parts()));
parts
}

fn from_parts<I>(values: &mut I) -> Result<Self, MemoryError>
where
I: Iterator<Item = Option<Primitive>>,
{
// Read the length of the vec -- how many elements does it have?
let n: usize = values
.next()
.flatten()
.ok_or(MemoryError::MemoryWrongSize)?
.try_into()?;
// Read `n` elements from the parts.
(0..n).map(|_| T::from_parts(values)).collect()
}
}
25 changes: 25 additions & 0 deletions execution-plan-traits/src/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,36 @@ impl TryFrom<Primitive> for usize {
}
}

impl TryFrom<Primitive> for u32 {
type Error = MemoryError;

fn try_from(value: Primitive) -> Result<Self, Self::Error> {
if let Primitive::NumericValue(NumericPrimitive::Integer(x)) = value {
Ok(x.try_into().map_err(|_| MemoryError::MemoryWrongType {
expected: "u32",
actual: x.to_string(),
})?)
} else {
Err(MemoryError::MemoryWrongType {
expected: "u32",
actual: format!("{value:?}"),
})
}
}
}

impl From<usize> for Primitive {
fn from(value: usize) -> Self {
Self::NumericValue(NumericPrimitive::Integer(value))
}
}

impl From<u32> for Primitive {
fn from(value: u32) -> Self {
Self::NumericValue(NumericPrimitive::Integer(value as usize))
}
}

/// Various kinds of number.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum NumericPrimitive {
Expand Down Expand Up @@ -202,3 +226,4 @@ impl_value_on_primitive_ish!(Value, Uuid);
type VecU8 = Vec<u8>;
impl_value_on_primitive_ish!(Value, VecU8);
impl_value_on_primitive_ish!(Value, usize);
impl_value_on_primitive_ish!(Value, u32);
14 changes: 7 additions & 7 deletions execution-plan/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ async fn add_literals() {
let plan = vec![Instruction::Arithmetic {
arithmetic: Arithmetic {
operation: Operation::Add,
operand0: Operand::Literal(3.into()),
operand1: Operand::Literal(2.into()),
operand0: Operand::Literal(3u32.into()),
operand1: Operand::Literal(2u32.into()),
},
destination: Address(1),
}];
let mut mem = Memory::default();
let client = test_client().await;
execute(&mut mem, plan, client).await.expect("failed to execute plan");
assert_eq!(mem.get(&Address(1)), Some(&5.into()))
assert_eq!(mem.get(&Address(1)), Some(&5u32.into()))
}

#[tokio::test]
Expand All @@ -60,14 +60,14 @@ async fn add_literal_to_reference() {
// Memory addr 0 contains 450
Instruction::Set {
address: Address(0),
value: 450.into(),
value: 450u32.into(),
},
// Add 20 to addr 0
Instruction::Arithmetic {
arithmetic: Arithmetic {
operation: Operation::Add,
operand0: Operand::Reference(Address(0)),
operand1: Operand::Literal(20.into()),
operand1: Operand::Literal(20u32.into()),
},
destination: Address(1),
},
Expand All @@ -76,7 +76,7 @@ async fn add_literal_to_reference() {
let mut mem = Memory::default();
let client = test_client().await;
execute(&mut mem, plan, client).await.expect("failed to execute plan");
assert_eq!(mem.get(&Address(1)), Some(&470.into()))
assert_eq!(mem.get(&Address(1)), Some(&470u32.into()))
}

#[tokio::test]
Expand All @@ -103,7 +103,7 @@ async fn add_to_composite_value() {
arithmetic: Arithmetic {
operation: Operation::Add,
operand0: Operand::Reference(start_addr),
operand1: Operand::Literal(40.into()),
operand1: Operand::Literal(40u32.into()),
},
destination: start_addr,
}],
Expand Down
22 changes: 22 additions & 0 deletions modeling-cmds/src/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use kittycad_execution_plan_traits::{MemoryError, Primitive};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
Expand Down Expand Up @@ -32,3 +33,24 @@ impl std::fmt::Display for ModelingCmdId {
self.0.fmt(f)
}
}

impl From<ModelingCmdId> for Primitive {
fn from(id: ModelingCmdId) -> Self {
Self::Uuid(id.into())
}
}

impl TryFrom<Primitive> for ModelingCmdId {
type Error = MemoryError;

fn try_from(value: Primitive) -> Result<Self, Self::Error> {
if let Primitive::Uuid(u) = value {
Ok(u.into())
} else {
Err(MemoryError::MemoryWrongType {
expected: "uuid",
actual: format!("{value:?}"),
})
}
}
}
3 changes: 2 additions & 1 deletion modeling-cmds/src/kcep_primitive.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use kittycad_execution_plan_traits::{impl_value_on_primitive_ish, MemoryError, NumericPrimitive, Primitive, Value};

use crate::{base64::Base64Data, shared::Angle};
use crate::{base64::Base64Data, id::ModelingCmdId, shared::Angle};

/// Angle is always stored as f64 degrees.
impl From<Angle> for Primitive {
Expand Down Expand Up @@ -40,3 +40,4 @@ impl TryFrom<Primitive> for Base64Data {

impl_value_on_primitive_ish!(Value, Angle);
impl_value_on_primitive_ish!(Value, Base64Data);
impl_value_on_primitive_ish!(Value, ModelingCmdId);
71 changes: 0 additions & 71 deletions modeling-cmds/src/kcep_value.rs

This file was deleted.

1 change: 0 additions & 1 deletion modeling-cmds/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub mod id;
pub mod impl_extern_type;
mod impl_traits;
mod kcep_primitive;
mod kcep_value;
/// When a modeling command is successful, these responses could be returned.
pub mod ok_response;
/// Output of each modeling command.
Expand Down
3 changes: 2 additions & 1 deletion modeling-cmds/src/ok_response.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use kittycad_execution_plan_macros::ExecutionPlanValue;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

Expand All @@ -7,7 +8,7 @@ macro_rules! build_enum {
($( $variant:ident ),* ) => {
/// A successful response from a modeling command.
/// This can be one of several types of responses, depending on the command.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Serialize, Deserialize, JsonSchema, ExecutionPlanValue)]
#[serde(rename_all = "snake_case", tag = "type", content = "data")]
pub enum OkModelingCmdResponse {
/// An empty response, used for any command that does not explicitly have a response
Expand Down
Loading

0 comments on commit 8d55e78

Please sign in to comment.