Skip to content

Commit b34a5db

Browse files
authored
execution-plan: Macro for implementing Value on structs (#40)
* execution-plan: Macro for implementing Value on a struct * Impl Value and conversion with primitive for more types
1 parent 3e218fa commit b34a5db

File tree

2 files changed

+128
-31
lines changed

2 files changed

+128
-31
lines changed

execution-plan/src/primitive.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use kittycad_modeling_cmds::shared::Angle;
1+
use kittycad_modeling_cmds::{base64::Base64Data, shared::Angle};
22
use serde::{Deserialize, Serialize};
33
use uuid::Uuid;
44

@@ -33,6 +33,12 @@ impl From<String> for Primitive {
3333
}
3434
}
3535

36+
impl From<f32> for Primitive {
37+
fn from(value: f32) -> Self {
38+
Self::NumericValue(NumericPrimitive::Float(value as f64))
39+
}
40+
}
41+
3642
impl From<f64> for Primitive {
3743
fn from(value: f64) -> Self {
3844
Self::NumericValue(NumericPrimitive::Float(value))
@@ -52,6 +58,12 @@ impl From<Vec<u8>> for Primitive {
5258
}
5359
}
5460

61+
impl From<Base64Data> for Primitive {
62+
fn from(value: Base64Data) -> Self {
63+
Self::Bytes(value.into())
64+
}
65+
}
66+
5567
impl TryFrom<Primitive> for String {
5668
type Error = ExecutionError;
5769

@@ -113,6 +125,14 @@ impl TryFrom<Primitive> for f64 {
113125
}
114126
}
115127

128+
impl TryFrom<Primitive> for f32 {
129+
type Error = ExecutionError;
130+
131+
fn try_from(value: Primitive) -> Result<Self, Self::Error> {
132+
f64::try_from(value).map(|x| x as f32)
133+
}
134+
}
135+
116136
impl TryFrom<Primitive> for Vec<u8> {
117137
type Error = ExecutionError;
118138

@@ -128,6 +148,14 @@ impl TryFrom<Primitive> for Vec<u8> {
128148
}
129149
}
130150

151+
impl TryFrom<Primitive> for Base64Data {
152+
type Error = ExecutionError;
153+
154+
fn try_from(value: Primitive) -> Result<Self, Self::Error> {
155+
Vec::<u8>::try_from(value).map(Base64Data::from)
156+
}
157+
}
158+
131159
impl TryFrom<Primitive> for bool {
132160
type Error = ExecutionError;
133161

@@ -143,7 +171,21 @@ impl TryFrom<Primitive> for bool {
143171
}
144172
}
145173

146-
#[cfg(test)]
174+
impl TryFrom<Primitive> for usize {
175+
type Error = ExecutionError;
176+
177+
fn try_from(value: Primitive) -> Result<Self, Self::Error> {
178+
if let Primitive::NumericValue(NumericPrimitive::Integer(x)) = value {
179+
Ok(x)
180+
} else {
181+
Err(ExecutionError::MemoryWrongType {
182+
expected: "usize",
183+
actual: format!("{value:?}"),
184+
})
185+
}
186+
}
187+
}
188+
147189
impl From<usize> for Primitive {
148190
fn from(value: usize) -> Self {
149191
Self::NumericValue(NumericPrimitive::Integer(value))

execution-plan/src/value/impls.rs

Lines changed: 84 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
//! - This canonical ordering is the order of the struct's fields in its Rust source code definition.
66
//! - Enums get laid out by first putting the variant as a string, then putting the variant's fields.
77
use kittycad_modeling_cmds::{
8+
base64::Base64Data,
89
ok_response::OkModelingCmdResponse,
910
output,
10-
shared::{Angle, PathSegment, Point2d, Point3d},
11+
shared::{Angle, PathSegment, Point2d, Point3d, Point4d},
1112
};
13+
use uuid::Uuid;
1214

1315
use super::Value;
1416
use crate::{ExecutionError, Primitive};
@@ -25,45 +27,98 @@ fn err() -> ExecutionError {
2527
ExecutionError::MemoryWrongSize
2628
}
2729

30+
/// Macro to generate an `impl Value` for the given type `$subject`.
31+
/// The type `$subject` must be "primitive-ish",
32+
/// i.e. something that can be converted Into a Primitive and TryFrom a primitive
33+
macro_rules! impl_value_on_primitive_ish {
34+
($subject:ident) => {
35+
impl Value for $subject {
36+
fn into_parts(self) -> Vec<Primitive> {
37+
vec![self.into()]
38+
}
39+
40+
fn from_parts<I>(values: &mut I) -> Result<Self, ExecutionError>
41+
where
42+
I: Iterator<Item = Option<Primitive>>,
43+
{
44+
values.next().ok_or(err())?.to_owned().ok_or(err())?.try_into()
45+
}
46+
}
47+
};
48+
}
49+
50+
impl_value_on_primitive_ish!(f32);
51+
impl_value_on_primitive_ish!(f64);
52+
impl_value_on_primitive_ish!(bool);
53+
impl_value_on_primitive_ish!(String);
54+
impl_value_on_primitive_ish!(Uuid);
55+
type VecU8 = Vec<u8>;
56+
impl_value_on_primitive_ish!(VecU8);
57+
impl_value_on_primitive_ish!(Angle);
58+
impl_value_on_primitive_ish!(usize);
59+
impl_value_on_primitive_ish!(Base64Data);
60+
61+
/// Macro to generate the methods of trait `Value` for the given fields.
62+
/// Args:
63+
/// `$field`: Repeated 0 or more times. Listing of each field in the struct.
64+
/// The order in which these fields are given determines the order that fields are
65+
/// written to and read from memory.
66+
macro_rules! impl_value_on_struct_fields {
67+
($($field:ident),*) => {
68+
fn into_parts(self) -> Vec<Primitive> {
69+
let mut parts = Vec::new();
70+
$(
71+
parts.extend(self.$field.into_parts());
72+
)*
73+
parts
74+
}
75+
76+
fn from_parts<I>(values: &mut I) -> Result<Self, ExecutionError>
77+
where
78+
I: Iterator<Item = Option<Primitive>>,
79+
{
80+
$(
81+
let $field = Value::from_parts(values)?;
82+
)*
83+
Ok(Self {
84+
$(
85+
$field,
86+
)*
87+
})
88+
}
89+
};
90+
}
91+
2892
impl<T> Value for Point2d<T>
2993
where
3094
Primitive: From<T>,
31-
T: TryFrom<Primitive, Error = ExecutionError>,
95+
T: Value,
3296
{
33-
fn into_parts(self) -> Vec<Primitive> {
34-
let points = [self.x, self.y];
35-
points.into_iter().map(|component| component.into()).collect()
36-
}
37-
38-
fn from_parts<I>(values: &mut I) -> Result<Self, ExecutionError>
39-
where
40-
I: Iterator<Item = Option<Primitive>>,
41-
{
42-
let x = values.next().ok_or(err())?.to_owned().ok_or(err())?.try_into()?;
43-
let y = values.next().ok_or(err())?.to_owned().ok_or(err())?.try_into()?;
44-
Ok(Self { x, y })
45-
}
97+
impl_value_on_struct_fields!(x, y);
4698
}
4799

48100
impl<T> Value for Point3d<T>
49101
where
50102
Primitive: From<T>,
51-
T: TryFrom<Primitive, Error = ExecutionError>,
103+
T: Value,
52104
{
53-
fn into_parts(self) -> Vec<Primitive> {
54-
let points = [self.x, self.y, self.z];
55-
points.into_iter().map(|component| component.into()).collect()
56-
}
105+
impl_value_on_struct_fields!(x, y, z);
106+
}
57107

58-
fn from_parts<I>(values: &mut I) -> Result<Self, ExecutionError>
59-
where
60-
I: Iterator<Item = Option<Primitive>>,
61-
{
62-
let x = values.next().ok_or(err())?.to_owned().ok_or(err())?.try_into()?;
63-
let y = values.next().ok_or(err())?.to_owned().ok_or(err())?.try_into()?;
64-
let z = values.next().ok_or(err())?.to_owned().ok_or(err())?.try_into()?;
65-
Ok(Self { x, y, z })
66-
}
108+
impl<T> Value for Point4d<T>
109+
where
110+
Primitive: From<T>,
111+
T: Value,
112+
{
113+
impl_value_on_struct_fields!(x, y, z, w);
114+
}
115+
116+
impl Value for kittycad_modeling_cmds::shared::Color {
117+
impl_value_on_struct_fields!(r, g, b, a);
118+
}
119+
120+
impl Value for kittycad_modeling_cmds::shared::ExportFile {
121+
impl_value_on_struct_fields!(name, contents);
67122
}
68123

69124
/// Layout:

0 commit comments

Comments
 (0)