Files
OneCAD-Draft/crates/ocs_plugin_api/src/ipc/server.rs
T
Hakan SevenandClaude Opus 4.8 f9f7138370 feat(plugin): persist XDATA to DWG and let plugins modify/delete entities (#249, #250)
#249 — plugin XDATA written via write_record survived only in memory: the
acadrust DWG writer dropped ExtendedData::records on save. Bump acadrust to
e88a9a6 (records now encode to EED and decode back on read) and fix the host
side that fed it:
  - ensure_app_id allocates a real APPID handle; a null handle serializes as
    0 and the EED reference can't resolve, so the XDATA vanished on reopen.
  - write_record / remove_record drop stale raw_dwg_eed for the target app so
    an edit made after a save/reopen wins over the pre-edit bytes.

#250 — out-of-process plugins got a throwaway document_mut() snapshot, so
edits to existing entities were silently discarded and deletion wasn't
expressible at all. Add the missing mutation surface:
  - UpdateEntity / RemoveEntity IPC requests + HostApi::update_entity /
    remove_entity (default in-process impls, RPC overrides on the client that
    invalidate the stale document cache).
  - Scene::update_entity replaces the entity in place, preserving its handle
    and owning block, and reseeds only its derived caches; remove reuses the
    cache-coherent erase_entities (which also honours layer locks).
  - document_mut() is documented as a local-only snapshot out-of-process.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-02 15:39:26 +03:00

66 lines
2.3 KiB
Rust

//! Host-side IPC request handler.
use crate::host::HostApi;
use crate::ipc::protocol::{PluginRequest, PluginResponse};
/// Apply one plugin request to the host's `HostApi` implementation.
///
/// `on_start_interactive` is called when the plugin starts an interactive
/// command; the host should install an adapter that sends
/// `HostRequest::InteractiveEvent` back to the plugin process.
pub fn handle_plugin_request(
host: &mut dyn HostApi,
req: PluginRequest,
on_start_interactive: &mut dyn FnMut(u64),
) -> PluginResponse {
use PluginRequest::*;
match req {
PushInfo(msg) => {
host.push_info(&msg);
PluginResponse::Ok
}
PushOutput(msg) => {
host.push_output(&msg);
PluginResponse::Ok
}
PushError(msg) => {
host.push_error(&msg);
PluginResponse::Ok
}
AddEntity(entity) => PluginResponse::Handle(host.add_entity(entity)),
UpdateEntity(entity) => PluginResponse::Bool(host.update_entity(entity)),
RemoveEntity { handle } => PluginResponse::Bool(host.remove_entity(handle)),
BumpGeometry => {
host.bump_geometry();
PluginResponse::Ok
}
ReadRecord { handle, app_name } => {
PluginResponse::Record(host.read_record(handle, &app_name).cloned())
}
WriteRecord { handle, record } => PluginResponse::Bool(host.write_record(handle, record)),
RemoveRecord { handle, app_name } => {
PluginResponse::Bool(host.remove_record(handle, &app_name))
}
PushUndo { label } => {
host.push_undo(&label);
PluginResponse::Ok
}
SetDirty => {
host.set_dirty();
PluginResponse::Ok
}
StartInteractive { command_id } => {
on_start_interactive(command_id);
PluginResponse::Ok
}
DocumentSnapshot => PluginResponse::Document(host.document().clone()),
OpenDocumentView => match host.document_view() {
Some(info) => PluginResponse::DocumentView {
path: info.path,
version: info.version,
},
None => PluginResponse::Error("shared document view unavailable".to_string()),
},
}
}