1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Traits and types used for KittyCAD Execution Plans.

pub use self::address::Address;
use self::events::EventWriter;
pub use self::primitive::{ListHeader, NumericPrimitive, ObjectHeader, Primitive};

use serde::{Deserialize, Serialize};

mod address;
mod containers;
pub mod events;
#[macro_use]
mod primitive;

/// Types that can be written to or read from KCEP program memory, in one contiguous block.
/// If they require multiple memory addresses, they will be laid out
/// into multiple consecutive memory addresses.
pub trait Value: Sized {
    /// Store the value in memory.
    fn into_parts(self) -> Vec<Primitive>;
    /// Read the value from memory.
    fn from_parts<I>(values: &mut I) -> Result<(Self, usize), MemoryError>
    where
        I: Iterator<Item = Option<Primitive>>;
}

/// Types that can be read from KCEP program memory,
/// scattered across multiple places in the address space.
pub trait FromMemory: Sized {
    /// Read this type from memory, getting each field of the type from a different memory address.
    fn from_memory<I, M>(fields: &mut I, mem: &mut M, events: &mut EventWriter) -> Result<Self, MemoryError>
    where
        M: ReadMemory,
        I: Iterator<Item = InMemory>;
}

/// Where in memory a value is.
#[derive(Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub enum InMemory {
    /// At the given address.
    Address(Address),
    /// Top of stack. Pop the value after it's read.
    StackPop,
    /// Top of stack. Leave the value there after it's read.
    StackPeek,
}

impl From<Address> for InMemory {
    fn from(a: Address) -> Self {
        Self::Address(a)
    }
}

/// Memory that a KittyCAD Execution Plan can read from.
pub trait ReadMemory {
    /// Get a value from the given address.
    fn get(&self, addr: &Address) -> Option<&Primitive>;
    /// Same as get but match the return signature of stack operations.
    fn get_ok(&self, address: &Address) -> Result<Vec<Primitive>, MemoryError> {
        match self.get(address) {
            Some(prim) => Ok(vec![prim.clone()]),
            None => Err(MemoryError::MemoryBadAccess),
        }
    }

    /// Get a value from the given starting address. Value might require multiple addresses.
    fn get_composite<T: Value>(&self, start: Address) -> Result<(T, usize), MemoryError>;
    /// Remove the value on top of the stack, return it.
    fn stack_pop(&mut self) -> Result<Vec<Primitive>, MemoryError>;
    /// Return the value on top of the stack.
    fn stack_peek(&self) -> Result<Vec<Primitive>, MemoryError>;
}

/// Errors that could occur when reading a type from KittyCAD Execution Plan program memory.
#[derive(Debug, thiserror::Error)]
pub enum MemoryError {
    /// Something went wrong
    #[error("Memory was wrong size")]
    MemoryWrongSize,
    /// Something went very wrong
    #[error("Bad memory access")]
    MemoryBadAccess,
    /// Type error, memory contained the wrong type.
    #[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")]
    MemoryWrongType {
        /// What the KittyCAD executor expected memory to contain
        expected: &'static str,
        /// What was actually in memory
        actual: String,
    },
    /// When trying to read an enum from memory, found a variant tag which is not valid for this enum.
    #[error("Found an unexpected tag '{actual}' when trying to read an enum of type {expected_type} from memory. Looking for one of {}", csv(.valid_variants))]
    InvalidEnumVariant {
        /// What type of enum was being read from memory.
        expected_type: String,
        /// The actual enum tag found in memory.
        actual: String,
        /// Which values would be acceptable?
        valid_variants: Vec<&'static str>,
    },
    /// Stack is empty
    #[error("Stack is empty")]
    StackEmpty,
    /// Stack should have contained a single primitive but it had a composite value instead.
    #[error("Expected stack to contain a single primitive, but it had a slice of length {actual_length}")]
    StackNotPrimitive {
        /// The actual size of the data that was popped off the stack
        /// Expected to be 1, but it was something else.
        actual_length: usize,
    },
    /// You didn't supply enough fields -- you have to supply one field per field of the original struct
    #[error("You didn't supply enough fields -- you have to supply one field per field of the original struct")]
    NotEnoughFields,
}

fn csv(v: &[&'static str]) -> String {
    v.join(", ")
}

/// Macro to generate an `impl Value` for the given type `$subject`.
/// The type `$subject` must be "primitive-ish",
/// i.e. something that can be converted Into a Primitive and TryFrom a primitive
#[macro_export]
macro_rules! impl_value_on_primitive_ish {
    ($trait:ident, $subject:ident) => {
        impl $trait for $subject {
            fn into_parts(self) -> Vec<Primitive> {
                vec![self.into()]
            }

            fn from_parts<I>(values: &mut I) -> Result<(Self, usize), MemoryError>
            where
                I: Iterator<Item = Option<Primitive>>,
            {
                values
                    .next()
                    .ok_or(MemoryError::MemoryWrongSize)?
                    .to_owned()
                    .ok_or(MemoryError::MemoryWrongSize)?
                    .try_into()
                    .map(|prim| (prim, 1))
            }
        }
    };
}