feat: node graph overlay; move to opencadcodec/opencadkernel/opencadgraph

The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.

A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-09-25 07:34:23 +03:00
1 parent 56c04408d6
commit f537aff75c
430 files changed
+9925 -8540

No files matched your search

+166 -166
View File
@@ -23,7 +23,7 @@ pub fn entity_snapshot(
}
#[cfg(feature = "host")]
fn finite_vector(name: &str, value: &crate::host::acadrust::types::Vector3) -> Result<(), String> {
fn finite_vector(name: &str, value: &crate::host::codec::types::Vector3) -> Result<(), String> {
if value.x.is_finite() && value.y.is_finite() && value.z.is_finite() {
Ok(())
} else {
@@ -32,7 +32,7 @@ fn finite_vector(name: &str, value: &crate::host::acadrust::types::Vector3) -> R
}
#[cfg(feature = "host")]
fn unit_direction(name: &str, value: &crate::host::acadrust::types::Vector3) -> Result<(), String> {
fn unit_direction(name: &str, value: &crate::host::codec::types::Vector3) -> Result<(), String> {
finite_vector(name, value)?;
let length = value.x.hypot(value.y).hypot(value.z);
if (length - 1.0).abs() <= 1e-6 {
@@ -43,7 +43,7 @@ fn unit_direction(name: &str, value: &crate::host::acadrust::types::Vector3) ->
}
#[cfg(feature = "host")]
fn solid_normal(name: &str, value: &crate::host::acadrust::types::Vector3) -> Result<(), String> {
fn solid_normal(name: &str, value: &crate::host::codec::types::Vector3) -> Result<(), String> {
finite_vector(name, value)?;
if value.x.hypot(value.y).hypot(value.z) > 0.0 {
Ok(())
@@ -62,9 +62,9 @@ fn insert_scale(name: &str, value: f64) -> Result<(), String> {
}
#[cfg(feature = "host")]
fn validate_hatch_paths(paths: &[crate::host::acadrust::entities::BoundaryPath]) -> Result<(), String> {
use crate::host::acadrust::entities::BoundaryEdge;
use crate::host::acadrust::types::Vector2;
fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> Result<(), String> {
use crate::host::codec::entities::BoundaryEdge;
use crate::host::codec::types::Vector2;
let close = |a: Vector2, b: Vector2| (a.x - b.x).hypot(a.y - b.y) <= 1e-8;
let ellipse_point = |center: Vector2, major: Vector2, ratio: f64, angle: f64| {
Vector2::new(
@@ -200,7 +200,7 @@ fn validate_hatch_paths(paths: &[crate::host::acadrust::entities::BoundaryPath])
}
#[cfg(feature = "host")]
fn validate_hatch_pattern(value: &crate::host::acadrust::entities::Hatch) -> Result<(), String> {
fn validate_hatch_pattern(value: &crate::host::codec::entities::Hatch) -> Result<(), String> {
if value.pattern.name.trim().is_empty() {
return Err("Hatch.pattern.name is empty".into());
}
@@ -235,12 +235,12 @@ fn validate_hatch_pattern(value: &crate::host::acadrust::entities::Hatch) -> Res
/// checks to fields the edit touched so legacy DWG leaders stay editable.
#[cfg(feature = "host")]
fn validate_leader(
old: Option<&crate::host::acadrust::entities::Leader>,
new: &crate::host::acadrust::entities::Leader,
old: Option<&crate::host::codec::entities::Leader>,
new: &crate::host::codec::entities::Leader,
) -> Result<(), String> {
use crate::host::acadrust::entities::LeaderCreationType;
let vec_changed = |a: &crate::host::acadrust::types::Vector3,
b: &crate::host::acadrust::types::Vector3| {
use crate::host::codec::entities::LeaderCreationType;
let vec_changed = |a: &crate::host::codec::types::Vector3,
b: &crate::host::codec::types::Vector3| {
old.is_none()
|| a.x.to_bits() != b.x.to_bits()
|| a.y.to_bits() != b.y.to_bits()
@@ -323,8 +323,8 @@ fn validate_leader(
/// finiteness is checked here.
#[cfg(feature = "host")]
fn validate_mline(
old: Option<&crate::host::acadrust::entities::MLine>,
new: &crate::host::acadrust::entities::MLine,
old: Option<&crate::host::codec::entities::MLine>,
new: &crate::host::codec::entities::MLine,
) -> Result<(), String> {
if old.is_none_or(|old| old.flags != new.flags) && new.flags.bits() & !0x0f != 0 {
return Err("MLine.flags has unknown bits".into());
@@ -369,7 +369,7 @@ fn validate_mline(
return Err("MLine.vertices must not all coincide".into());
}
}
if new.flags.contains(crate::host::acadrust::entities::MLineFlags::CLOSED)
if new.flags.contains(crate::host::codec::entities::MLineFlags::CLOSED)
&& new.vertices.len() < 3
{
return Err("closed MLine requires at least 3 vertices".into());
@@ -382,10 +382,10 @@ fn validate_mline(
/// validated as inputs; the measurement the geometry implies must be finite.
#[cfg(feature = "host")]
fn validate_dimension(
old: Option<&crate::host::acadrust::entities::Dimension>,
new: &crate::host::acadrust::entities::Dimension,
old: Option<&crate::host::codec::entities::Dimension>,
new: &crate::host::codec::entities::Dimension,
) -> Result<(), String> {
use crate::host::acadrust::entities::Dimension as D;
use crate::host::codec::entities::Dimension as D;
if old == Some(new) {
return Ok(());
}
@@ -398,7 +398,7 @@ fn validate_dimension(
if base.style_name.trim().is_empty() {
return Err("Dimension.style_name is empty".into());
}
let (points, scalars, distinct): (Vec<(&str, &crate::host::acadrust::types::Vector3)>, Vec<(&str, f64)>, Vec<(&str, &str)>) = match new {
let (points, scalars, distinct): (Vec<(&str, &crate::host::codec::types::Vector3)>, Vec<(&str, f64)>, Vec<(&str, &str)>) = match new {
D::Aligned(d) => (
vec![("definition_point", &d.definition_point), ("first_point", &d.first_point), ("second_point", &d.second_point)],
vec![("ext_line_rotation", d.ext_line_rotation)],
@@ -468,10 +468,10 @@ fn validate_dimension(
/// Field checks shared by MultiLeader creation and mutation.
#[cfg(feature = "host")]
fn validate_multileader(
old: Option<&crate::host::acadrust::entities::MultiLeader>,
new: &crate::host::acadrust::entities::MultiLeader,
old: Option<&crate::host::codec::entities::MultiLeader>,
new: &crate::host::codec::entities::MultiLeader,
) -> Result<(), String> {
use crate::host::acadrust::entities::LeaderContentType;
use crate::host::codec::entities::LeaderContentType;
if old == Some(new) {
return Ok(());
}
@@ -551,8 +551,8 @@ fn validate_multileader(
/// Field checks shared by Table creation and mutation.
#[cfg(feature = "host")]
fn validate_table(
old: Option<&crate::host::acadrust::entities::Table>,
new: &crate::host::acadrust::entities::Table,
old: Option<&crate::host::codec::entities::Table>,
new: &crate::host::codec::entities::Table,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -625,8 +625,8 @@ fn validate_table(
/// PolygonMesh: an M x N grid; the vertex list must match exactly.
#[cfg(feature = "host")]
fn validate_polygon_mesh(
old: Option<&crate::host::acadrust::entities::PolygonMeshEntity>,
new: &crate::host::acadrust::entities::PolygonMeshEntity,
old: Option<&crate::host::codec::entities::PolygonMeshEntity>,
new: &crate::host::codec::entities::PolygonMeshEntity,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -660,8 +660,8 @@ fn validate_polygon_mesh(
/// that edge; 0 marks an unused corner).
#[cfg(feature = "host")]
fn validate_polyface_mesh(
old: Option<&crate::host::acadrust::entities::PolyfaceMesh>,
new: &crate::host::acadrust::entities::PolyfaceMesh,
old: Option<&crate::host::codec::entities::PolyfaceMesh>,
new: &crate::host::codec::entities::PolyfaceMesh,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -703,8 +703,8 @@ fn validate_polyface_mesh(
/// Mesh (subdivision): faces and edges index the vertex list (0-based).
#[cfg(feature = "host")]
fn validate_mesh(
old: Option<&crate::host::acadrust::entities::Mesh>,
new: &crate::host::acadrust::entities::Mesh,
old: Option<&crate::host::codec::entities::Mesh>,
new: &crate::host::codec::entities::Mesh,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -747,8 +747,8 @@ fn validate_mesh(
/// the parameters are validated.
#[cfg(feature = "host")]
fn validate_helix(
old: Option<&crate::host::acadrust::entities::Helix>,
new: &crate::host::acadrust::entities::Helix,
old: Option<&crate::host::codec::entities::Helix>,
new: &crate::host::codec::entities::Helix,
) -> Result<(), String> {
if old.is_some_and(|old| {
old.axis_base_point == new.axis_base_point
@@ -785,8 +785,8 @@ fn validate_helix(
/// by the host; a script supplies the file and the placement.
#[cfg(feature = "host")]
fn validate_raster_image(
old: Option<&crate::host::acadrust::entities::RasterImage>,
new: &crate::host::acadrust::entities::RasterImage,
old: Option<&crate::host::codec::entities::RasterImage>,
new: &crate::host::codec::entities::RasterImage,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -833,10 +833,10 @@ fn validate_raster_image(
/// normalized image coordinates) defines the masked area.
#[cfg(feature = "host")]
fn validate_wipeout(
old: Option<&crate::host::acadrust::entities::Wipeout>,
new: &crate::host::acadrust::entities::Wipeout,
old: Option<&crate::host::codec::entities::Wipeout>,
new: &crate::host::codec::entities::Wipeout,
) -> Result<(), String> {
use crate::host::acadrust::entities::WipeoutClipType;
use crate::host::codec::entities::WipeoutClipType;
if old == Some(new) {
return Ok(());
}
@@ -878,8 +878,8 @@ fn validate_wipeout(
/// `UnderlayDefinition`, which a script names by handle at creation.
#[cfg(feature = "host")]
fn validate_underlay(
old: Option<&crate::host::acadrust::entities::Underlay>,
new: &crate::host::acadrust::entities::Underlay,
old: Option<&crate::host::codec::entities::Underlay>,
new: &crate::host::codec::entities::Underlay,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -923,8 +923,8 @@ fn validate_underlay(
/// other references; this validates the numbers.
#[cfg(feature = "host")]
fn validate_viewport(
old: Option<&crate::host::acadrust::entities::Viewport>,
new: &crate::host::acadrust::entities::Viewport,
old: Option<&crate::host::codec::entities::Viewport>,
new: &crate::host::codec::entities::Viewport,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -952,8 +952,8 @@ fn validate_viewport(
/// ViewBorder: the frame of a drawing view, tied to a viewport and a scale.
#[cfg(feature = "host")]
fn validate_view_border(
old: Option<&crate::host::acadrust::entities::ViewBorder>,
new: &crate::host::acadrust::entities::ViewBorder,
old: Option<&crate::host::codec::entities::ViewBorder>,
new: &crate::host::codec::entities::ViewBorder,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -978,8 +978,8 @@ fn validate_view_border(
/// Light: type, placement, intensity, attenuation and shadow settings.
#[cfg(feature = "host")]
fn validate_light(
old: Option<&crate::host::acadrust::entities::Light>,
new: &crate::host::acadrust::entities::Light,
old: Option<&crate::host::codec::entities::Light>,
new: &crate::host::codec::entities::Light,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -1227,8 +1227,8 @@ fn validate_basic_entity(
/// storage is never rewritten.
#[cfg(feature = "host")]
fn validate_ole_frame(
old: Option<&crate::host::acadrust::entities::Ole2Frame>,
new: &crate::host::acadrust::entities::Ole2Frame,
old: Option<&crate::host::codec::entities::Ole2Frame>,
new: &crate::host::codec::entities::Ole2Frame,
) -> Result<(), String> {
if old == Some(new) {
return Ok(());
@@ -1249,8 +1249,8 @@ fn validate_ole_frame(
/// in the ACIS payload that the geometry kernel owns.
#[cfg(feature = "host")]
fn validate_surface(
old: Option<&crate::host::acadrust::entities::Surface>,
new: &crate::host::acadrust::entities::Surface,
old: Option<&crate::host::codec::entities::Surface>,
new: &crate::host::codec::entities::Surface,
) -> Result<(), String> {
if old.is_some_and(|old| old.u_isolines == new.u_isolines && old.v_isolines == new.v_isolines) {
return Ok(());
@@ -1268,8 +1268,8 @@ fn validate_surface(
/// derived by the host, so only the inputs are validated.
#[cfg(feature = "host")]
fn validate_section_symbol(
old: Option<&crate::host::acadrust::entities::SectionSymbol>,
new: &crate::host::acadrust::entities::SectionSymbol,
old: Option<&crate::host::codec::entities::SectionSymbol>,
new: &crate::host::codec::entities::SectionSymbol,
) -> Result<(), String> {
if old.is_some_and(|old| old.points == new.points && old.symbol_scale.to_bits() == new.symbol_scale.to_bits()) {
return Ok(());
@@ -1553,8 +1553,8 @@ pub fn validate_entity_mutation(
Err(format!("{name} must contain finite coordinates"))
}
};
let same3 = |a: &crate::host::acadrust::types::Vector3,
b: &crate::host::acadrust::types::Vector3| {
let same3 = |a: &crate::host::codec::types::Vector3,
b: &crate::host::codec::types::Vector3| {
a.x.to_bits() == b.x.to_bits()
&& a.y.to_bits() == b.y.to_bits()
&& a.z.to_bits() == b.z.to_bits()
@@ -1567,8 +1567,8 @@ pub fn validate_entity_mutation(
}
};
let changed3 = |name: &str,
old: &crate::host::acadrust::types::Vector3,
new: &crate::host::acadrust::types::Vector3| {
old: &crate::host::codec::types::Vector3,
new: &crate::host::codec::types::Vector3| {
if !same3(old, new) {
finite_vector(name, new)
} else {
@@ -2060,7 +2060,7 @@ pub fn validate_canvas_entity_references(
// An edit that keeps the block name is left alone so a legacy insert of a
// missing block stays editable; creation and renames must name a real,
// ordinary block that does not create a cycle.
let insert_is_unchanged_edit = |value: &crate::host::acadrust::entities::Insert| {
let insert_is_unchanged_edit = |value: &crate::host::codec::entities::Insert| {
matches!(document.get_entity(value.common.handle),
Some(EntityType::Insert(existing)) if existing.block_name == value.block_name)
};
@@ -2105,7 +2105,7 @@ pub fn validate_canvas_entity_references(
}
}
if let EntityType::SectionSymbol(value) = entity {
use crate::host::acadrust::objects::{ClassObjectData, ObjectType};
use crate::host::codec::objects::{ClassObjectData, ObjectType};
for (name, handle, wanted) in [
("style", value.style_handle, "SectionViewStyle"),
("view representation", value.view_rep_handle, "ViewRep"),
@@ -2132,7 +2132,7 @@ pub fn validate_canvas_entity_references(
}
if !matches!(
document.objects.get(&value.scale_handle),
Some(crate::host::acadrust::objects::ObjectType::Scale(_))
Some(crate::host::codec::objects::ObjectType::Scale(_))
) {
return Err(format!("ViewBorder scale {:?} does not exist", value.scale_handle));
}
@@ -2155,7 +2155,7 @@ pub fn validate_canvas_entity_references(
}
if let EntityType::Underlay(value) = entity {
match document.objects.get(&value.definition_handle) {
Some(crate::host::acadrust::objects::ObjectType::UnderlayDefinition(definition)) => {
Some(crate::host::codec::objects::ObjectType::UnderlayDefinition(definition)) => {
if definition.underlay_type != value.underlay_type {
return Err(format!(
"Underlay type {:?} does not match its definition ({:?})",
@@ -2175,7 +2175,7 @@ pub fn validate_canvas_entity_references(
if let Some(handle) = value.definition_handle.filter(|handle| !handle.is_null()) {
if !matches!(
document.objects.get(&handle),
Some(crate::host::acadrust::objects::ObjectType::ImageDefinition(_))
Some(crate::host::codec::objects::ObjectType::ImageDefinition(_))
) {
return Err(format!("RasterImage image definition {handle:?} does not exist"));
}
@@ -2185,7 +2185,7 @@ pub fn validate_canvas_entity_references(
if let Some(handle) = value.table_style_handle.filter(|handle| !handle.is_null()) {
if !matches!(
document.objects.get(&handle),
Some(crate::host::acadrust::objects::ObjectType::TableStyle(_))
Some(crate::host::codec::objects::ObjectType::TableStyle(_))
) {
return Err(format!("Table style handle {handle:?} does not exist"));
}
@@ -2208,8 +2208,8 @@ pub fn validate_canvas_entity_references(
}
}
if let EntityType::MultiLeader(value) = entity {
use crate::host::acadrust::objects::ObjectType;
let handle_exists = |handle: Option<crate::host::acadrust::Handle>, what: &str, ok: &dyn Fn(crate::host::acadrust::Handle) -> bool| {
use crate::host::codec::objects::ObjectType;
let handle_exists = |handle: Option<crate::host::codec::Handle>, what: &str, ok: &dyn Fn(crate::host::codec::Handle) -> bool| {
match handle.filter(|handle| !handle.is_null()) {
Some(handle) if !ok(handle) => Err(format!("MultiLeader {what} handle {handle:?} does not exist")),
_ => Ok(()),
@@ -2252,7 +2252,7 @@ pub fn validate_canvas_entity_references(
}
}
if let EntityType::Leader(value) = entity {
use crate::host::acadrust::entities::LeaderCreationType;
use crate::host::codec::entities::LeaderCreationType;
if !document
.dim_styles
.iter()
@@ -2376,9 +2376,9 @@ pub fn validate_canvas_entity_references(
#[cfg(feature = "host")]
fn resolve_mline_style<'a>(
document: &'a crate::host::CadDocument,
mline: &crate::host::acadrust::entities::MLine,
) -> Option<&'a crate::host::acadrust::objects::MLineStyle> {
use crate::host::acadrust::objects::ObjectType;
mline: &crate::host::codec::entities::MLine,
) -> Option<&'a crate::host::codec::objects::MLineStyle> {
use crate::host::codec::objects::ObjectType;
mline
.style_handle
.filter(|handle| !handle.is_null())
@@ -2980,18 +2980,18 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn tolerance_geometry_and_style_references_are_validated() {
use crate::host::acadrust::{self, types::Vector3};
let document = acadrust::CadDocument::new();
let mut tolerance = acadrust::entities::Tolerance::with_text(
use crate::host::codec::{self, types::Vector3};
let document = codec::CadDocument::new();
let mut tolerance = codec::entities::Tolerance::with_text(
Vector3::new(1.0, 2.0, 0.0),
"{\\Fgdt;p}%%v0.1",
);
let entity = acadrust::EntityType::Tolerance(tolerance.clone());
let entity = codec::EntityType::Tolerance(tolerance.clone());
validate_new_canvas_entity(&entity).unwrap();
validate_canvas_entity_references(&document, &entity).unwrap();
tolerance.direction = Vector3::new(2.0, 0.0, 0.0);
assert!(
validate_new_canvas_entity(&acadrust::EntityType::Tolerance(tolerance.clone()))
validate_new_canvas_entity(&codec::EntityType::Tolerance(tolerance.clone()))
.unwrap_err()
.contains("unit vector")
);
@@ -2999,7 +2999,7 @@ mod tests {
tolerance.dimension_style_name = "Missing".into();
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::Tolerance(tolerance)
&codec::EntityType::Tolerance(tolerance)
)
.unwrap_err()
.contains("does not exist"));
@@ -3008,18 +3008,18 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn leader_geometry_and_annotation_references_are_validated() {
use crate::host::acadrust::{self, entities::LeaderCreationType, types::Vector3};
let mut document = acadrust::CadDocument::new();
let mut text = acadrust::entities::Text::new();
use crate::host::codec::{self, entities::LeaderCreationType, types::Vector3};
let mut document = codec::CadDocument::new();
let mut text = codec::entities::Text::new();
text.common.handle = document.allocate_handle();
let text_handle = text.common.handle;
document.add_entity(acadrust::EntityType::Text(text)).unwrap();
let mut leader = acadrust::entities::Leader::two_point(
document.add_entity(codec::EntityType::Text(text)).unwrap();
let mut leader = codec::entities::Leader::two_point(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 5.0, 0.0),
);
leader.annotation_handle = text_handle;
let entity = acadrust::EntityType::Leader(leader.clone());
let entity = codec::EntityType::Leader(leader.clone());
validate_new_canvas_entity(&entity).unwrap();
validate_canvas_entity_references(&document, &entity).unwrap();
@@ -3027,33 +3027,33 @@ mod tests {
wrong_kind.creation_type = LeaderCreationType::WithTolerance;
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::Leader(wrong_kind)
&codec::EntityType::Leader(wrong_kind)
)
.unwrap_err()
.contains("does not match"));
let mut missing = leader.clone();
missing.annotation_handle = acadrust::Handle::new(0xdead);
missing.annotation_handle = codec::Handle::new(0xdead);
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::Leader(missing)
&codec::EntityType::Leader(missing)
)
.unwrap_err()
.contains("does not exist"));
let mut none_with_handle = leader.clone();
none_with_handle.creation_type = LeaderCreationType::NoAnnotation;
assert!(validate_new_canvas_entity(&acadrust::EntityType::Leader(none_with_handle))
assert!(validate_new_canvas_entity(&codec::EntityType::Leader(none_with_handle))
.unwrap_err()
.contains("no annotation"));
let mut one_point = leader.clone();
one_point.vertices.truncate(1);
assert!(validate_new_canvas_entity(&acadrust::EntityType::Leader(one_point))
assert!(validate_new_canvas_entity(&codec::EntityType::Leader(one_point))
.unwrap_err()
.contains("at least 2"));
let mut bad_style = leader.clone();
bad_style.dimension_style = "Missing".into();
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::Leader(bad_style)
&codec::EntityType::Leader(bad_style)
)
.unwrap_err()
.contains("dimension style"));
@@ -3061,8 +3061,8 @@ mod tests {
let mut edited = leader.clone();
edited.vertices[1] = Vector3::new(f64::NAN, 0.0, 0.0);
assert!(validate_entity_mutation(
&acadrust::EntityType::Leader(leader.clone()),
&acadrust::EntityType::Leader(edited)
&codec::EntityType::Leader(leader.clone()),
&codec::EntityType::Leader(edited)
)
.unwrap_err()
.contains("finite"));
@@ -3071,48 +3071,48 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn mline_geometry_and_style_references_are_validated_and_bound() {
use crate::host::acadrust::{self, entities::MLineVertex, types::Vector3};
let document = acadrust::CadDocument::new();
let mut mline = acadrust::entities::MLine::new();
use crate::host::codec::{self, entities::MLineVertex, types::Vector3};
let document = codec::CadDocument::new();
let mut mline = codec::entities::MLine::new();
for x in [0.0, 5.0] {
mline.vertices.push(MLineVertex::new(Vector3::new(x, 0.0, 0.0)));
}
let mut entity = acadrust::EntityType::MLine(mline.clone());
let mut entity = codec::EntityType::MLine(mline.clone());
validate_new_canvas_entity(&entity).unwrap();
bind_canvas_entity_references(&document, &mut entity).unwrap();
assert!(matches!(&entity, acadrust::EntityType::MLine(value) if value.style_handle.is_some()));
assert!(matches!(&entity, codec::EntityType::MLine(value) if value.style_handle.is_some()));
let mut missing = mline.clone();
missing.style_name = "Missing".into();
assert!(bind_canvas_entity_references(&document, &mut acadrust::EntityType::MLine(missing))
assert!(bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(missing))
.unwrap_err().contains("does not exist"));
let mut wrong_segments = mline.clone();
wrong_segments.vertices[0].init_segments(3);
assert!(bind_canvas_entity_references(&document, &mut acadrust::EntityType::MLine(wrong_segments))
assert!(bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(wrong_segments))
.unwrap_err().contains("segments but style"));
let mut one = mline.clone();
one.vertices.truncate(1);
assert!(validate_new_canvas_entity(&acadrust::EntityType::MLine(one)).unwrap_err().contains("at least 2"));
assert!(validate_new_canvas_entity(&codec::EntityType::MLine(one)).unwrap_err().contains("at least 2"));
let mut closed = mline.clone();
closed.flags |= acadrust::entities::MLineFlags::CLOSED;
assert!(validate_new_canvas_entity(&acadrust::EntityType::MLine(closed)).unwrap_err().contains("at least 3"));
closed.flags |= codec::entities::MLineFlags::CLOSED;
assert!(validate_new_canvas_entity(&codec::EntityType::MLine(closed)).unwrap_err().contains("at least 3"));
let mut scale = mline.clone();
scale.scale_factor = 0.0;
assert!(validate_new_canvas_entity(&acadrust::EntityType::MLine(scale)).unwrap_err().contains("nonzero"));
assert!(validate_new_canvas_entity(&codec::EntityType::MLine(scale)).unwrap_err().contains("nonzero"));
}
#[cfg(feature = "host")]
#[test]
fn shape_geometry_and_shape_file_style_are_validated_and_bound() {
use crate::host::acadrust::{self, tables::TableEntry, types::Vector3};
let mut document = acadrust::CadDocument::new();
let mut style = acadrust::tables::TextStyle::new("Symbols");
use crate::host::codec::{self, tables::TableEntry, types::Vector3};
let mut document = codec::CadDocument::new();
let mut style = codec::tables::TextStyle::new("Symbols");
style.set_handle(document.allocate_handle());
style.is_shape_file = true;
style.font_file = "symbols.shx".into();
let style_handle = style.handle;
document.text_styles.add(style).unwrap();
let mut shape = acadrust::entities::Shape::with_style(
let mut shape = codec::entities::Shape::with_style(
Vector3::new(1.0, 2.0, 0.0),
"ARROW",
"Symbols",
@@ -3120,17 +3120,17 @@ mod tests {
0.5,
);
shape.shape_number = 1;
let mut entity = acadrust::EntityType::Shape(shape);
let mut entity = codec::EntityType::Shape(shape);
validate_new_canvas_entity(&entity).unwrap();
bind_canvas_entity_references(&document, &mut entity).unwrap();
assert!(matches!(entity, acadrust::EntityType::Shape(ref value)
assert!(matches!(entity, codec::EntityType::Shape(ref value)
if value.style_handle == Some(style_handle)));
let acadrust::EntityType::Shape(mut bad) = entity else {
let codec::EntityType::Shape(mut bad) = entity else {
unreachable!()
};
bad.size = 0.0;
assert!(
validate_new_canvas_entity(&acadrust::EntityType::Shape(bad))
validate_new_canvas_entity(&codec::EntityType::Shape(bad))
.unwrap_err()
.contains("greater than zero")
);
@@ -3204,8 +3204,8 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn unsupported_canvas_kind_has_typed_snapshot_fields() {
use crate::host::acadrust;
let hatch = acadrust::EntityType::Hatch(acadrust::entities::Hatch::default());
use crate::host::codec;
let hatch = codec::EntityType::Hatch(codec::entities::Hatch::default());
let snapshot = entity_snapshot(&hatch).unwrap();
let fields = snapshot
.get("Hatch")
@@ -3218,21 +3218,21 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn changed_geometry_is_validated_without_rejecting_untouched_legacy_values() {
use crate::host::acadrust;
let mut before = acadrust::entities::Circle::default();
use crate::host::codec;
let mut before = codec::entities::Circle::default();
before.radius = f64::NAN;
let mut layer_only = before.clone();
layer_only.common.layer = "OTHER".into();
assert!(validate_entity_mutation(
&acadrust::EntityType::Circle(before.clone()),
&acadrust::EntityType::Circle(layer_only),
&codec::EntityType::Circle(before.clone()),
&codec::EntityType::Circle(layer_only),
)
.is_ok());
let mut bad_radius = before.clone();
bad_radius.radius = -1.0;
assert!(validate_entity_mutation(
&acadrust::EntityType::Circle(before),
&acadrust::EntityType::Circle(bad_radius),
&codec::EntityType::Circle(before),
&codec::EntityType::Circle(bad_radius),
)
.unwrap_err()
.contains("Circle.radius"));
@@ -3241,29 +3241,29 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn new_phase_seven_shapes_reject_invalid_geometry_and_keep_legacy_fields() {
use crate::host::acadrust::{self, types::Vector3};
let mut ray = acadrust::entities::Ray::default();
use crate::host::codec::{self, types::Vector3};
let mut ray = codec::entities::Ray::default();
ray.direction = Vector3::new(2.0, 0.0, 0.0);
assert!(
validate_new_canvas_entity(&acadrust::EntityType::Ray(ray.clone()))
validate_new_canvas_entity(&codec::EntityType::Ray(ray.clone()))
.unwrap_err()
.contains("Ray.direction")
);
let before = acadrust::EntityType::Ray(ray.clone());
let before = codec::EntityType::Ray(ray.clone());
ray.common.layer = "CONSTRUCTION".into();
assert!(validate_entity_mutation(&before, &acadrust::EntityType::Ray(ray.clone())).is_ok());
assert!(validate_entity_mutation(&before, &codec::EntityType::Ray(ray.clone())).is_ok());
ray.direction = Vector3::new(0.0, 1.0, 0.0);
assert!(validate_entity_mutation(&before, &acadrust::EntityType::Ray(ray)).is_ok());
assert!(validate_entity_mutation(&before, &codec::EntityType::Ray(ray)).is_ok());
let mut xline = acadrust::entities::XLine::default();
let mut xline = codec::entities::XLine::default();
xline.base_point.x = f64::NAN;
assert!(
validate_new_canvas_entity(&acadrust::EntityType::XLine(xline))
validate_new_canvas_entity(&codec::EntityType::XLine(xline))
.unwrap_err()
.contains("XLine.base_point")
);
let solid = acadrust::entities::Solid::new(
let solid = codec::entities::Solid::new(
Vector3::ZERO,
Vector3::UNIT_X,
Vector3::UNIT_Y,
@@ -3272,23 +3272,23 @@ mod tests {
let mut changed = solid.clone();
changed.thickness = f64::INFINITY;
assert!(validate_entity_mutation(
&acadrust::EntityType::Solid(solid),
&acadrust::EntityType::Solid(changed)
&codec::EntityType::Solid(solid),
&codec::EntityType::Solid(changed)
)
.unwrap_err()
.contains("Solid.thickness"));
let face = acadrust::entities::Face3D::new(
let face = codec::entities::Face3D::new(
Vector3::ZERO,
Vector3::UNIT_X,
Vector3::UNIT_Y,
Vector3::ZERO,
);
let mut changed = face.clone();
changed.invisible_edges = acadrust::entities::InvisibleEdgeFlags::from_bits(0x10);
changed.invisible_edges = codec::entities::InvisibleEdgeFlags::from_bits(0x10);
assert!(validate_entity_mutation(
&acadrust::EntityType::Face3D(face),
&acadrust::EntityType::Face3D(changed)
&codec::EntityType::Face3D(face),
&codec::EntityType::Face3D(changed)
)
.unwrap_err()
.contains("Face3D.invisible_edges"));
@@ -3297,37 +3297,37 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn insert_transaction_checks_transforms_and_protects_references() {
use crate::host::acadrust::{self, types::Vector3};
let before = acadrust::entities::Insert::new("DOOR", Vector3::new(1.0, 2.0, 0.0));
use crate::host::codec::{self, types::Vector3};
let before = codec::entities::Insert::new("DOOR", Vector3::new(1.0, 2.0, 0.0));
let mut moved = before.clone();
moved.insert_point = Vector3::new(5.0, 6.0, 0.0);
moved.set_x_scale(2.0);
assert!(validate_entity_mutation(
&acadrust::EntityType::Insert(before.clone()),
&acadrust::EntityType::Insert(moved)
&codec::EntityType::Insert(before.clone()),
&codec::EntityType::Insert(moved)
)
.is_ok());
let mut bad = before.clone();
bad.set_y_scale(f64::NAN);
assert!(validate_entity_mutation(
&acadrust::EntityType::Insert(before.clone()),
&acadrust::EntityType::Insert(bad)
&codec::EntityType::Insert(before.clone()),
&codec::EntityType::Insert(bad)
)
.unwrap_err()
.contains("Insert.y_scale"));
let mut bad = before.clone();
bad.column_count = 0;
assert!(validate_entity_mutation(
&acadrust::EntityType::Insert(before.clone()),
&acadrust::EntityType::Insert(bad)
&codec::EntityType::Insert(before.clone()),
&codec::EntityType::Insert(bad)
)
.unwrap_err()
.contains("array counts"));
let mut bad = before.clone();
bad.block_name = "OTHER".into();
assert!(validate_entity_mutation(
&acadrust::EntityType::Insert(before),
&acadrust::EntityType::Insert(bad)
&codec::EntityType::Insert(before),
&codec::EntityType::Insert(bad)
)
.unwrap_err()
.contains("block identity"));
@@ -3336,8 +3336,8 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn canvas_layer_patch_preserves_unmapped_hatch_fields() {
use crate::host::acadrust;
let hatch = acadrust::EntityType::Hatch(acadrust::entities::Hatch::default());
use crate::host::codec;
let hatch = codec::EntityType::Hatch(codec::entities::Hatch::default());
let patched = patch_canvas_layer(&hatch, "HATCHES").unwrap();
assert_eq!(patched.common().layer, "HATCHES");
assert_eq!(hatch.common().layer, "0");
@@ -3350,17 +3350,17 @@ mod tests {
#[cfg(feature = "host")]
#[test]
fn attribute_definition_requires_a_block_owner_and_text_style() {
use crate::host::acadrust::{
use crate::host::codec::{
self,
entities::{Block, BlockEnd},
types::{Handle, Vector3},
};
let mut document = acadrust::CadDocument::new();
let mut document = codec::CadDocument::new();
let next = document.next_handle();
let record_handle = Handle::new(next);
let block_handle = Handle::new(next + 1);
let end_handle = Handle::new(next + 2);
let mut record = acadrust::tables::BlockRecord::new("TAGBLOCK");
let mut record = codec::tables::BlockRecord::new("TAGBLOCK");
record.handle = record_handle;
record.block_entity_handle = block_handle;
record.block_end_handle = end_handle;
@@ -3369,30 +3369,30 @@ mod tests {
block.common.handle = block_handle;
block.common.owner_handle = record_handle;
document
.add_entity(acadrust::EntityType::Block(block))
.add_entity(codec::EntityType::Block(block))
.unwrap();
let mut end = BlockEnd::new();
end.common.handle = end_handle;
end.common.owner_handle = record_handle;
document
.add_entity(acadrust::EntityType::BlockEnd(end))
.add_entity(codec::EntityType::BlockEnd(end))
.unwrap();
let mut definition = acadrust::entities::AttributeDefinition::new(
let mut definition = codec::entities::AttributeDefinition::new(
"PART_NO".into(),
"Part number".into(),
"PN-001".into(),
);
definition.common.owner_handle = record_handle;
definition.insertion_point = Vector3::new(1.0, 2.0, 0.0);
let entity = acadrust::EntityType::AttributeDefinition(definition.clone());
let entity = codec::EntityType::AttributeDefinition(definition.clone());
validate_new_canvas_entity(&entity).unwrap();
validate_canvas_entity_references(&document, &entity).unwrap();
definition.common.owner_handle = Handle::NULL;
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::AttributeDefinition(definition.clone())
&codec::EntityType::AttributeDefinition(definition.clone())
)
.unwrap_err()
.contains("block-record owner"));
@@ -3400,22 +3400,22 @@ mod tests {
definition.text_style = "Missing".into();
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::AttributeDefinition(definition.clone())
&codec::EntityType::AttributeDefinition(definition.clone())
)
.unwrap_err()
.contains("does not exist"));
definition.text_style = "Standard".into();
definition.tag = "BAD TAG".into();
assert!(
validate_new_canvas_entity(&acadrust::EntityType::AttributeDefinition(definition))
validate_new_canvas_entity(&codec::EntityType::AttributeDefinition(definition))
.unwrap_err()
.contains("no whitespace")
);
}
#[test]
fn hatch_geometry_and_boundaries_are_validated() {
use acadrust::entities::hatch::{BoundaryEdge, BoundaryPath, LineEdge};
let mut hatch = acadrust::entities::Hatch::new();
use codec::entities::hatch::{BoundaryEdge, BoundaryPath, LineEdge};
let mut hatch = codec::entities::Hatch::new();
let mut path = BoundaryPath::new();
for (start, end) in [
((0.0, 0.0), (10.0, 0.0)),
@@ -3424,32 +3424,32 @@ mod tests {
((0.0, 10.0), (0.0, 0.0)),
] {
path.edges.push(BoundaryEdge::Line(LineEdge {
start: acadrust::types::Vector2::new(start.0, start.1),
end: acadrust::types::Vector2::new(end.0, end.1),
start: codec::types::Vector2::new(start.0, start.1),
end: codec::types::Vector2::new(end.0, end.1),
}));
}
hatch.paths.push(path);
let entity = acadrust::EntityType::Hatch(hatch.clone());
let entity = codec::EntityType::Hatch(hatch.clone());
validate_new_canvas_entity(&entity).unwrap();
let mut invalid = hatch.clone();
invalid.paths.clear();
assert!(validate_new_canvas_entity(&acadrust::EntityType::Hatch(invalid))
assert!(validate_new_canvas_entity(&codec::EntityType::Hatch(invalid))
.unwrap_err()
.contains("at least one boundary path"));
let mut invalid = hatch.clone();
invalid.is_solid = false;
invalid.pattern_scale = 0.0;
assert!(validate_new_canvas_entity(&acadrust::EntityType::Hatch(invalid))
assert!(validate_new_canvas_entity(&codec::EntityType::Hatch(invalid))
.unwrap_err()
.contains("greater than zero"));
let mut document = acadrust::CadDocument::new();
let boundary = document.add_entity(acadrust::EntityType::Line(
acadrust::entities::Line::from_points(
acadrust::types::Vector3::ZERO,
acadrust::types::Vector3::UNIT_X,
let mut document = codec::CadDocument::new();
let boundary = document.add_entity(codec::EntityType::Line(
codec::entities::Line::from_points(
codec::types::Vector3::ZERO,
codec::types::Vector3::UNIT_X,
),
)).unwrap();
let mut associative = hatch.clone();
@@ -3457,12 +3457,12 @@ mod tests {
associative.paths[0].boundary_handles.push(boundary);
validate_canvas_entity_references(
&document,
&acadrust::EntityType::Hatch(associative.clone()),
&codec::EntityType::Hatch(associative.clone()),
).unwrap();
associative.paths[0].boundary_handles[0] = acadrust::Handle::new(u64::MAX);
associative.paths[0].boundary_handles[0] = codec::Handle::new(u64::MAX);
assert!(validate_canvas_entity_references(
&document,
&acadrust::EntityType::Hatch(associative),
&codec::EntityType::Hatch(associative),
).unwrap_err().contains("does not exist"));
}
}
@@ -22,7 +22,7 @@ pub enum ModelAccess {
pub struct PropertyCoverage {
/// Name shown in the document model, or source name when unmapped.
pub name: String,
/// Field in the acadrust snapshot; `kind` is synthesized from the variant.
/// Field in the opencadcodec snapshot; `kind` is synthesized from the variant.
pub source_path: String,
pub type_id: String,
pub optional: bool,
+10 -10
View File
@@ -1,6 +1,6 @@
//! Runtime host surface (`host` feature).
//!
//! [`HostApi`] is the `acadrust`-typed adapter a plugin uses at *dispatch* time
//! [`HostApi`] is the `opencadcodec`-typed adapter a plugin uses at *dispatch* time
//! — document access, entity creation, XDATA, undo, and the command line. It is
//! the stable counterpart to the dependency-free manifest/ribbon contract: a
//! plugin's `dispatch` receives `&mut dyn HostApi` rather than the host's
@@ -19,16 +19,16 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::manifest::PluginManifest;
use crate::ribbon::CadModule;
// Re-export the acadrust crate and the types that appear in the HostApi trait
// so out-of-tree plugins can use them without adding their own acadrust
// Re-export the opencadcodec crate and the types that appear in the HostApi trait
// so out-of-tree plugins can use them without adding their own opencadcodec
// dependency (which would risk an ABI-mismatching version).
pub use acadrust;
pub use acadrust::objects::{
pub use codec;
pub use codec::objects::{
DictionaryCloningFlags, KnownXRecordKind, ProxyObjectReference, ProxyReferenceKind, XRecord,
XRecordEntry, XRecordSection, XRecordValue, XRecordValueType,
};
pub use acadrust::xdata::{ExtendedDataRecord, XDataValue};
pub use acadrust::{CadDocument, EntityType, Handle};
pub use codec::xdata::{ExtendedDataRecord, XDataValue};
pub use codec::{CadDocument, EntityType, Handle};
use crate::ipc::protocol::{PluginRequest, PluginResponse};
@@ -1016,14 +1016,14 @@ pub trait HostApi {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct LayerConfig {
pub name: String,
pub color: Option<acadrust::types::Color>,
pub color: Option<codec::types::Color>,
pub linetype: Option<String>,
pub lineweight: Option<acadrust::types::LineWeight>,
pub lineweight: Option<codec::types::LineWeight>,
pub off: Option<bool>,
pub frozen: Option<bool>,
pub locked: Option<bool>,
pub plottable: Option<bool>,
pub transparency: Option<acadrust::types::Transparency>,
pub transparency: Option<codec::types::Transparency>,
pub description: Option<String>,
}
+1 -1
View File
@@ -13,7 +13,7 @@
use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
use acadrust::CadDocument;
use codec::CadDocument;
use crate::shm::{DocumentSnapshotStore, DocumentViewDataV4, DocumentViewInfo};
+4 -4
View File
@@ -6,8 +6,8 @@ use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
use acadrust::xdata::ExtendedDataRecord;
use acadrust::{CadDocument, EntityType, Handle};
use codec::xdata::ExtendedDataRecord;
use codec::{CadDocument, EntityType, Handle};
use interprocess::local_socket::traits::Stream as StreamTrait;
use interprocess::local_socket::{GenericNamespaced, Stream, ToNsName};
@@ -589,8 +589,8 @@ mod tests {
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;
use acadrust::entities::Point;
use acadrust::{EntityType, Handle};
use codec::entities::Point;
use codec::{EntityType, Handle};
use interprocess::local_socket::{
traits::{Listener, Stream as StreamTrait},
GenericNamespaced, ListenerOptions, Stream, ToNsName,
+1 -1
View File
@@ -1,6 +1,6 @@
//! Inter-process communication layer for out-of-process plugins.
//!
//! Built only with the `host` feature because it needs `acadrust`-typed
//! Built only with the `host` feature because it needs `opencadcodec`-typed
//! messages and the plugin runner binary.
//!
//! Submodules:
+2 -2
View File
@@ -21,8 +21,8 @@ use crate::host::{CommandSource, CommandStep, HostSettingValue};
use crate::manifest::ApiVersion;
use crate::ribbon::owned::{OwnedPluginManifest, OwnedRibbonGroup};
pub use acadrust::xdata::{ExtendedDataRecord, XDataValue};
pub use acadrust::{CadDocument, EntityType, Handle};
pub use codec::xdata::{ExtendedDataRecord, XDataValue};
pub use codec::{CadDocument, EntityType, Handle};
pub use crate::host::PreviewWire;
/// Events the host forwards to an active plugin `InteractiveCommand`.
+3 -3
View File
@@ -3,17 +3,17 @@
use crate::host::HostApi;
use crate::ipc::protocol::{PluginRequest, PluginResponse};
fn addressable_document_snapshot(host: &dyn HostApi) -> acadrust::CadDocument {
fn addressable_document_snapshot(host: &dyn HostApi) -> codec::CadDocument {
let mut snapshot = host.document().clone();
let attributes = host
.document()
.entities()
.filter_map(|entity| match entity {
acadrust::EntityType::Insert(insert) => Some(insert.attributes.clone()),
codec::EntityType::Insert(insert) => Some(insert.attributes.clone()),
_ => None,
})
.flatten()
.map(acadrust::EntityType::AttributeEntity)
.map(codec::EntityType::AttributeEntity)
.collect::<Vec<_>>();
for attribute in attributes {
// Attribute entities remain canonical children of INSERT in the host
+3 -3
View File
@@ -9,8 +9,8 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{mpsc, Arc, Mutex};
use std::time::{Duration, Instant};
use acadrust::xdata::ExtendedDataRecord;
use acadrust::{CadDocument, EntityType, Handle};
use codec::xdata::ExtendedDataRecord;
use codec::{CadDocument, EntityType, Handle};
use interprocess::local_socket::traits::Stream as StreamTrait;
use interprocess::local_socket::{GenericNamespaced, Stream, ToNsName};
use interprocess::TryClone;
@@ -937,7 +937,7 @@ mod tests {
use crate::ipc::transport::recv;
use crate::ipc::v4::protocol::{HostToPluginV4, PluginToHostV4};
use crate::test_lock::ENV_LOCK;
use acadrust::entities::Point;
use codec::entities::Point;
fn unique_socket_name() -> String {
static COUNTER: AtomicU64 = AtomicU64::new(0);
+16 -16
View File
@@ -114,7 +114,7 @@ mod tests {
CommandRequest::Point { point: [1.0, 2.0, 3.0] },
CommandRequest::Text { text: "2".into() },
CommandRequest::Token { text: "R".into() },
CommandRequest::Entity { handle: acadrust::Handle::new(9), point: [0.0; 3] },
CommandRequest::Entity { handle: codec::Handle::new(9), point: [0.0; 3] },
CommandRequest::Selection,
CommandRequest::Enter,
CommandRequest::Cancel,
@@ -149,7 +149,7 @@ mod tests {
TableOperation::LayerCreate {
config: LayerConfig {
name: "Walls".into(),
color: Some(acadrust::types::Color::Index(5)),
color: Some(codec::types::Color::Index(5)),
frozen: Some(true),
description: Some("d".into()),
..Default::default()
@@ -170,7 +170,7 @@ mod tests {
TableOperation::StyleSetCurrent { kind: crate::host::TableStyleKind::Dim, name: "A".into() },
TableOperation::BlockCreate {
name: "B".into(),
entities: vec![acadrust::Handle::new(4), acadrust::Handle::new(5)],
entities: vec![codec::Handle::new(4), codec::Handle::new(5)],
base_point: [1.0, 2.0, 3.0],
erase_originals: true,
description: Some("d".into()),
@@ -180,7 +180,7 @@ mod tests {
TableOperation::BlockDelete { name: "C".into() },
TableOperation::BlockEntityAdd {
block: "C".into(),
entity: acadrust::EntityType::Line(acadrust::entities::Line::new()),
entity: codec::EntityType::Line(codec::entities::Line::new()),
},
TableOperation::LinetypeCreate { name: "L".into(), description: "d".into(), pattern: vec![1.0, -1.0] },
TableOperation::LinetypeModify { name: "L".into(), description: None, pattern: Some(vec![2.0, -2.0]) },
@@ -196,7 +196,7 @@ mod tests {
assert!(matches!(bincode::deserialize::<PluginRequest>(&bytes).unwrap(),
PluginRequest::TableOperation { operation: decoded } if decoded == operation));
}
for result in [Ok(acadrust::Handle::new(9)), Err("refused".to_owned())] {
for result in [Ok(codec::Handle::new(9)), Err("refused".to_owned())] {
let bytes = bincode::serialize(&PluginResponse::TableResult(result.clone())).unwrap();
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
PluginResponse::TableResult(decoded) if decoded == result));
@@ -211,24 +211,24 @@ mod tests {
primitive: SolidPrimitive::Pyramid { center: [1.0, 2.0, 3.0], radius: 4.0, height: 5.0, sides: 6 },
layer: Some("SOLIDS".into()),
},
SolidOperation::Transform { handle: acadrust::Handle::new(7), matrix: [1.0; 16] },
SolidOperation::RegionFromProfile { source: acadrust::Handle::new(3), layer: None, delete_source: true },
SolidOperation::Transform { handle: codec::Handle::new(7), matrix: [1.0; 16] },
SolidOperation::RegionFromProfile { source: codec::Handle::new(3), layer: None, delete_source: true },
SolidOperation::EmbedPicture { path: "p.png".into(), origin: [0.0; 3], width: 5.0, layer: None },
SolidOperation::Boolean {
first: acadrust::Handle::new(1),
second: acadrust::Handle::new(2),
first: codec::Handle::new(1),
second: codec::Handle::new(2),
operation: crate::host::SolidBoolean::Subtract,
layer: None,
keep_operands: false,
},
SolidOperation::SurfaceFromProfile { source: acadrust::Handle::new(4), layer: Some("S".into()), delete_source: false },
SolidOperation::Extrude { source: acadrust::Handle::new(5), direction: [0.0, 0.0, 2.0], layer: None, delete_source: true },
SolidOperation::SurfaceFromProfile { source: codec::Handle::new(4), layer: Some("S".into()), delete_source: false },
SolidOperation::Extrude { source: codec::Handle::new(5), direction: [0.0, 0.0, 2.0], layer: None, delete_source: true },
] {
let bytes = bincode::serialize(&PluginRequest::SolidOperation { operation: operation.clone() }).unwrap();
assert!(matches!(bincode::deserialize::<PluginRequest>(&bytes).unwrap(),
PluginRequest::SolidOperation { operation: decoded } if decoded == operation));
}
for result in [Ok(acadrust::Handle::new(9)), Err("refused".to_owned())] {
for result in [Ok(codec::Handle::new(9)), Err("refused".to_owned())] {
let bytes = bincode::serialize(&PluginResponse::SolidResult(result.clone())).unwrap();
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
PluginResponse::SolidResult(decoded) if decoded == result));
@@ -237,14 +237,14 @@ mod tests {
#[test]
fn selection_request_and_response_roundtrip() {
let request = PluginRequest::SetSelection { handles: vec![acadrust::Handle::new(9)] };
let request = PluginRequest::SetSelection { handles: vec![codec::Handle::new(9)] };
let bytes = bincode::serialize(&request).unwrap();
assert!(matches!(bincode::deserialize::<PluginRequest>(&bytes).unwrap(),
PluginRequest::SetSelection { handles } if handles == vec![acadrust::Handle::new(9)]));
let response = PluginResponse::Selection(vec![acadrust::Handle::new(9)]);
PluginRequest::SetSelection { handles } if handles == vec![codec::Handle::new(9)]));
let response = PluginResponse::Selection(vec![codec::Handle::new(9)]);
let bytes = bincode::serialize(&response).unwrap();
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
PluginResponse::Selection(handles) if handles == vec![acadrust::Handle::new(9)]));
PluginResponse::Selection(handles) if handles == vec![codec::Handle::new(9)]));
}
#[test]
+5 -5
View File
@@ -2,7 +2,7 @@
//!
//! The stable, semver-versioned contract an add-on package targets instead of
//! the `OpenCADStudio` binary internals. It is intentionally **dependency
//! free** (no `iced`, no `acadrust`) so engine crates and external tooling can
//! free** (no `iced`, no `opencadcodec`) so engine crates and external tooling can
//! depend on it cheaply.
//!
//! Two pieces live here:
@@ -13,12 +13,12 @@
//! ([`RibbonGroup`], [`ToolDef`], …) a plugin uses to describe its tab.
//!
//! The runtime host surface a plugin uses at *dispatch* time (document access,
//! command line, undo) is `acadrust`-typed and therefore lives in the `host`
//! feature; it re-exports `acadrust` so plugins do not need to depend on it
//! command line, undo) is `opencadcodec`-typed and therefore lives in the `host`
//! feature; it re-exports `opencadcodec` so plugins do not need to depend on it
//! directly and risk an ABI mismatch from a different version.
//!
//! For binary compatibility, the host and every plugin must resolve the same
//! `acadrust` source. The host does this via a `[patch.crates-io]` entry in
//! `opencadcodec` source. The host does this via a `[patch.crates-io]` entry in
//! `Cargo.toml`; out-of-tree plugins should copy that exact patch.
//!
//! For internal architecture (process model, wire protocols, versioning policy,
@@ -40,7 +40,7 @@ pub mod entity_coverage;
pub mod entity_coverage_types;
pub mod version_info;
/// Runtime host surface — only built with the `host` feature (pulls `acadrust`).
/// Runtime host surface — only built with the `host` feature (pulls `opencadcodec`).
#[cfg(feature = "host")]
pub mod host;
+3 -3
View File
@@ -8,7 +8,7 @@
//! 2. The runner presents a pre-shared token via [`crate::ipc::protocol::PLUGIN_TOKEN_ENV`];
//! the host rejects the connection on mismatch.
//! 3. The host requests the manifest, checks `api_version` and (for v4+) the
//! acadrust source gate, then keeps the process alive.
//! opencadcodec source gate, then keeps the process alive.
//! 4. Host → plugin calls (`dispatch`, `execute_code`, interactive events) are
//! sent over the socket with a configurable per-call timeout.
//! 5. Stdout/stderr of the child are drained into `PluginIoLine` records for
@@ -1171,8 +1171,8 @@ mod timeout_tests {
use crate::ipc::transport::{recv, send};
use crate::ribbon::owned::OwnedPluginManifest;
use crate::test_lock::ENV_LOCK;
use acadrust::xdata::ExtendedDataRecord;
use acadrust::{CadDocument, EntityType, Handle};
use codec::xdata::ExtendedDataRecord;
use codec::{CadDocument, EntityType, Handle};
use interprocess::local_socket::{
traits::{Listener, Stream as StreamTrait},
GenericNamespaced, ListenerOptions, Stream, ToNsName,
+10 -10
View File
@@ -286,10 +286,10 @@ mod tests {
0
}
fn for_each_entity(&self, _f: &mut dyn FnMut(crate::host::ReaderEntity<'_>)) {}
fn layer_name(&self, _handle: acadrust::Handle) -> Option<&str> {
fn layer_name(&self, _handle: codec::Handle) -> Option<&str> {
None
}
fn app_id_name(&self, _handle: acadrust::Handle) -> Option<&str> {
fn app_id_name(&self, _handle: codec::Handle) -> Option<&str> {
None
}
}
@@ -299,34 +299,34 @@ mod tests {
fn tab_index(&self) -> usize {
0
}
fn document(&self) -> &acadrust::CadDocument {
fn document(&self) -> &codec::CadDocument {
panic!("not used")
}
fn document_mut(&mut self) -> &mut acadrust::CadDocument {
fn document_mut(&mut self) -> &mut codec::CadDocument {
panic!("not used")
}
fn document_reader(&self) -> Box<dyn crate::host::DocumentReader + '_> {
Box::new(EmptyReader)
}
fn add_entity(&mut self, _entity: acadrust::EntityType) -> acadrust::Handle {
fn add_entity(&mut self, _entity: codec::EntityType) -> codec::Handle {
panic!("not used")
}
fn bump_geometry(&mut self) {}
fn read_record(
&self,
_handle: acadrust::Handle,
_handle: codec::Handle,
_app_name: &str,
) -> Option<&acadrust::xdata::ExtendedDataRecord> {
) -> Option<&codec::xdata::ExtendedDataRecord> {
None
}
fn write_record(
&mut self,
_handle: acadrust::Handle,
_record: acadrust::xdata::ExtendedDataRecord,
_handle: codec::Handle,
_record: codec::xdata::ExtendedDataRecord,
) -> bool {
false
}
fn remove_record(&mut self, _handle: acadrust::Handle, _app_name: &str) -> bool {
fn remove_record(&mut self, _handle: codec::Handle, _app_name: &str) -> bool {
false
}
fn push_undo(&mut self, _label: &str) {}
+9 -9
View File
@@ -338,7 +338,7 @@ mod tests {
use crate::ipc::protocol::{HostResponse, PluginRequest, PluginResponse};
use crate::ipc::transport::recv;
use crate::ipc::v4::protocol::{HostToPluginV4, PluginToHostV4};
use acadrust::{CadDocument, Handle};
use codec::{CadDocument, Handle};
use interprocess::local_socket::{
traits::{Listener, Stream as StreamTrait},
GenericNamespaced, ListenerOptions, ToNsName,
@@ -386,7 +386,7 @@ mod tests {
struct DummyHost {
push_info_messages: StdMutex<Vec<String>>,
transactions: StdMutex<Vec<(String, Vec<acadrust::EntityType>)>>,
transactions: StdMutex<Vec<(String, Vec<codec::EntityType>)>>,
}
impl DummyHost {
fn new() -> Self {
@@ -398,7 +398,7 @@ mod tests {
fn take_push_info(&self) -> Vec<String> {
std::mem::take(&mut *self.push_info_messages.lock().unwrap())
}
fn take_transactions(&self) -> Vec<(String, Vec<acadrust::EntityType>)> {
fn take_transactions(&self) -> Vec<(String, Vec<codec::EntityType>)> {
std::mem::take(&mut *self.transactions.lock().unwrap())
}
}
@@ -415,7 +415,7 @@ mod tests {
fn document_reader(&self) -> Box<dyn DocumentReader + '_> {
Box::new(EmptyReader)
}
fn add_entity(&mut self, _entity: acadrust::EntityType) -> Handle {
fn add_entity(&mut self, _entity: codec::EntityType) -> Handle {
panic!("not used")
}
fn bump_geometry(&mut self) {}
@@ -423,13 +423,13 @@ mod tests {
&self,
_handle: Handle,
_app_name: &str,
) -> Option<&acadrust::xdata::ExtendedDataRecord> {
) -> Option<&codec::xdata::ExtendedDataRecord> {
None
}
fn write_record(
&mut self,
_handle: Handle,
_record: acadrust::xdata::ExtendedDataRecord,
_record: codec::xdata::ExtendedDataRecord,
) -> bool {
false
}
@@ -440,7 +440,7 @@ mod tests {
fn update_entities_transaction(
&mut self,
label: &str,
entities: Vec<acadrust::EntityType>,
entities: Vec<codec::EntityType>,
) -> Result<(), String> {
self.transactions.lock().unwrap().push((label.to_owned(), entities));
Ok(())
@@ -624,10 +624,10 @@ mod tests {
let handler: Arc<dyn Fn(Option<u64>, PluginNotification) + Send + Sync> =
Arc::new(|_, _| {});
let conn = V4Connection::new(host_stream, handler).unwrap();
let mut point = acadrust::entities::Point::new();
let mut point = codec::entities::Point::new();
point.common.handle = Handle::new(42);
point.location.x = 7.0;
let expected = acadrust::EntityType::Point(point);
let expected = codec::EntityType::Point(point);
let sent = expected.clone();
let runner = thread::spawn(move || {
+10 -10
View File
@@ -29,7 +29,7 @@ use std::io;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering};
use acadrust::{CadDocument, EntityType, Handle};
use codec::{CadDocument, EntityType, Handle};
use memmap2::{Mmap, MmapMut, MmapOptions};
use rkyv::{check_archived_root, to_bytes, Archive, Deserialize, Serialize};
@@ -444,8 +444,8 @@ pub struct LayerView {
pub name: String,
}
impl From<&acadrust::tables::Layer> for LayerView {
fn from(layer: &acadrust::tables::Layer) -> Self {
impl From<&codec::tables::Layer> for LayerView {
fn from(layer: &codec::tables::Layer) -> Self {
Self {
handle: layer.handle.value(),
name: layer.name.clone(),
@@ -460,8 +460,8 @@ pub struct AppIdView {
pub name: String,
}
impl From<&acadrust::tables::AppId> for AppIdView {
fn from(app_id: &acadrust::tables::AppId) -> Self {
impl From<&codec::tables::AppId> for AppIdView {
fn from(app_id: &codec::tables::AppId) -> Self {
Self {
handle: app_id.handle.value(),
name: app_id.name.clone(),
@@ -539,8 +539,8 @@ impl ReaderEntityKind {
// ── V4 full-entity document view ────────────────────────────────────────────
/// Serializable V4 document view. The outer structure is rkyv; each entity's
/// `data` is a bincode-encoded `acadrust::EntityType` so that the plugin can
/// reconstruct the full typed entity without relying on acadrust's own rkyv
/// `data` is a bincode-encoded `codec::EntityType` so that the plugin can
/// reconstruct the full typed entity without relying on opencadcodec's own rkyv
/// support.
#[derive(Archive, Serialize, Deserialize, Debug, Clone)]
#[archive(check_bytes)]
@@ -605,9 +605,9 @@ impl From<&EntityType> for EntityViewV4 {
mod tests {
use super::*;
use crate::host::{DocumentReader, ReaderEntityKind};
use acadrust::entities::Point;
use acadrust::tables::Layer;
use acadrust::{CadDocument, EntityType};
use codec::entities::Point;
use codec::tables::Layer;
use codec::{CadDocument, EntityType};
fn unique_path(name: &str) -> PathBuf {
std::env::temp_dir().join(format!(
+2 -2
View File
@@ -1,7 +1,7 @@
//! Embedded type registry generated at build time.
//!
//! The JSON embedded here is produced by tracing every editor record root and
//! its dependent `acadrust` types with `serde-reflection`, then mapping the
//! its dependent `opencadcodec` types with `serde-reflection`, then mapping the
//! result into a stable, language-binding-friendly schema defined in
//! [`crate::type_registry_types`]. The registry is embedded via
//! `include_str!(concat!(env!("OUT_DIR"), "/type_registry.json"))` so the
@@ -9,7 +9,7 @@
//!
//! The registry covers the complete entity, object, symbol-table, header, and
//! document-record roots used by the editor. Language bindings and tooling can
//! inspect every variant without pulling the full `acadrust` dependency tree.
//! inspect every variant without pulling the full `opencadcodec` dependency tree.
pub use crate::type_registry_types::*;
+13 -13
View File
@@ -2,13 +2,13 @@
//!
//! At compile time `build.rs` writes a small JSON blob to
//! `OUT_DIR/version_info.json` containing host version, `ocs_plugin_api`
//! version, `acadrust` version and source, API version bounds, and a build
//! version, `opencadcodec` version and source, API version bounds, and a build
//! timestamp. The blob is embedded via `include_str!` and is available without
//! enabling the `host` feature.
//!
//! The acadrust source string is used to detect binary-incompatible plugin
//! The opencadcodec source string is used to detect binary-incompatible plugin
//! builds. For API v4 and later the host compares the plugin's resolved
//! `acadrust` source with its own via [`acadrust_sources_compatible`]. Two
//! `opencadcodec` source with its own via [`acadrust_sources_compatible`]. Two
//! sources are considered compatible only when they resolve to the same 40
//! character git commit hash.
//!
@@ -60,7 +60,7 @@ fn parsed_version_info() -> &'static VersionInfo {
})
}
/// Returns the host's full `acadrust` Cargo source.
/// Returns the host's full `opencadcodec` Cargo source.
pub fn host_acadrust_source() -> &'static str {
&parsed_version_info().acadrust_source
}
@@ -151,8 +151,8 @@ mod tests {
assert_eq!(value["api_version_min_supported"].as_i64(), Some(2));
// acadrust_version should have a patch component (e.g. "0.4.0").
let acadrust = value["acadrust_version"].as_str().expect("acadrust_version string");
assert_eq!(acadrust.split('.').count(), 3);
let opencadcodec = value["acadrust_version"].as_str().expect("acadrust_version string");
assert_eq!(opencadcodec.split('.').count(), 3);
}
#[test]
@@ -208,7 +208,7 @@ mod tests {
#[test]
fn extracts_full_hash_from_cargo_source() {
let src = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let src = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
assert_eq!(
acadrust_source_hash(src),
Some("94df2c3f87fa051b16ffc3923f80e9247c85c5fd")
@@ -228,22 +228,22 @@ mod tests {
#[test]
fn source_comparison_matches_full_hashes() {
let a = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let b = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let a = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let b = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
assert!(acadrust_sources_compatible(a, b));
}
#[test]
fn source_comparison_detects_mismatch() {
let a = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let b = "git+https://github.com/HakanSeven12/cadcodec.git?rev=0908da7#0908da7b6e4f702a6c78359a57f53e2b79cf39eb";
let a = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let b = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=0908da7#0908da7b6e4f702a6c78359a57f53e2b79cf39eb";
assert!(!acadrust_sources_compatible(a, b));
}
#[test]
fn source_comparison_case_insensitive() {
let a = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let b = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94DF2C3F87FA051B16FFC3923F80E9247C85C5FD";
let a = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd";
let b = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94DF2C3F87FA051B16FFC3923F80E9247C85C5FD";
assert!(acadrust_sources_compatible(a, b));
}
}