Add ocs_doc_api: object-oriented CAD entity API (v2)

New crate crates/ocs_doc_api: object-centric facade (DocApi -> Document ->
collections -> typed handles), transport-agnostic wire layer (DocApiEnvelope =
single op | read-only query batch), per-op autocommit (no write transactions),
bulk ops with upfront-validation all-or-nothing, structured ApiError, and
build-time codegen (spec/entities.toml -> ops/query enums, api_reference.md,
binding_schema.json).

ocs_plugin_api: append-only PluginRequest::DocApiRequest / PluginResponse::
DocApiResponse (serde_bytes payload) + defaulted HostApi::doc_api_dispatch.

Host: DocApiBackend impl for HostSession (src/app/doc_api.rs) mapping to scene
primitives (solid_models lift-on-miss, register_prepared_solid_model, meshes
metrics), entity conversions (doc_api_convert.rs), HostSession accessors +
doc_api_dispatch override.

Tests: 592 host lib (incl. 7 DocApi integration tests: create/boolean/query,
unknown-id, transform-many all-or-nothing, query-batch cap, 2D/geometric/DWG
roundtrips), 8 crate unit tests. Examples + README/ARCHITECTURE/IMPLEMENTATION docs.
This commit is contained in:
Sebastian committed 2026-09-06 00:15:27 +02:00
1 parent 9129c85f97
commit e41086b058
44 files changed
+4837 -12

No files matched your search

+59
View File
@@ -0,0 +1,59 @@
//! Structured error model (plan §2 decision #9). Serializable so IPC returns the
//! same error the in-process executor produced.
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::id::ObjectId;
/// Which kind of kernel failure occurred (a mapped `cadkernel` `Snag`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GeometryErrorKind {
/// The operation produced an empty / degenerate body.
Empty,
/// Boolean refused (coincident geometry, unsupported cut, …).
BooleanFailed,
/// Invalid input to a kernel constructor (e.g. non-positive radius).
InvalidInput,
/// ACIS lift/parse/lower failed.
Acis,
/// Any other kernel error (message carries detail).
Other,
}
/// The single error type for the whole API. `commit`/ops return
/// `Result<_, ApiError>`; on a failed op nothing is applied and no undo step is
/// recorded (per-op atomicity).
#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ApiError {
/// Input failed validation before any mutation (names the op + reason).
#[error("validation failed on {op}: {reason}")]
Validation { op: String, reason: String },
/// The geometry kernel reported a failure (a mapped `Snag`).
#[error("geometry kernel: {kind:?}: {msg}")]
Geometry { kind: GeometryErrorKind, msg: String },
/// An `ObjectId` does not resolve to a live entity (stale/deleted/never existed).
#[error("unknown ObjectId {0:?}")]
UnknownId(ObjectId),
/// The requested capability is not supported (e.g. over-cap bulk op, unimplemented family).
#[error("unsupported capability: {0}")]
Unsupported(String),
/// The transport (IPC / channel) failed — disconnected, oversized, timeout.
#[error("transport: {0}")]
Transport(String),
}
pub type ApiResult<T> = Result<T, ApiError>;
impl ApiError {
pub fn validation(op: &'static str, reason: impl Into<String>) -> Self {
ApiError::Validation { op: op.to_string(), reason: reason.into() }
}
pub fn geometry(kind: GeometryErrorKind, msg: impl Into<String>) -> Self {
ApiError::Geometry { kind, msg: msg.into() }
}
}