Merge branch 'main' into feature/parametric-constraint-system

# Conflicts:
#	src/app/automation.rs
#	src/app/mod.rs
#	src/ui/ribbon/widgets.rs
This commit is contained in:
Felix Riestra committed 2026-09-09 13:35:07 +02:00
commit e2d39b049d
114 files changed
+13641 -1685

No files matched your search

+1 -1
View File
@@ -259,7 +259,7 @@ pub unsafe extern "C" fn dwg_thumbnail_png(
#[no_mangle]
pub unsafe extern "C" fn dwg_thumbnail_free(ptr: *mut u8, len: usize) {
if !ptr.is_null() && len != 0 {
drop(Box::from_raw(std::slice::from_raw_parts_mut(ptr, len)));
drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(ptr, len)));
}
}
+2 -2
View File
@@ -14,8 +14,8 @@ bincode = "1.3"
thiserror = "1"
# Host adapters convert ObjectId to acadrust handles; kernel handles stay local.
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "5b56571a190e7a17c8f12d36390d2938b0fb72f7", features = ["serde"], optional = true }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "6a5e6fc2822f6af6b2611cd605b44437b35158f0", features = ["acis", "offset"], optional = true }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7b4c112bd7895fe9039514c21c5b5aa59eaf2525", features = ["serde"], optional = true }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "8efd43cb2f04577d20c3ee33ce5273bb49affa3d", features = ["acis", "offset"], optional = true }
ocs_plugin_api = { path = "../ocs_plugin_api", optional = true, features = ["host"] }
[build-dependencies]
+1 -4
View File
@@ -450,10 +450,7 @@ fn profile_curves<B: DocApiBackend>(
b.profile_curves(profile)
}
/// Validate a `Curve2Spec`'s fallible conditions BEFORE mutation (the same rules
/// the host's `curve_spec_to_entity` enforces), so `CreateMany` stays all-or-nothing.
/// Currently: ellipse minor/major ratio must be in (0, 1].
/// Validate that an entity exists before performing an operation.
fn require_exists<B: DocApiBackend>(b: &B, id: ObjectId, op: &'static str) -> ApiResult<()> {
if b.entity_exists(id) {
Ok(())
+1 -1
View File
@@ -219,7 +219,7 @@ Broadcasting is V4-gated inside `PluginManager::broadcast_notification`: V2/V3 p
The crate embeds two JSON blobs at build time:
- **Type registry** (`OUT_DIR/type_registry.json`) — a language-binding-friendly schema for a curated allow-list of `acadrust` types, generated by `serde-reflection` in `build.rs`. See [`src/type_registry.rs`](src/type_registry.rs).
- **Type registry** (`OUT_DIR/type_registry.json`) — a language-binding-friendly schema for every editor record root and its dependent types, generated by `serde-reflection` in `build.rs`. See [`src/type_registry.rs`](src/type_registry.rs).
- **Version info** (`OUT_DIR/version_info.json`) — host version, `ocs_plugin_api` version, `acadrust` version and source, `rustc` version, API versions, build timestamp. See [`src/version_info.rs`](src/version_info.rs).
Both are accessible without enabling the `host` feature.
+2 -2
View File
@@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] }
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
# `HostApi` runtime surface. The default crate stays dependency-free so engine
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", optional = true, features = ["serde"] }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7b4c112bd7895fe9039514c21c5b5aa59eaf2525", optional = true, features = ["serde"] }
# Runtime IPC and serialization (host feature only).
interprocess = { version = "2", optional = true }
@@ -38,7 +38,7 @@ serde_json = "1"
serde = { version = "1", features = ["derive"] }
cargo-lock = "11"
# acadrust is scanned at build time to generate the embedded type registry.
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", features = ["serde"] }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7b4c112bd7895fe9039514c21c5b5aa59eaf2525", features = ["serde"] }
[dev-dependencies]
serde_json = "1"
+1 -1
View File
@@ -243,7 +243,7 @@ session belongs to.
`ocs_plugin_api` embeds two JSON blobs at build time:
- **Type registry** — a stable, language-binding-friendly schema generated by
tracing a curated allow-list of `acadrust` types with `serde-reflection`.
tracing every editor record root and its dependent types with `serde-reflection`.
- **Version info** — `ocs_version`, `ocs_plugin_api_version`, `acadrust_version`,
`api_version`, etc.
+404 -5
View File
@@ -34,11 +34,42 @@ fn generate_type_registry(out_dir: &Path) {
let mut samples = Samples::new();
add_enum_samples(&mut tracer, &mut samples);
// Curated allow-list of acadrust types. We intentionally exclude
// EntityType here to avoid pulling in all 41+ entity variants and any
// custom-serializer edge cases.
// Trace the complete record roots used by the editor and MCP API. Nested
// entity/object variants and their enums are added to the same registry.
type TraceFn = fn(&mut Tracer, &Samples);
let types: Vec<(&str, TraceFn)> = vec![
("EntityType", trace::<acadrust::EntityType>),
("ObjectType", trace::<acadrust::objects::ObjectType>),
(
"HeaderVariables",
trace::<acadrust::document::HeaderVariables>,
),
("SummaryInfo", trace::<acadrust::document::SummaryInfo>),
("LineType", trace::<acadrust::LineType>),
("TextStyle", trace::<acadrust::TextStyle>),
("BlockRecord", trace::<acadrust::BlockRecord>),
("DimStyle", trace::<acadrust::DimStyle>),
("AppId", trace::<acadrust::AppId>),
("View", trace::<acadrust::View>),
("VPort", trace::<acadrust::VPort>),
("Ucs", trace::<acadrust::Ucs>),
("VxTableRecord", trace::<acadrust::VxTableRecord>),
("DxfClass", trace::<acadrust::classes::DxfClass>),
(
"BlockVisibilityParameter",
trace::<acadrust::objects::BlockVisibilityParameter>,
),
("FieldDef", trace::<acadrust::document::FieldDef>),
(
"DgnLsDefinition",
trace::<acadrust::objects::DgnLsDefinition>,
),
("DgnLsComponent", trace::<acadrust::objects::DgnLsComponent>),
(
"NotificationCollection",
trace::<acadrust::notification::NotificationCollection>,
),
("Preview", trace::<acadrust::document::Preview>),
("Point", trace::<acadrust::Point>),
("Line", trace::<acadrust::Line>),
("Circle", trace::<acadrust::Circle>),
@@ -93,10 +124,366 @@ fn generate_type_registry(out_dir: &Path) {
);
}
type TraceSimpleFn = fn(&mut Tracer);
let enums: Vec<(&str, TraceSimpleFn)> = vec![
(
"AssocAnnotationKind",
trace_simple::<acadrust::objects::AssocAnnotationKind>,
),
(
"AssocConstraintNodeData",
trace_simple::<acadrust::objects::AssocConstraintNodeData>,
),
(
"AssocEvalValue",
trace_simple::<acadrust::objects::AssocEvalValue>,
),
(
"AssocSubcurveKind",
trace_simple::<acadrust::objects::AssocSubcurveKind>,
),
(
"AssocSurfaceActionKind",
trace_simple::<acadrust::objects::AssocSurfaceActionKind>,
),
(
"AssocViewObjectActionParamKind",
trace_simple::<acadrust::objects::AssocViewObjectActionParamKind>,
),
(
"AcisVersion",
trace_simple::<acadrust::entities::AcisVersion>,
),
(
"AssociativeData",
trace_simple::<acadrust::objects::AssociativeData>,
),
(
"AttachmentPointType",
trace_simple::<acadrust::entities::AttachmentPointType>,
),
(
"BlockContentConnectionType",
trace_simple::<acadrust::entities::BlockContentConnectionType>,
),
(
"BlockEvalValue",
trace_simple::<acadrust::objects::BlockEvalValue>,
),
("BorderType", trace_simple::<acadrust::entities::BorderType>),
(
"BoundaryEdge",
trace_simple::<acadrust::entities::BoundaryEdge>,
),
(
"BreakFlowDirection",
trace_simple::<acadrust::entities::BreakFlowDirection>,
),
(
"CellAlignment",
trace_simple::<acadrust::objects::CellAlignment>,
),
(
"CellStyleType",
trace_simple::<acadrust::entities::CellStyleType>,
),
("CellType", trace_simple::<acadrust::entities::CellType>),
(
"CellValueType",
trace_simple::<acadrust::entities::CellValueType>,
),
(
"ClassObjectData",
trace_simple::<acadrust::objects::ClassObjectData>,
),
("ClipMode", trace_simple::<acadrust::entities::ClipMode>),
("ClipType", trace_simple::<acadrust::entities::ClipType>),
(
"CompoundEntry",
trace_simple::<acadrust::compound_file::CompoundEntry>,
),
(
"CompoundPropertyValue",
trace_simple::<acadrust::compound_file::CompoundPropertyValue>,
),
(
"CompoundStreamContent",
trace_simple::<acadrust::compound_file::CompoundStreamContent>,
),
(
"DataObjectData",
trace_simple::<acadrust::objects::DataObjectData>,
),
(
"DgnLineStyleData",
trace_simple::<acadrust::objects::DgnLineStyleData>,
),
(
"DgnLsComponentData",
trace_simple::<acadrust::objects::DgnLsComponentData>,
),
(
"DgnLsComponentType",
trace_simple::<acadrust::objects::DgnLsComponentType>,
),
(
"DgnLsPhaseMode",
trace_simple::<acadrust::objects::DgnLsPhaseMode>,
),
("DimSubtype", trace_simple::<acadrust::objects::DimSubtype>),
("Dimension", trace_simple::<acadrust::entities::Dimension>),
(
"DimensionType",
trace_simple::<acadrust::entities::DimensionType>,
),
(
"DynamicBlockData",
trace_simple::<acadrust::objects::DynamicBlockData>,
),
(
"EmbeddedEntity",
trace_simple::<acadrust::entities::EmbeddedEntity>,
),
(
"ExtendedEntityData",
trace_simple::<acadrust::entities::ExtendedEntityData>,
),
(
"FlowDirectionType",
trace_simple::<acadrust::entities::FlowDirectionType>,
),
(
"HatchPatternType",
trace_simple::<acadrust::entities::HatchPatternType>,
),
(
"HatchStyleType",
trace_simple::<acadrust::entities::HatchStyleType>,
),
(
"HelixConstraint",
trace_simple::<acadrust::entities::HelixConstraint>,
),
(
"HooklineDirection",
trace_simple::<acadrust::entities::HooklineDirection>,
),
(
"HorizontalAlignment",
trace_simple::<acadrust::entities::HorizontalAlignment>,
),
(
"LeaderContentType",
trace_simple::<acadrust::entities::LeaderContentType>,
),
(
"LeaderCreationType",
trace_simple::<acadrust::entities::LeaderCreationType>,
),
(
"LeaderDrawOrderType",
trace_simple::<acadrust::objects::LeaderDrawOrderType>,
),
(
"LeaderPathType",
trace_simple::<acadrust::entities::LeaderPathType>,
),
(
"LineTypeComplexContent",
trace_simple::<acadrust::tables::LineTypeComplexContent>,
),
(
"LegacyEntityData",
trace_simple::<acadrust::entities::LegacyEntityData>,
),
(
"MLineJustification",
trace_simple::<acadrust::entities::MLineJustification>,
),
("MTextFlag", trace_simple::<acadrust::entities::MTextFlag>),
(
"MaterialProceduralValue",
trace_simple::<acadrust::objects::MaterialProceduralValue>,
),
(
"MultiLeaderDrawOrderType",
trace_simple::<acadrust::objects::MultiLeaderDrawOrderType>,
),
(
"MultiLeaderPathType",
trace_simple::<acadrust::entities::MultiLeaderPathType>,
),
(
"ObjectContextKind",
trace_simple::<acadrust::objects::ObjectContextKind>,
),
(
"NotificationType",
trace_simple::<acadrust::notification::NotificationType>,
),
(
"OleFrameEnvelope",
trace_simple::<acadrust::entities::OleFrameEnvelope>,
),
(
"OleObjectType",
trace_simple::<acadrust::entities::OleObjectType>,
),
(
"PlotPaperUnits",
trace_simple::<acadrust::objects::PlotPaperUnits>,
),
(
"PlotRotation",
trace_simple::<acadrust::objects::PlotRotation>,
),
("PlotType", trace_simple::<acadrust::objects::PlotType>),
(
"PreviewFormat",
trace_simple::<acadrust::document::PreviewFormat>,
),
(
"PolyfaceSmoothType",
trace_simple::<acadrust::entities::PolyfaceSmoothType>,
),
(
"ProxyPayloadEncoding",
trace_simple::<acadrust::objects::ProxyPayloadEncoding>,
),
(
"ResolutionUnit",
trace_simple::<acadrust::objects::ResolutionUnit>,
),
("ScaledType", trace_simple::<acadrust::objects::ScaledType>),
(
"SemanticPropertyValue",
trace_simple::<acadrust::objects::SemanticPropertyValue>,
),
(
"ShadePlotMode",
trace_simple::<acadrust::objects::ShadePlotMode>,
),
(
"ShadePlotResolutionLevel",
trace_simple::<acadrust::objects::ShadePlotResolutionLevel>,
),
(
"SolidHistoryOperation",
trace_simple::<acadrust::objects::SolidHistoryOperation>,
),
(
"SurfaceData",
trace_simple::<acadrust::entities::SurfaceData>,
),
(
"SurfaceKind",
trace_simple::<acadrust::entities::SurfaceKind>,
),
(
"SurfaceSmoothType",
trace_simple::<acadrust::entities::SurfaceSmoothType>,
),
(
"TableBorderType",
trace_simple::<acadrust::objects::TableBorderType>,
),
(
"TableCellContentType",
trace_simple::<acadrust::entities::TableCellContentType>,
),
(
"TableFlowDirection",
trace_simple::<acadrust::objects::TableFlowDirection>,
),
(
"TextAlignmentType",
trace_simple::<acadrust::entities::TextAlignmentType>,
),
(
"TextAngleType",
trace_simple::<acadrust::entities::TextAngleType>,
),
(
"TextAttachmentDirectionType",
trace_simple::<acadrust::entities::TextAttachmentDirectionType>,
),
(
"TextAttachmentPointType",
trace_simple::<acadrust::entities::TextAttachmentPointType>,
),
(
"TextAttachmentType",
trace_simple::<acadrust::entities::TextAttachmentType>,
),
(
"TextHorizontalAlignment",
trace_simple::<acadrust::entities::TextHorizontalAlignment>,
),
(
"TextVerticalAlignment",
trace_simple::<acadrust::entities::TextVerticalAlignment>,
),
(
"UnderlayType",
trace_simple::<acadrust::entities::UnderlayType>,
),
(
"ValueUnitType",
trace_simple::<acadrust::entities::ValueUnitType>,
),
(
"VbaDirectoryValue",
trace_simple::<acadrust::vba::VbaDirectoryValue>,
),
(
"VerticalAlignment",
trace_simple::<acadrust::entities::VerticalAlignment>,
),
(
"ViewportRenderMode",
trace_simple::<acadrust::entities::ViewportRenderMode>,
),
(
"VisualStylePropertyValue",
trace_simple::<acadrust::objects::VisualStylePropertyValue>,
),
(
"ViewRepSketchGeometry",
trace_simple::<acadrust::objects::ViewRepSketchGeometry>,
),
(
"WipeoutClipMode",
trace_simple::<acadrust::entities::WipeoutClipMode>,
),
(
"WipeoutClipType",
trace_simple::<acadrust::entities::WipeoutClipType>,
),
("WireType", trace_simple::<acadrust::entities::WireType>),
];
for (name, trace) in enums {
trace(&mut tracer);
eprintln!("[ocs_plugin_api build] traced enum variants: {name}");
}
let traced = tracer
.registry()
.expect("type registry tracing failed; see stderr for individual errors");
let registry = map_to_custom_schema(&traced);
let mut registry = map_to_custom_schema(&traced);
let mut section_style_tracer = Tracer::new(TracerConfig::default());
section_style_tracer
.trace_simple_type::<acadrust::entities::SectionViewStyle>()
.expect("section view style tracing failed");
let section_style_registry = section_style_tracer
.registry()
.expect("section view style registry failed");
let section_style = section_style_registry
.get("SectionViewStyle")
.expect("section view style registry entry");
registry.types.insert(
TypeId::new("EntitySectionViewStyle"),
map_container("EntitySectionViewStyle", section_style),
);
let json = serde_json::to_string_pretty(&registry).unwrap();
fs::write(out_dir.join("type_registry.json"), json).unwrap();
}
@@ -114,10 +501,22 @@ where
}
}
fn trace_simple<T>(tracer: &mut Tracer)
where
T: DeserializeOwned,
{
if let Err(error) = tracer.trace_simple_type::<T>() {
panic!(
"type registry enum tracing failed for {}: {error}",
std::any::type_name::<T>()
);
}
}
fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) {
// serde-reflection needs at least one sample value per enum variant in
// order to reconstruct the full schema. Provide samples for the enums that
// appear inside the allow-list types below.
// need concrete serialized values.
let _ = tracer.trace_value(samples, &acadrust::LineWeight::ByLayer);
let _ = tracer.trace_value(samples, &acadrust::LineWeight::ByBlock);
let _ = tracer.trace_value(samples, &acadrust::LineWeight::Default);
+5 -2
View File
@@ -746,10 +746,13 @@ mod repl {
error: Option<String>,
error_type: Option<String>,
traceback: Option<String>,
line_number: Option<u32>,
column_number: Option<u32>,
position: Option<(u32, u32)>,
duration_ms: f64,
) -> Self {
let (line_number, column_number) = match position {
Some((l, c)) => (Some(l), Some(c)),
None => (None, None),
};
Self {
success,
output,
+2 -9
View File
@@ -261,20 +261,13 @@ pub fn run_request_proxy(
token: [u8; PROXY_TOKEN_LEN],
) -> std::io::Result<()> {
for stream in listener.incoming() {
let mut stream = match stream {
Ok(s) => s,
Err(e) => return Err(e),
};
let mut stream = stream?;
if !verify_token(&mut stream, &token) {
continue;
}
let sender = Arc::clone(&sender);
std::thread::spawn(move || {
loop {
let req: PluginRequest = match recv_framed(&mut stream) {
Ok(r) => r,
Err(_) => break,
};
while let Ok(req) = recv_framed(&mut stream) {
let resp = match sender.request(req) {
Ok(r) => r,
Err(e) => PluginResponse::Error(e.to_string()),
+6 -4
View File
@@ -75,10 +75,12 @@ fn request_kind(req: &HostRequest) -> &'static str {
}
}
type DeferredRequest = (u64, Option<u64>, Box<PluginRequest>);
pub(crate) struct V4Connection {
shared: Arc<V4HostShared>,
incoming: Mutex<mpsc::Receiver<HostIncoming>>,
deferred: Mutex<VecDeque<(u64, Option<u64>, Box<PluginRequest>)>>,
deferred: Mutex<VecDeque<DeferredRequest>>,
call_lock: Mutex<()>,
next_id: AtomicU64,
reader_handle: Mutex<Option<std::thread::JoinHandle<()>>>,
@@ -199,7 +201,7 @@ impl V4Connection {
tab_id,
payload,
}) => {
if tab_id.map_or(true, |request_tab| request_tab == host.tab_id()) {
if tab_id.is_none_or(|request_tab| request_tab == host.tab_id()) {
self.respond_to_plugin_request(
host,
rid,
@@ -251,7 +253,7 @@ impl V4Connection {
let mut deferred = self.deferred.lock().unwrap_or_else(|e| e.into_inner());
let mut waiting = VecDeque::new();
while let Some((id, tab_id, payload)) = deferred.pop_front() {
if tab_id.map_or(true, |request_tab| request_tab == current_tab_id) {
if tab_id.is_none_or(|request_tab| request_tab == current_tab_id) {
ready.push_back((id, payload));
} else {
waiting.push_back((id, tab_id, payload));
@@ -271,7 +273,7 @@ impl V4Connection {
tab_id,
payload,
}) => {
if tab_id.map_or(true, |request_tab| request_tab == current_tab_id) {
if tab_id.is_none_or(|request_tab| request_tab == current_tab_id) {
self.respond_to_plugin_request(
host,
id,
+43 -13
View File
@@ -1,17 +1,15 @@
//! Embedded type registry generated at build time.
//!
//! The JSON embedded here is produced by tracing a curated allow-list of
//! `acadrust` types with `serde-reflection` and mapping the result into a
//! stable, language-binding-friendly schema defined in
//! The JSON embedded here is produced by tracing every editor record root and
//! its dependent `acadrust` 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
//! `host` feature is not required to read it.
//!
//! The allow-list is intentionally narrow: it excludes generic containers and
//! internal enums such as `EntityType` in favor of concrete serializable structs
//! like `Point`, `Line`, `Circle`, and `MText`. Language bindings and tooling
//! can depend on this schema without pulling the full `acadrust` dependency
//! tree.
//! 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.
pub use crate::type_registry_types::*;
@@ -121,13 +119,45 @@ mod tests {
}
#[test]
fn embedded_type_registry_does_not_contain_entity_type() {
fn embedded_type_registry_contains_every_entity_and_object_variant() {
let registry: TypeRegistry = serde_json::from_str(get_embedded_type_registry_json())
.expect("valid TypeRegistry JSON");
assert!(
!registry.types.contains_key(&TypeId::new("EntityType")),
"EntityType should not be in the allow-list registry"
);
let entities = registry
.types
.get(&TypeId::new("EntityType"))
.expect("EntityType in complete registry");
let objects = registry
.types
.get(&TypeId::new("ObjectType"))
.expect("ObjectType in complete registry");
assert_eq!(entities.variants.len(), 48);
assert_eq!(objects.variants.len(), 36);
for root in [
"HeaderVariables",
"SummaryInfo",
"LineType",
"TextStyle",
"BlockRecord",
"DimStyle",
"AppId",
"View",
"VPort",
"Ucs",
"VxTableRecord",
"DxfClass",
"BlockVisibilityParameter",
"FieldDef",
"DgnLsDefinition",
"DgnLsComponent",
"NotificationCollection",
"Preview",
"EntitySectionViewStyle",
] {
assert!(
registry.types.contains_key(&TypeId::new(root)),
"missing {root}"
);
}
}
#[test]
+1 -1
View File
@@ -8,7 +8,7 @@ publish = false
crate-type = ["cdylib"]
[dependencies]
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", features = ["serde"] }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7b4c112bd7895fe9039514c21c5b5aa59eaf2525", features = ["serde"] }
bincode = "1.3"
serde = { version = "1", features = ["derive"] }
console_error_panic_hook = "0.1"
+1 -1
View File
@@ -10,4 +10,4 @@ crate-type = ["cdylib"]
[dependencies]
ocs_plugin_api = { path = "../ocs_plugin_api", features = ["host"] }
acadrust = { git = "https://github.com/ramox81/cadcodec.git", rev = "6dcda1a", features = ["serde"] }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7b4c112bd7895fe9039514c21c5b5aa59eaf2525", features = ["serde"] }