Skip to content

Commit

Permalink
Merge pull request #113 from CADmium-Co/cleanup
Browse files Browse the repository at this point in the history
Basic rust documentation and some cleanup
  • Loading branch information
dzervas committed Jun 26, 2024
2 parents 5650060 + 28b9b8e commit 50c8c91
Show file tree
Hide file tree
Showing 16 changed files with 446 additions and 114 deletions.
38 changes: 34 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,37 @@

version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly

- package-ecosystem: "npm"
directory: "/"
schedule:
interval: monthly
- package-ecosystem: "npm"
directory: "/applications/web"
schedule:
interval: monthly
- package-ecosystem: "npm"
directory: "/packages/shared"
schedule:
interval: monthly

- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: monthly
- package-ecosystem: "cargo"
directory: "/packages/cadmium"
schedule:
interval: monthly
- package-ecosystem: "cargo"
directory: "/packages/cadmium-macros"
schedule:
interval: monthly
- package-ecosystem: "cargo"
directory: "/applications/tauri"
schedule:
interval: monthly
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ jobs:
run: pnpm exec playwright install
- name: Run rust tests, vitest unit tests, and playwright e2e tests
run: pnpm turbo run test

- name: Generate rust docs
run: cargo doc --no-deps --package cadmium --release && mv target/doc applications/web/dist/docs
- uses: actions/upload-artifact@v4
with:
name: cadmium
Expand Down
11 changes: 11 additions & 0 deletions packages/cadmium-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "cadmium-api"
version = "0.1.0"
edition = "2021"

[dependencies]
cadmium = { path = "../cadmium" }

[build-dependencies]
convert_case = "0.6.0"
cadmium = { path = "../cadmium" }
49 changes: 49 additions & 0 deletions packages/cadmium-api/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use std::env;
use std::fs::File;
use std::io::Write;

use convert_case::{Case, Casing};

fn main() {
let mut output =
File::create(format!("{}/generated.rs", env::var("OUT_DIR").unwrap())).unwrap();

writeln!(
output,
"// This file is generated by build script, do not edit"
)
.unwrap();

let all_defs = cadmium::Project::gen_typescript_defs();

for (name, fields) in &all_defs {
let iface_fields = fields.join("; ");
let fn_name = name.to_case(Case::Camel);
let fn_params_full = fields.join(", ");
let fn_params = fn_params_full.trim_end_matches(", ");
let iface_params_full = fields
.iter()
.map(|f| f.split(":").next().unwrap().trim())
.collect::<Vec<_>>()
.join(", ");
let iface_params = iface_params_full.trim_end_matches(", ");

writeln!(
output,
r#"
pub fn {fn_name}({fn_params}) -> MessageResult {{
const message: Message = {{ type: "{name}", {iface_params} }};
return sendWasmMessage(message);
}}"#
)
.unwrap();
}

let message_variants = all_defs
.iter()
.map(|(name, _)| format!("{{ type: \"{name}\" }} & {name}"))
.collect::<Vec<_>>()
.join(" | ");

writeln!(output, "export type Message = {message_variants};").unwrap();
}
8 changes: 8 additions & 0 deletions packages/cadmium-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// #![allow(unused_imports)]
use cadmium::feature::extrusion::{Direction, Mode};
use cadmium::isketch::ISketch;
use cadmium::message::{Message, MessageResult};
use cadmium::step::StepHash;
use cadmium::IDType;

include!(concat!(env!("OUT_DIR"), "/generated.rs"));
5 changes: 5 additions & 0 deletions packages/cadmium/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum CADmiumError {
// Message errors
#[error("The project ID {0} was not found")]
ProjectIDNotFound(usize),
#[error("The workbench ID {0} was not found")]
WorkbenchIDNotFound(u64),
#[error("The workbench name {0} was not found")]
Expand All @@ -28,4 +30,7 @@ pub enum CADmiumError {

#[error("This function is not implemented yet")]
NotImplemented,

#[error(transparent)]
Other(#[from] anyhow::Error),
}
5 changes: 0 additions & 5 deletions packages/cadmium/src/feature/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ use crate::archetypes::Vector3;

use super::prelude::*;

#[derive(Tsify, Debug, Serialize, Deserialize, Clone)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[repr(transparent)]
pub struct SolidArray(pub Vec<Solid>);

#[derive(Tsify, Debug, Serialize, Deserialize, Clone)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Solid {
Expand Down
192 changes: 178 additions & 14 deletions packages/cadmium/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,79 @@
//! CADmium is a library for building parametric CAD applications in Rust.
//!
//! Its main target is the web, as a WASM module, but it can also be used in native applications.
//!
//! The library is mostly an interface and interoperability layer for the [`ISOtope`](https://github.com/CADmium-Co/ISOtope)
//! 2D constraint solver and the [`Truck`](https://github.com/ricosjp/truck) CAD kernel.
//!
//! It is designed to be used in a functional way, where the state
//! of the application is stored in a single data structure in the global memory.
//!
//! ## Architecture
//!
//! The high-level architecture is as follows (tree-like, breadth-first):
//!
//! - A [`Project`] is the main data structure that holds every kind of
//! information about the project - mainly an array of [`Workbench`]es.
//! - A [`Workbench`] holds both the history and the internal state of the
//! result of the operations in the project.
//! The following arrays of structs are held:
//! - A [`Step`] is a single operation that takes place in a [`Workbench`].
//! Comprised of a [`Message`], its [`StepHash`] and its [`StepResult`],
//! an array of steps is also known as the history of the workbench.
//! - A [`Point3`] represents a point in 3D space. In the context of direct
//! [`Workbench`] descendant, it's a free-standing point, not part of a sketch
//! or solid.
//! - A [`Plane`] is a 2D plane that can be used to create sketches.
//! - An [`ISketch`] is a 2D sketch that can be used to create 3D models.
//! It holds an ISOtope `Sketch` and a list of [`Compound`]s (a way to
//! describe complex 2D shapes using a set of ISOtope primitives and constraints).
//! - A [`Feature`] is a 3D operation that mostly results in a 3D object -
//! either by creating or modifying it. For example, an [`Extrusion`] is a feature.
//!
//! ## Usage
//! The way to interact with CADmium is through messages. A message is a single,
//! pre-defined operation that can be applied to a project. For example, a message
//! could be `ProjectRename` or `FeatureExtrusionAdd`, both variants of the
//! [`Message`] enum.
//!
// TODO: Give a better example (e.g. a simple sketch and extrusion)
//! ```rust
//! use cadmium::{create_project, get_project, send_message};
//! use cadmium::message::Message;
//! use cadmium::project::ProjectRename;
//!
//! let project_id = create_project("My Project");
//! let mut project = get_project(project_id).unwrap();
//! let message = Message::ProjectRename(ProjectRename { new_name: "New Name".to_string() });
//! let result = send_message(project_id, &message);
//! assert!(result.success);
//!
//! let project = get_project(project_id).unwrap();
//! assert_eq!(project.name, "New Name");
//! ```
//!
//! ## WASM Usage
//!
//! CADmium is designed to be used in the browser as a WebAssembly module.
//! It can be compiled with `wasm-pack` and automatically produces a TypeScript
//! definition file that can be used in a web application.
// TODO: Add a javascript example
//!
//! [`Compound`]: crate::isketch::compound::Compound
//! [`Extrusion`]: crate::feature::extrusion::Extrusion
//! [`Feature`]: crate::feature::Feature
//! [`ISketch`]: crate::isketch::ISketch
//! [`Point3`]: crate::feature::point::Point3
//! [`Plane`]: crate::archetypes::Plane
//! [`Step`]: crate::step::Step
//! [`StepHash`]: crate::step::StepHash
//! [`StepResult`]: crate::step::StepResult
//! [`Workbench`]: crate::workbench::Workbench

use std::cell::RefCell;
use std::collections::BTreeMap;

use feature::solid::SolidArray;
use error::CADmiumError;
use message::{Message, MessageResult};
use step::StepHash;
use tsify_next::declare;
Expand All @@ -17,14 +89,118 @@ pub mod project;
pub mod step;
pub mod workbench;

/// The primary type used to describe an internal ID in CADmium
///
/// Could be an index to a vector, a key in a map, etc.
#[declare]
pub type IDType = u64;

thread_local! {
// TODO: This is a bad solution to the hash <-> crate-internal-ID mapping problem
/// This is a global map to keep track of hashes against local IDs
/// The hash is the unique identifier for a step
/// The ID could be any kind of ID, e.g. a isotope sketch primitive
///
/// <div class="warning">
///
/// Using this map in reverse (from local ID to hash) requires manual check logic
/// (that the resulting hash is a step of the correct type, points to the correct parent, etc.)
///
/// </div>
static ID_MAP: RefCell<BTreeMap<StepHash, IDType>> = RefCell::new(BTreeMap::new());

/// Global project list - this is the preferred way to store & access projects
static PROJECTS: RefCell<Vec<project::Project>> = RefCell::new(Vec::new());
}

/// Creates a new [`Project`](project::Project) and returns the index of the project in the global project list
///
/// # Examples
///
/// ```rust
/// use cadmium::{create_project, get_project};
///
/// let project_id = create_project("My Project");
/// let project = get_project(project_id).unwrap();
///
/// assert_eq!(project.name, "My Project");
/// ```
#[wasm_bindgen]
pub fn create_project(name: &str) -> usize {
let p = project::Project::new(name);
PROJECTS.with(|projects_ref| {
let mut projects = projects_ref.borrow_mut();
projects.push(p);
projects.len() - 1
})
}

/// Returns a concrete [`Project`](project::Project) from the global project list.
///
/// A new project can be created with [`create_project`] function.
#[wasm_bindgen]
pub fn get_project(project_index: usize) -> Result<project::Project, String> {
PROJECTS.with(|projects_ref| {
let projects = projects_ref.borrow();
Ok(projects
.get(project_index)
.ok_or(CADmiumError::ProjectIDNotFound(project_index).to_string())?
.clone())
})
}

/// Sends a message to a [`Project`](project::Project) and returns the result
///
/// [`Message`]s are the primary way to interact with CADmium.
/// They describe any kind of action that can be taken on a project.
///
/// # Examples
///
/// ```rust
/// use cadmium::{create_project, get_project, send_message};
/// use cadmium::message::Message;
/// use cadmium::project::ProjectRename;
///
/// let project_id = create_project("My Project");
/// let message = Message::ProjectRename(ProjectRename { new_name: "New Name".to_string() });
/// let result = send_message(project_id, &message);
/// assert!(result.success);
///
/// let project = get_project(project_id).unwrap();
/// assert_eq!(project.name, "New Name");
/// ```
#[wasm_bindgen]
pub fn send_message(project_index: usize, message: &Message) -> MessageResult {
PROJECTS.with(|projects_ref| {
let mut projects = projects_ref.borrow_mut();
let Some(mut p) = projects.get_mut(project_index as usize) else {
return CADmiumError::ProjectIDNotFound(project_index).into();
};

message.handle(&mut p).into()
})
}

/// Returns a concrete [`Workbench`](workbench::Workbench) from a [`Project`](project::Project).
#[wasm_bindgen]
pub fn get_workbench(
project_index: usize,
workbench_index: usize,
) -> Result<workbench::Workbench, String> {
PROJECTS.with(|projects_ref| {
let projects = projects_ref.borrow();
let p = projects
.get(project_index)
.ok_or(CADmiumError::ProjectIDNotFound(project_index).to_string())?;
let wb = p
.workbenches
.get(workbench_index)
.ok_or(CADmiumError::WorkbenchIDNotFound(workbench_index as u64).to_string())?
.borrow();
Ok(wb.clone())
})
}

#[derive(Debug, Clone)]
#[wasm_bindgen]
pub struct Project {
native: project::Project,
Expand Down Expand Up @@ -88,16 +264,4 @@ impl Project {
pub fn send_message(&mut self, message: &Message) -> MessageResult {
message.handle(&mut self.native).into()
}

#[wasm_bindgen]
pub fn get_workbench_solids(&self, workbench_index: u32) -> SolidArray {
SolidArray(
self.native
.workbenches
.get(workbench_index as usize)
.unwrap()
.borrow()
.get_solids(),
)
}
}
Loading

0 comments on commit 50c8c91

Please sign in to comment.