Skip to content

Commit

Permalink
Derive macro can handle generics and where clauses now
Browse files Browse the repository at this point in the history
  • Loading branch information
adamchalmers committed Dec 20, 2023
1 parent 76a09e9 commit a5cad08
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 83 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions execution-plan-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use proc_macro2::Span;
use quote::{quote, quote_spanned};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Fields};

#[proc_macro_derive(MyMacro)]
#[proc_macro_derive(ExecutionPlanValue)]
pub fn impl_value(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let input = parse_macro_input!(input as DeriveInput);
Expand All @@ -16,7 +16,7 @@ pub fn impl_value(input: TokenStream) -> TokenStream {

// Build the output, possibly using quasi-quotation
let expanded = match input.data {
syn::Data::Struct(data) => impl_value_on_struct(span, name, data),
syn::Data::Struct(data) => impl_value_on_struct(span, name, data, input.generics),
syn::Data::Enum(_) => todo!(),
syn::Data::Union(_) => quote_spanned! {span =>
compile_error!("Value cannot be implemented on a union type")
Expand All @@ -27,7 +27,12 @@ pub fn impl_value(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}

fn impl_value_on_struct(span: Span, name: proc_macro2::Ident, data: syn::DataStruct) -> proc_macro2::TokenStream {
fn impl_value_on_struct(
span: Span,
name: proc_macro2::Ident,
data: syn::DataStruct,
generics: syn::Generics,
) -> proc_macro2::TokenStream {
let Fields::Named(ref fields) = data.fields else {
return quote_spanned! {span =>
compile_error!("Value cannot be implemented on a struct with unnamed fields")
Expand All @@ -46,8 +51,11 @@ fn impl_value_on_struct(span: Span, name: proc_macro2::Ident, data: syn::DataStr
#instantiate_each_field
}
}
let where_clause = &generics.where_clause;
quote! {
impl kittycad_execution_plan_traits::Value for #name {
impl #generics kittycad_execution_plan_traits::Value for #name #generics
#where_clause
{
fn into_parts(self) -> Vec<kittycad_execution_plan_traits::Primitive> {
let mut parts = Vec::new();
#extend_per_field
Expand Down
19 changes: 14 additions & 5 deletions execution-plan-macros/tests/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
use kittycad_execution_plan_macros::MyMacro;
use kittycad_execution_plan_macros::ExecutionPlanValue;
use kittycad_execution_plan_traits::{Primitive, Value};

#[derive(MyMacro)]
struct Foo {
#[derive(ExecutionPlanValue)]
struct FooGeneric<T>
where
Primitive: From<T>,
T: Value,
{
f: f64,
i: usize,
t: T,
}

#[test]
fn check_impl() {}
#[derive(ExecutionPlanValue)]
struct FooConcrete {
f: f64,
i: usize,
}
1 change: 1 addition & 0 deletions modeling-cmds/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum-iterator-derive = "1.2.1"
euler = "0.4.1"
http = "0.2.9"
kittycad-execution-plan-traits = { path = "../execution-plan-traits" }
kittycad-execution-plan-macros = { path = "../execution-plan-macros" }
kittycad-unit-conversion-derive = "0.1.0"
measurements = "0.11.0"
parse-display = "0.8.2"
Expand Down
65 changes: 1 addition & 64 deletions modeling-cmds/src/kcep_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,9 @@ use kittycad_execution_plan_traits::{MemoryError, Primitive, Value};
use crate::{
ok_response::OkModelingCmdResponse,
output,
shared::{Angle, Color, ExportFile, PathSegment, Point2d, Point3d, Point4d},
shared::{Angle, PathSegment, Point2d, Point3d},
};

/// Macro to generate the methods of trait `Value` for the given fields.
/// Args:
/// `$field`: Repeated 0 or more times. Listing of each field in the struct.
/// The order in which these fields are given determines the order that fields are
/// written to and read from memory.
macro_rules! impl_value_on_struct_fields {
($($field:ident),*) => {
fn into_parts(self) -> Vec<Primitive> {
let mut parts = Vec::new();
$(
parts.extend(self.$field.into_parts());
)*
parts
}

fn from_parts<I>(values: &mut I) -> Result<Self, MemoryError>
where
I: Iterator<Item = Option<Primitive>>,
{
$(
let $field = Value::from_parts(values)?;
)*
Ok(Self {
$(
$field,
)*
})
}
};
}

impl<T> Value for Point2d<T>
where
Primitive: From<T>,
T: Value,
{
impl_value_on_struct_fields!(x, y);
}

impl<T> Value for Point3d<T>
where
Primitive: From<T>,
T: Value,
{
impl_value_on_struct_fields!(x, y, z);
}

impl<T> Value for Point4d<T>
where
Primitive: From<T>,
T: Value,
{
impl_value_on_struct_fields!(x, y, z, w);
}

impl Value for Color {
impl_value_on_struct_fields!(r, g, b, a);
}

impl Value for ExportFile {
impl_value_on_struct_fields!(name, contents);
}

pub(crate) const EMPTY: &str = "EMPTY";
pub(crate) const TAKE_SNAPSHOT: &str = "TAKE_SNAPSHOT";
pub(crate) const ARC: &str = "arc";
Expand Down
21 changes: 11 additions & 10 deletions modeling-cmds/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use diesel::{mysql::Mysql, serialize::ToSql, sql_types::Text};
#[cfg(feature = "diesel")]
use diesel_derives::{AsExpression, FromSqlRow};
use enum_iterator::Sequence;
use kittycad_execution_plan_macros::ExecutionPlanValue;
use parse_display_derive::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -50,7 +51,7 @@ pub struct AnnotationOptions {
/// Color to render the annotation
pub color: Option<Color>,
/// Position to put the annotation
pub position: Option<Point3d>,
pub position: Option<Point3d<f32>>,
}

/// Options for annotation text
Expand Down Expand Up @@ -93,7 +94,7 @@ pub enum DistanceType {
}

/// An RGBA color
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, ExecutionPlanValue)]
pub struct Color {
/// Red
pub r: f32,
Expand Down Expand Up @@ -138,10 +139,10 @@ pub enum AnnotationTextAlignmentY {
impl_string_enum_sql! {AnnotationTextAlignmentY}

/// A point in 3D space
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Default, ExecutionPlanValue)]
#[serde(rename = "Point3d")]
#[serde(rename_all = "snake_case")]
pub struct Point3d<T = f32> {
pub struct Point3d<T> {
#[allow(missing_docs)]
pub x: T,
#[allow(missing_docs)]
Expand Down Expand Up @@ -256,10 +257,10 @@ pub enum PathSegment {
}

/// A point in homogeneous (4D) space
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, ExecutionPlanValue)]
#[serde(rename = "Point4d")]
#[serde(rename_all = "snake_case")]
pub struct Point4d<T = f32> {
pub struct Point4d<T> {
#[allow(missing_docs)]
pub x: T,
#[allow(missing_docs)]
Expand All @@ -270,17 +271,17 @@ pub struct Point4d<T = f32> {
pub w: T,
}

impl From<euler::Vec3> for Point3d {
impl From<euler::Vec3> for Point3d<f32> {
fn from(v: euler::Vec3) -> Self {
Self { x: v.x, y: v.y, z: v.z }
}
}

/// A point in 2D space
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default, ExecutionPlanValue)]
#[serde(rename = "Point2d")]
#[serde(rename_all = "snake_case")]
pub struct Point2d<T = f32> {
pub struct Point2d<T> {
#[allow(missing_docs)]
pub x: T,
#[allow(missing_docs)]
Expand Down Expand Up @@ -472,7 +473,7 @@ pub enum CurveType {
}

/// A file to be exported to the client.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Serialize, Deserialize, JsonSchema, ExecutionPlanValue)]
pub struct ExportFile {
/// The name of the file.
pub name: String,
Expand Down

0 comments on commit a5cad08

Please sign in to comment.