Skip to content

Commit

Permalink
convert v8 objects to Rust structs manually, without ops
Browse files Browse the repository at this point in the history
this allows the JavaScript/TypeScript interface to be more as it should
  • Loading branch information
ahdinosaur committed Mar 25, 2023
1 parent 776208b commit 9123abf
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 84 deletions.
207 changes: 143 additions & 64 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anyhow::{bail, Context, Error, Result};
use deno_core::{
include_js_files, op, serde_v8, url::Url, v8, Extension, JsRuntime, ModuleId, RuntimeOptions,
};
use fj::{Circle, Difference2d, Group, Shape, Sketch, Sweep, Transform};
use fj::{Circle, Difference2d, Group, Shape, Shape2d, Sketch, Sweep, Transform};
use std::rc::Rc;

use crate::module::ModuleLoader;
Expand All @@ -16,16 +16,6 @@ impl ModelLoader {
pub fn new() -> Self {
let js_extension = Extension::builder("sandkit")
.esm(include_js_files!("runtime.js",))
.ops(vec![
op_sketch_from_circle::decl(),
op_circle_from_radius::decl(),
op_difference2d_from_shapes_sketch_sketch::decl(),
op_difference2d_from_shapes_difference2d_sketch::decl(),
op_difference2d_from_shapes_sketch_difference2d::decl(),
op_difference2d_from_shapes_difference2d_difference2d::decl(),
op_sweep_from_paths_sketch::decl(),
op_sweep_from_paths_difference2d::decl(),
])
.build();

let js = JsRuntime::new(RuntimeOptions {
Expand Down Expand Up @@ -110,72 +100,161 @@ impl ModelLoader {
}
}

#[op]
fn op_circle_from_radius(radius: f64) -> Circle {
Circle::from_radius(radius)
}
fn into_shape<'a, 'b, 's>(
scope: &'b mut v8::HandleScope<'s>,
value: v8::Local<'a, v8::Value>,
) -> Result<Shape, Error> {
let object =
v8::Local::<v8::Object>::try_from(value).context("Shape value is not an object")?;
let type_name = v8::String::new(scope, "type")
.context("Unable to create JavaScript string \"type\".")?
.into();
let type_value = object
.get(scope, type_name)
.context("Unable to get \"type\" from shape object")?;
let type_id: String =
serde_v8::from_v8(scope, type_value).context("Unable to convert shape type to String")?;

#[op]
fn op_sketch_from_circle(circle: Circle) -> Sketch {
Sketch::from_circle(circle)
}
let shape: Shape = match type_id.as_str() {
"Difference2d" => into_difference2d(scope, value)?.into(),
"Sketch" => into_sketch(scope, value)?.into(),
"Sweep" => into_sweep(scope, value)?.into(),
_ => {
bail!("Unknown shape type: {}", type_id);
}
};

#[op]
fn op_difference2d_from_shapes_sketch_sketch(a: Sketch, b: Sketch) -> Difference2d {
Difference2d::from_shapes([a.into(), b.into()])
Ok(shape)
}

#[op]
fn op_difference2d_from_shapes_difference2d_sketch(a: Difference2d, b: Sketch) -> Difference2d {
Difference2d::from_shapes([a.into(), b.into()])
fn into_sweep<'a, 'b, 's>(
scope: &'b mut v8::HandleScope<'s>,
value: v8::Local<'a, v8::Value>,
) -> Result<Sweep, Error> {
let object =
v8::Local::<v8::Object>::try_from(value).context("Sweep value is not an object")?;
let shape_name = v8::String::new(scope, "shape")
.context("Unable to create JavaScript string \"shape\".")?
.into();
let shape_value = object
.get(scope, shape_name)
.context("Unable to get \"shape\" from Sweep object")?;
let shape = into_shape2d(scope, shape_value).context("Sweep shape is not a shape")?;
let path_name = v8::String::new(scope, "path")
.context("Unable to create JavaScript string \"path\".")?
.into();
let path_value = object
.get(scope, path_name)
.context("Unable to get \"path\" from Sweep object")?;
let path = serde_v8::from_v8::<[f64; 3]>(scope, path_value)
.context("Unable to parse Sweep path value")?;
let sweep = Sweep::from_path(shape, path);
Ok(sweep)
}

#[op]
fn op_difference2d_from_shapes_sketch_difference2d(a: Sketch, b: Difference2d) -> Difference2d {
Difference2d::from_shapes([a.into(), b.into()])
}
fn into_shape2d<'a, 'b, 's>(
scope: &'b mut v8::HandleScope<'s>,
value: v8::Local<'a, v8::Value>,
) -> Result<Shape2d, Error> {
let object =
v8::Local::<v8::Object>::try_from(value).context("Shape2d value is not an object")?;
let type_name = v8::String::new(scope, "type")
.context("Unable to create JavaScript string \"type\".")?
.into();
let type_value = object
.get(scope, type_name)
.context("Unable to get \"type\" from Shape2d object")?;
let type_id = serde_v8::from_v8::<String>(scope, type_value)
.context("Unable to convert shape2d type to String")?;

let shape: Shape2d = match type_id.as_str() {
"Difference2d" => into_difference2d(scope, value)?.into(),
"Sketch" => into_sketch(scope, value)?.into(),
_ => {
bail!("Unknown shape2d type: {}", type_id);
}
};

#[op]
fn op_difference2d_from_shapes_difference2d_difference2d(
a: Difference2d,
b: Difference2d,
) -> Difference2d {
Difference2d::from_shapes([a.into(), b.into()])
Ok(shape)
}

#[op]
fn op_sweep_from_paths_sketch(shape: Sketch, path: [f64; 3]) -> Sweep {
Sweep::from_path(shape.into(), path)
fn into_difference2d<'a, 'b, 's>(
scope: &'b mut v8::HandleScope<'s>,
value: v8::Local<'a, v8::Value>,
) -> Result<Difference2d, Error> {
let object =
v8::Local::<v8::Object>::try_from(value).context("Difference2d value is not an object")?;
let shapes_name = v8::String::new(scope, "shapes")
.context("Unable to create JavaScript string \"shapes\".")?
.into();
let shapes_value = object
.get(scope, shapes_name)
.context("Unable to get \"shapes\" from Difference2d object")?;
let shapes_array = v8::Local::<v8::Array>::try_from(shapes_value)
.context("Difference2d shapes is not an Array")?;
let shapes_a_value = shapes_array
.get_index(scope, 0)
.context("Unable to get shapes[0] value from Difference2d shapes object")?;
let shapes_a =
into_shape2d(scope, shapes_a_value).context("Difference2d shapes[0] is not a shape")?;
let shapes_b_value = shapes_array
.get_index(scope, 1)
.context("Unable to get shapes[1] value from Difference2d shapes object")?;
let shapes_b =
into_shape2d(scope, shapes_b_value).context("Difference2d shapes[1] is not a shape")?;
let difference2d = Difference2d::from_shapes([shapes_a.into(), shapes_b.into()]);
Ok(difference2d)
}

#[op]
fn op_sweep_from_paths_difference2d(shape: Difference2d, path: [f64; 3]) -> Sweep {
Sweep::from_path(shape.into(), path)
fn into_sketch<'a, 'b, 's>(
scope: &'b mut v8::HandleScope<'s>,
value: v8::Local<'a, v8::Value>,
) -> Result<Sketch, Error> {
let object =
v8::Local::<v8::Object>::try_from(value).context("Sketch value is not an object")?;
let chain_name = v8::String::new(scope, "chain")
.context("Unable to create JavaScript string \"chain\".")?
.into();
let chain_value = object
.get(scope, chain_name)
.context("Unable to get \"chain\" from Sketch object")?;
let chain_object =
v8::Local::<v8::Object>::try_from(chain_value).context("Sketch chain is not an object")?;
let chain_type_name = v8::String::new(scope, "type")
.context("Unable to create JavaScript string \"type\".")?
.into();
let chain_type_value = chain_object
.get(scope, chain_type_name)
.context("Unable to get \"type\" from Sketch chain object")?;
let chain_type = serde_v8::from_v8::<String>(scope, chain_type_value)
.context("Unable to convert chain.type to String")?;
println!("chain type: {}", chain_type);
let sketch = match chain_type.as_str() {
"Circle" => {
let circle = into_circle(scope, chain_value)?;
Sketch::from_circle(circle)
}
_ => {
bail!("Unknown Chain type: {}", chain_type);
}
};
Ok(sketch)
}

fn into_shape<'a, 'b, 's>(
fn into_circle<'a, 'b, 's>(
scope: &'b mut v8::HandleScope<'s>,
value: v8::Local<'a, v8::Value>,
) -> Result<Shape, Error> {
let result = serde_v8::from_v8::<Group>(scope, value);
if result.is_ok() {
return Ok(result?.into());
}
let result = serde_v8::from_v8::<Difference2d>(scope, value);
if result.is_ok() {
return Ok(result?.into());
}
let result = serde_v8::from_v8::<Sketch>(scope, value);
if result.is_ok() {
return Ok(result?.into());
}
let result = serde_v8::from_v8::<Sweep>(scope, value);
if result.is_ok() {
return Ok(result?.into());
}
let result = serde_v8::from_v8::<Transform>(scope, value);
if result.is_ok() {
return Ok(result?.into());
}
bail!("Unable to convert value into Shape");
) -> Result<Circle, Error> {
let object =
v8::Local::<v8::Object>::try_from(value).context("Circle value is not an object")?;
let radius_name = v8::String::new(scope, "radius")
.context("Unable to create JavaScript string \"radius\".")?
.into();
let radius_value = object
.get(scope, radius_name)
.context("Unable to get \"radius\" from Circle object")?;
let radius = serde_v8::from_v8::<f64>(scope, radius_value)
.context("Unable to convert radius value to f64")?;
let circle = Circle::from_radius(radius);
Ok(circle)
}
29 changes: 9 additions & 20 deletions src/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,58 +12,47 @@ const console = {
}

class Circle {
type = "Circle"
constructor({ radius }) {
this.radius = radius
}

static fromRadius(radius) {
return new Circle(ops.op_circle_from_radius(radius))
return new Circle({ radius })
}
}

class Sketch {
constructor({ chain, color }) {
type = "Sketch"
constructor({ chain }) {
this.chain = chain
this.color = color
}

static fromCircle(circle) {
return new Sketch(ops.op_sketch_from_circle(circle))
return new Sketch({ chain: circle })
}
}

class Difference2d {
type = "Difference2d"
constructor({ shapes }) {
this.shapes = shapes
}

static fromShapes(a, b) {
switch (true) {
case a instanceof Sketch && b instanceof Sketch:
return new Difference2d(ops.op_difference2d_from_shapes_sketch_sketch(a, b))
case a instanceof Difference2d && b instanceof Sketch:
return new Difference2d(ops.op_difference2d_from_shapes_difference2d_sketch(a, b))
case a instanceof Sketch && b instanceof Difference2d:
return new Difference2d(ops.op_difference2d_from_shapes_sketch_difference2d(a, b))
case a instanceof Difference2d && b instanceof Difference2d:
return new Difference2d(ops.op_difference2d_from_shapes_difference2d_difference2d(a, b))
}
return new Difference2d({ shapes: [a, b] })
}
}

class Sweep {
type = "Sweep"
constructor({ shape, path }) {
this.shape = shape
this.path = path
}

static fromPath(shape, path) {
switch (true) {
case shape instanceof Sketch:
return new Sweep(ops.op_sweep_from_paths_sketch(shape, path))
case shape instanceof Difference2d:
return new Sweep(ops.op_sweep_from_paths_difference2d(shape, path))
}
return new Sweep({ shape, path })
}
}

Expand Down

0 comments on commit 9123abf

Please sign in to comment.