forked from 1b-agent/opencadstudio-upstream
Merge pull request #1423 from felixriestra:pr/python-host-model
The bundled `opencad.python` plugin drives OCS through plugin API v7: all 43 canvas entity kinds, the drawing tables, and OCS's own commands run step by step. RustPython stays in the plugin process — the plugin crate is outside the workspace with its own lock file, so the application's own dependencies are unchanged. Reconciled with the plugin command work that landed as #1425, which the branch was written before: - Both added `impl Drop for PluginProcessInteractiveAdapter`, which git merges silently into two impls that do not compile. One body now cancels the command and then drops it from the runner, in that order: a plugin waiting on a step has to be released before the runner forgets the command. - `execute_command` (#1425) and `run_command` (#1423) are both routes from a script to the command line, and only the second was guarded. The SCRIPTCOMMANDS switch and the denylist are shared now, so neither route is the unguarded one. - `run_command` dispatched with plugin dispatch still enabled, so a script naming a plugin command re-entered the plugin that was blocked waiting for it. It sets the suppression flag for the length of the dispatch, as `execute_command` does. The denylist read the command as typed, while the dispatcher resolves aliases: a user-defined `Q` for `QUIT` (ALIASEDIT writes those) walked past it. It resolves the alias first. Also: the active-command comparison ran on every message, a mouse move included, allocating two strings for names that are already `&'static str`; SCRIPTCOMMANDS' own messages go through the translation macros; and `docs/.DS_Store` (now ignored) and an unrelated RTF note about constraint commands are out of the tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
41ecc0c458
72 files changed
+30614
-91
No files matched your search
@@ -11,7 +11,7 @@ use acadrust::{CadDocument, EntityType, Handle};
|
||||
use interprocess::local_socket::traits::Stream as StreamTrait;
|
||||
use interprocess::local_socket::{GenericNamespaced, Stream, ToNsName};
|
||||
|
||||
use crate::host::{DocumentReader, HostApi, InteractiveCommand, ReaderEntity};
|
||||
use crate::host::{DocumentReader, HostApi, HostSettingValue, InteractiveCommand, ReaderEntity};
|
||||
use crate::ipc::protocol::{
|
||||
HostResponse, HostToPlugin, PluginRequest, PluginResponse, PluginToHost, RunnerHandshake,
|
||||
};
|
||||
@@ -417,6 +417,104 @@ impl HostApi for PluginHostApi {
|
||||
}
|
||||
}
|
||||
|
||||
fn system_variable(&self, name: &str) -> Option<HostSettingValue> {
|
||||
match self.client.request(PluginRequest::GetSystemVariable { name: name.to_owned() }) {
|
||||
Ok(PluginResponse::SystemVariable(value)) => value,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_system_variable(
|
||||
&mut self,
|
||||
name: &str,
|
||||
value: HostSettingValue,
|
||||
) -> Result<HostSettingValue, String> {
|
||||
match self.client.request(PluginRequest::SetSystemVariable {
|
||||
name: name.to_owned(),
|
||||
value,
|
||||
}) {
|
||||
Ok(PluginResponse::SystemVariableResult(result)) => {
|
||||
if result.is_ok() {
|
||||
self.document_cache = OnceCell::new();
|
||||
}
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected system variable response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_entities_transaction(
|
||||
&mut self,
|
||||
label: &str,
|
||||
entities: Vec<EntityType>,
|
||||
) -> Result<(), String> {
|
||||
match self.client.request(PluginRequest::UpdateEntitiesTransaction {
|
||||
label: label.to_owned(), entities,
|
||||
}) {
|
||||
Ok(PluginResponse::EntityTransactionResult(result)) => {
|
||||
if result.is_ok() { self.document_cache = OnceCell::new(); }
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected entity transaction response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn selection(&self) -> Vec<Handle> {
|
||||
match self.client.request(PluginRequest::GetSelection) {
|
||||
Ok(PluginResponse::Selection(handles)) => handles,
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn solid_operation(&mut self, operation: crate::host::SolidOperation) -> Result<Handle, String> {
|
||||
match self.client.request(PluginRequest::SolidOperation { operation }) {
|
||||
Ok(PluginResponse::SolidResult(result)) => {
|
||||
if result.is_ok() { self.document_cache = OnceCell::new(); }
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected solid operation response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_command(&mut self, request: crate::host::CommandRequest) -> Result<crate::host::CommandOutcome, String> {
|
||||
match self.client.request(PluginRequest::RunCommand { request }) {
|
||||
Ok(PluginResponse::CommandResult(result)) => {
|
||||
self.document_cache = OnceCell::new();
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected command response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn table_operation(&mut self, operation: crate::host::TableOperation) -> Result<Handle, String> {
|
||||
match self.client.request(PluginRequest::TableOperation { operation }) {
|
||||
Ok(PluginResponse::TableResult(result)) => {
|
||||
if result.is_ok() { self.document_cache = OnceCell::new(); }
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected table operation response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_selection(&mut self, handles: &[Handle]) -> Result<(), String> {
|
||||
match self.client.request(PluginRequest::SetSelection { handles: handles.to_vec() }) {
|
||||
Ok(PluginResponse::SelectionResult(result)) => result,
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected selection response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_layer(&mut self, config: crate::host::LayerConfig) -> Option<Handle> {
|
||||
match self.client.request(PluginRequest::AddLayer(config)) {
|
||||
Ok(PluginResponse::OptHandle(h)) => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::host::{CommandSource, CommandStep};
|
||||
use crate::host::{CommandSource, CommandStep, HostSettingValue};
|
||||
use crate::manifest::ApiVersion;
|
||||
use crate::ribbon::owned::{OwnedPluginManifest, OwnedRibbonGroup};
|
||||
|
||||
@@ -81,6 +81,8 @@ pub enum HostRequest {
|
||||
code: String,
|
||||
tab_index: usize,
|
||||
},
|
||||
/// V7: release an interactive command after completion or cancellation.
|
||||
DropInteractive { command_id: u64 },
|
||||
}
|
||||
|
||||
/// Responses the plugin runner sends back for `HostRequest`.
|
||||
@@ -149,6 +151,21 @@ pub enum PluginRequest {
|
||||
ModifyLayer(crate::host::LayerConfig),
|
||||
/// Run a command on the active document tab's command line (AutoLISP style).
|
||||
ExecuteCommand(String),
|
||||
/// Read a host-managed setting without nested command dispatch.
|
||||
GetSystemVariable { name: String },
|
||||
/// Change a host-managed setting without nested command dispatch.
|
||||
SetSystemVariable { name: String, value: HostSettingValue },
|
||||
/// V7: validate and replace existing entities in a single undo step.
|
||||
UpdateEntitiesTransaction { label: String, entities: Vec<EntityType> },
|
||||
/// V7: synchronous selection read/write for the dispatch tab.
|
||||
GetSelection,
|
||||
SetSelection { handles: Vec<Handle> },
|
||||
/// V7 (additive): kernel-backed solid create or transform.
|
||||
SolidOperation { operation: crate::host::SolidOperation },
|
||||
/// V7 (additive): drawing table record create/modify/rename/delete.
|
||||
TableOperation { operation: crate::host::TableOperation },
|
||||
/// V7 (additive): drive an OCS command.
|
||||
RunCommand { request: crate::host::CommandRequest },
|
||||
}
|
||||
|
||||
/// Responses the host sends back for `PluginRequest`.
|
||||
@@ -177,6 +194,14 @@ pub enum PluginResponse {
|
||||
DocumentPath(Option<std::ffi::OsString>),
|
||||
/// Optional entity handle (e.g. from AddLayer).
|
||||
OptHandle(Option<Handle>),
|
||||
SystemVariable(Option<HostSettingValue>),
|
||||
SystemVariableResult(Result<HostSettingValue, String>),
|
||||
EntityTransactionResult(Result<(), String>),
|
||||
Selection(Vec<Handle>),
|
||||
SelectionResult(Result<(), String>),
|
||||
SolidResult(Result<Handle, String>),
|
||||
TableResult(Result<Handle, String>),
|
||||
CommandResult(Result<crate::host::CommandOutcome, String>),
|
||||
}
|
||||
|
||||
/// Messages sent from the host to the plugin runner.
|
||||
|
||||
@@ -3,6 +3,27 @@
|
||||
use crate::host::HostApi;
|
||||
use crate::ipc::protocol::{PluginRequest, PluginResponse};
|
||||
|
||||
fn addressable_document_snapshot(host: &dyn HostApi) -> acadrust::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()),
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
.map(acadrust::EntityType::AttributeEntity)
|
||||
.collect::<Vec<_>>();
|
||||
for attribute in attributes {
|
||||
// Attribute entities remain canonical children of INSERT in the host
|
||||
// document. The IPC snapshot also indexes them by handle so plugin
|
||||
// document models can address a child exactly like any other entity.
|
||||
let _ = snapshot.add_entity(attribute);
|
||||
}
|
||||
snapshot
|
||||
}
|
||||
|
||||
/// Apply one plugin request to the host's `HostApi` implementation.
|
||||
///
|
||||
/// `on_start_interactive` is called when the plugin starts an interactive
|
||||
@@ -54,7 +75,7 @@ pub fn handle_plugin_request(
|
||||
on_start_interactive(command_id);
|
||||
PluginResponse::Ok
|
||||
}
|
||||
DocumentSnapshot => PluginResponse::Document(Box::new(host.document().clone())),
|
||||
DocumentSnapshot => PluginResponse::Document(Box::new(addressable_document_snapshot(host))),
|
||||
OpenDocumentView => match host.document_view() {
|
||||
Some(info) => PluginResponse::DocumentView {
|
||||
path: info.path,
|
||||
@@ -80,5 +101,17 @@ pub fn handle_plugin_request(
|
||||
AddLayer(config) => PluginResponse::OptHandle(host.add_layer(config)),
|
||||
ModifyLayer(config) => PluginResponse::Bool(host.modify_layer(config)),
|
||||
ExecuteCommand(cmd) => PluginResponse::Bool(host.execute_command(&cmd)),
|
||||
GetSystemVariable { name } => PluginResponse::SystemVariable(host.system_variable(&name)),
|
||||
SetSystemVariable { name, value } => {
|
||||
PluginResponse::SystemVariableResult(host.set_system_variable(&name, value))
|
||||
}
|
||||
UpdateEntitiesTransaction { label, entities } => PluginResponse::EntityTransactionResult(
|
||||
host.update_entities_transaction(&label, entities),
|
||||
),
|
||||
GetSelection => PluginResponse::Selection(host.selection()),
|
||||
SetSelection { handles } => PluginResponse::SelectionResult(host.set_selection(&handles)),
|
||||
SolidOperation { operation } => PluginResponse::SolidResult(host.solid_operation(operation)),
|
||||
TableOperation { operation } => PluginResponse::TableResult(host.table_operation(operation)),
|
||||
RunCommand { request } => PluginResponse::CommandResult(host.run_command(request)),
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ use interprocess::TryClone;
|
||||
|
||||
use crate::host::{
|
||||
DocumentReader, ExecutionResult, HostApi, HostNotification, InteractiveCommand,
|
||||
PluginNotification, PluginRequestSender, PluginRequestError, ReaderEntity,
|
||||
PluginNotification, PluginRequestSender, PluginRequestError, ReaderEntity, HostSettingValue,
|
||||
};
|
||||
use crate::ipc::protocol::{
|
||||
HostRequest, HostResponse, PluginRequest, PluginResponse, RunnerHandshake,
|
||||
@@ -731,6 +731,104 @@ impl HostApi for V4PluginHostApi {
|
||||
}
|
||||
}
|
||||
|
||||
fn system_variable(&self, name: &str) -> Option<HostSettingValue> {
|
||||
match self.request(PluginRequest::GetSystemVariable { name: name.to_owned() }) {
|
||||
Ok(PluginResponse::SystemVariable(value)) => value,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_system_variable(
|
||||
&mut self,
|
||||
name: &str,
|
||||
value: HostSettingValue,
|
||||
) -> Result<HostSettingValue, String> {
|
||||
match self.request(PluginRequest::SetSystemVariable {
|
||||
name: name.to_owned(),
|
||||
value,
|
||||
}) {
|
||||
Ok(PluginResponse::SystemVariableResult(result)) => {
|
||||
if result.is_ok() {
|
||||
self.document_cache = OnceCell::new();
|
||||
}
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected system variable response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_entities_transaction(
|
||||
&mut self,
|
||||
label: &str,
|
||||
entities: Vec<EntityType>,
|
||||
) -> Result<(), String> {
|
||||
match self.request(PluginRequest::UpdateEntitiesTransaction {
|
||||
label: label.to_owned(), entities,
|
||||
}) {
|
||||
Ok(PluginResponse::EntityTransactionResult(result)) => {
|
||||
if result.is_ok() { self.document_cache = OnceCell::new(); }
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected entity transaction response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn selection(&self) -> Vec<Handle> {
|
||||
match self.request(PluginRequest::GetSelection) {
|
||||
Ok(PluginResponse::Selection(handles)) => handles,
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn solid_operation(&mut self, operation: crate::host::SolidOperation) -> Result<Handle, String> {
|
||||
match self.request(PluginRequest::SolidOperation { operation }) {
|
||||
Ok(PluginResponse::SolidResult(result)) => {
|
||||
if result.is_ok() { self.document_cache = OnceCell::new(); }
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected solid operation response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_command(&mut self, request: crate::host::CommandRequest) -> Result<crate::host::CommandOutcome, String> {
|
||||
match self.request(PluginRequest::RunCommand { request }) {
|
||||
Ok(PluginResponse::CommandResult(result)) => {
|
||||
self.document_cache = OnceCell::new();
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected command response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn table_operation(&mut self, operation: crate::host::TableOperation) -> Result<Handle, String> {
|
||||
match self.request(PluginRequest::TableOperation { operation }) {
|
||||
Ok(PluginResponse::TableResult(result)) => {
|
||||
if result.is_ok() { self.document_cache = OnceCell::new(); }
|
||||
result
|
||||
}
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected table operation response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_selection(&mut self, handles: &[Handle]) -> Result<(), String> {
|
||||
match self.request(PluginRequest::SetSelection { handles: handles.to_vec() }) {
|
||||
Ok(PluginResponse::SelectionResult(result)) => result,
|
||||
Ok(PluginResponse::Error(error)) => Err(error),
|
||||
Ok(other) => Err(format!("unexpected selection response: {other:?}")),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_layer(&mut self, config: crate::host::LayerConfig) -> Option<Handle> {
|
||||
match self.request(PluginRequest::AddLayer(config)) {
|
||||
Ok(PluginResponse::OptHandle(h)) => {
|
||||
|
||||
@@ -86,6 +86,167 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_state_notification_roundtrips() {
|
||||
let notification = HostNotification::CommandStateChanged {
|
||||
tab_id: 42,
|
||||
command: Some("LINE".to_owned()),
|
||||
};
|
||||
let bytes = bincode::serialize(¬ification).unwrap();
|
||||
let decoded: HostNotification = bincode::deserialize(&bytes).unwrap();
|
||||
assert_eq!(decoded, notification);
|
||||
let stopped = HostNotification::CommandStateChanged { tab_id: 42, command: None };
|
||||
let bytes = bincode::serialize(&stopped).unwrap();
|
||||
let decoded: HostNotification = bincode::deserialize(&bytes).unwrap();
|
||||
assert_eq!(decoded, stopped);
|
||||
let changed = HostNotification::DrawingChanged { tab_id: 42, epoch: 7 };
|
||||
let bytes = bincode::serialize(&changed).unwrap();
|
||||
let decoded: HostNotification = bincode::deserialize(&bytes).unwrap();
|
||||
assert_eq!(decoded, changed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_request_and_outcome_roundtrip() {
|
||||
use crate::host::{CommandOutcome, CommandRequest};
|
||||
for request in [
|
||||
CommandRequest::Run { line: "LINE 0,0 1,1".into() },
|
||||
CommandRequest::Start { name: "OFFSET".into() },
|
||||
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::Selection,
|
||||
CommandRequest::Enter,
|
||||
CommandRequest::Cancel,
|
||||
] {
|
||||
let bytes = bincode::serialize(&PluginRequest::RunCommand { request: request.clone() }).unwrap();
|
||||
assert!(matches!(bincode::deserialize::<PluginRequest>(&bytes).unwrap(),
|
||||
PluginRequest::RunCommand { request: decoded } if decoded == request));
|
||||
}
|
||||
let outcome = CommandOutcome {
|
||||
status: "waiting_input".into(),
|
||||
blocked_by: Some("command".into()),
|
||||
command: "OFFSET".into(),
|
||||
prompt: "Select object".into(),
|
||||
accepts: vec!["entity".into()],
|
||||
options: vec!["M".into()],
|
||||
entities: 4,
|
||||
added: -1,
|
||||
unconsumed: vec!["x".into()],
|
||||
error: Some("e".into()),
|
||||
};
|
||||
for result in [Ok(outcome), Err("refused".to_owned())] {
|
||||
let bytes = bincode::serialize(&PluginResponse::CommandResult(result.clone())).unwrap();
|
||||
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
|
||||
PluginResponse::CommandResult(decoded) if decoded == result));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_operation_request_and_response_roundtrip() {
|
||||
use crate::host::{LayerConfig, TableOperation};
|
||||
for operation in [
|
||||
TableOperation::LayerCreate {
|
||||
config: LayerConfig {
|
||||
name: "Walls".into(),
|
||||
color: Some(acadrust::types::Color::Index(5)),
|
||||
frozen: Some(true),
|
||||
description: Some("d".into()),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
TableOperation::LayerModify { config: LayerConfig { name: "Walls".into(), off: Some(true), ..Default::default() } },
|
||||
TableOperation::LayerRename { from: "A".into(), to: "B".into() },
|
||||
TableOperation::LayerDelete { name: "A".into(), erase_objects: true },
|
||||
TableOperation::LayerSetCurrent { name: "A".into() },
|
||||
TableOperation::TextStyleCreate {
|
||||
config: crate::host::TextStyleConfig { name: "T".into(), height: Some(2.5), oblique_angle: Some(0.2), backward: Some(true), ..Default::default() },
|
||||
},
|
||||
TableOperation::TextStyleModify { config: crate::host::TextStyleConfig { name: "T".into(), font_file: Some("romans".into()), ..Default::default() } },
|
||||
TableOperation::DimStyleCreate { name: "D".into(), copy_from: Some("Standard".into()), properties: "{\"dimscale\":2}".into() },
|
||||
TableOperation::DimStyleModify { name: "D".into(), properties: "{}".into() },
|
||||
TableOperation::StyleRename { kind: crate::host::TableStyleKind::Text, from: "A".into(), to: "B".into() },
|
||||
TableOperation::StyleDelete { kind: crate::host::TableStyleKind::Dim, name: "A".into() },
|
||||
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)],
|
||||
base_point: [1.0, 2.0, 3.0],
|
||||
erase_originals: true,
|
||||
description: Some("d".into()),
|
||||
},
|
||||
TableOperation::BlockModify { name: "B".into(), description: None, explodable: Some(false), scale_uniformly: Some(true) },
|
||||
TableOperation::BlockRename { from: "B".into(), to: "C".into() },
|
||||
TableOperation::BlockDelete { name: "C".into() },
|
||||
TableOperation::BlockEntityAdd {
|
||||
block: "C".into(),
|
||||
entity: acadrust::EntityType::Line(acadrust::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]) },
|
||||
TableOperation::LinetypeRename { from: "L".into(), to: "M".into() },
|
||||
TableOperation::LinetypeDelete { name: "M".into() },
|
||||
TableOperation::LayoutCreate { name: "S".into() },
|
||||
TableOperation::LayoutRename { from: "S".into(), to: "T".into() },
|
||||
TableOperation::LayoutDelete { name: "T".into() },
|
||||
TableOperation::LayoutSetCurrent { name: "Model".into() },
|
||||
TableOperation::LayoutSetPage { name: "S".into(), paper_size: Some([420.0, 297.0]), rotation: Some(90), scale: Some([1.0, 50.0]) },
|
||||
] {
|
||||
let bytes = bincode::serialize(&PluginRequest::TableOperation { operation: operation.clone() }).unwrap();
|
||||
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())] {
|
||||
let bytes = bincode::serialize(&PluginResponse::TableResult(result.clone())).unwrap();
|
||||
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
|
||||
PluginResponse::TableResult(decoded) if decoded == result));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solid_operation_request_and_response_roundtrip() {
|
||||
use crate::host::{SolidOperation, SolidPrimitive};
|
||||
for operation in [
|
||||
SolidOperation::Create {
|
||||
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::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),
|
||||
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 },
|
||||
] {
|
||||
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())] {
|
||||
let bytes = bincode::serialize(&PluginResponse::SolidResult(result.clone())).unwrap();
|
||||
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
|
||||
PluginResponse::SolidResult(decoded) if decoded == result));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selection_request_and_response_roundtrip() {
|
||||
let request = PluginRequest::SetSelection { handles: vec![acadrust::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)]);
|
||||
let bytes = bincode::serialize(&response).unwrap();
|
||||
assert!(matches!(bincode::deserialize::<PluginResponse>(&bytes).unwrap(),
|
||||
PluginResponse::Selection(handles) if handles == vec![acadrust::Handle::new(9)]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_notification_roundtrip() {
|
||||
let frame = PluginToHostV4::Notification(NotificationEnvelope {
|
||||
|
||||
Reference in new issue
Block a user