The old ids live only in crates/engine/src/legacy_ids.rs (the legacy migration table): find_command maps them, so old scripts, recipes and control calls keep working, and saved shortcuts, recent and pinned commands are migrated on load (designs store no command ids). Parity maps the observed catalog ids to ours through xtask/data/fusion-ids.tsv. The agent rule now reads: our own dotted id, never another product's. Cargo.toml's repository points at github.com/bherbruck/solvecraft. Co-Authored-By: Claude Opus 5.5 <[email protected]>
172 lines
5.0 KiB
Rust
172 lines
5.0 KiB
Rust
//! The command registry. Ids are SolveCraft's own dotted ids (`solid.extrude`,
|
|
//! `sketch.rectangle.two_point`); the toolbar placement (`tab`, `panel`) follows the familiar
|
|
//! Design workspace layout. Ids used before the rename still resolve (`legacy_ids`).
|
|
|
|
mod browser;
|
|
mod clipboard;
|
|
mod component;
|
|
mod delete;
|
|
mod edit;
|
|
mod face;
|
|
mod features;
|
|
mod features_more;
|
|
pub use features::auto_operation;
|
|
mod config;
|
|
mod file;
|
|
mod inspect;
|
|
pub(crate) mod joints;
|
|
mod measure_sel;
|
|
pub(crate) mod motion;
|
|
mod parts;
|
|
mod section;
|
|
pub(crate) use measure_sel::measure_items;
|
|
#[cfg(test)]
|
|
mod kernel_heal_tests;
|
|
mod parameters;
|
|
mod plastic;
|
|
mod sheet;
|
|
mod sketch;
|
|
mod sketch_3d;
|
|
mod sketch_canvas;
|
|
mod sketch_constraints;
|
|
mod sketch_create;
|
|
mod sketch_freeform;
|
|
mod sketch_import;
|
|
mod sketch_inspect;
|
|
mod sketch_modify;
|
|
mod sketch_project;
|
|
mod surface;
|
|
mod surface_analysis;
|
|
|
|
use serde::Serialize;
|
|
use serde_json::Value;
|
|
|
|
use crate::{Result, Session};
|
|
|
|
pub type Run = fn(&mut Session, &Value) -> Result<Value>;
|
|
pub type Enabled = fn(&Session) -> std::result::Result<(), String>;
|
|
|
|
pub struct CommandSpec {
|
|
pub id: &'static str,
|
|
pub label: &'static str,
|
|
/// Toolbar tab (`SOLID`, `SKETCH`…); empty = not on the toolbar.
|
|
pub tab: &'static str,
|
|
/// Toolbar panel (`CREATE`, `MODIFY`…).
|
|
pub panel: &'static str,
|
|
/// Icon name for the UI (drawn in code).
|
|
pub icon: &'static str,
|
|
pub shortcut: Option<&'static str>,
|
|
/// One-line parameter documentation.
|
|
pub params: &'static str,
|
|
pub enabled: Enabled,
|
|
pub run: Run,
|
|
pub undoable: bool,
|
|
}
|
|
|
|
impl CommandSpec {
|
|
pub const fn new(id: &'static str, label: &'static str, run: Run) -> Self {
|
|
CommandSpec { id, label, tab: "", panel: "", icon: "", shortcut: None, params: "", enabled: always, run, undoable: true }
|
|
}
|
|
pub const fn at(mut self, tab: &'static str, panel: &'static str) -> Self {
|
|
self.tab = tab;
|
|
self.panel = panel;
|
|
self
|
|
}
|
|
pub const fn icon(mut self, i: &'static str) -> Self {
|
|
self.icon = i;
|
|
self
|
|
}
|
|
pub const fn key(mut self, k: &'static str) -> Self {
|
|
self.shortcut = Some(k);
|
|
self
|
|
}
|
|
pub const fn params(mut self, p: &'static str) -> Self {
|
|
self.params = p;
|
|
self
|
|
}
|
|
pub const fn enabled(mut self, e: Enabled) -> Self {
|
|
self.enabled = e;
|
|
self
|
|
}
|
|
pub const fn noundo(mut self) -> Self {
|
|
self.undoable = false;
|
|
self
|
|
}
|
|
pub fn info(&self, s: &Session) -> CommandInfo {
|
|
let e = (self.enabled)(s);
|
|
CommandInfo {
|
|
id: self.id,
|
|
label: self.label,
|
|
tab: self.tab,
|
|
panel: self.panel,
|
|
icon: self.icon,
|
|
shortcut: self.shortcut,
|
|
params: self.params,
|
|
enabled: e.is_ok(),
|
|
disabled_reason: e.err(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize)]
|
|
pub struct CommandInfo {
|
|
pub id: &'static str,
|
|
pub label: &'static str,
|
|
pub tab: &'static str,
|
|
pub panel: &'static str,
|
|
pub icon: &'static str,
|
|
pub shortcut: Option<&'static str>,
|
|
pub params: &'static str,
|
|
pub enabled: bool,
|
|
pub disabled_reason: Option<String>,
|
|
}
|
|
|
|
pub fn always(_: &Session) -> std::result::Result<(), String> {
|
|
Ok(())
|
|
}
|
|
|
|
pub fn in_sketch(s: &Session) -> std::result::Result<(), String> {
|
|
if s.active_sketch.is_some() { Ok(()) } else { Err("no sketch is being edited (Create Sketch or Edit Sketch first)".into()) }
|
|
}
|
|
|
|
/// Every command, in toolbar order.
|
|
pub fn command_specs() -> Vec<&'static CommandSpec> {
|
|
let mut v: Vec<&'static CommandSpec> = Vec::new();
|
|
v.extend(sketch::COMMANDS.iter());
|
|
v.extend(sketch_create::COMMANDS.iter());
|
|
v.extend(sketch_constraints::COMMANDS.iter());
|
|
v.extend(sketch_import::COMMANDS.iter());
|
|
v.extend(sketch_inspect::COMMANDS.iter());
|
|
v.extend(sketch_canvas::COMMANDS.iter());
|
|
v.extend(sketch_3d::COMMANDS.iter());
|
|
v.extend(surface_analysis::COMMANDS.iter());
|
|
v.extend(surface::COMMANDS.iter());
|
|
v.extend(sketch_modify::COMMANDS.iter());
|
|
v.extend(sketch_project::COMMANDS.iter());
|
|
v.extend(features::COMMANDS.iter());
|
|
v.extend(features_more::COMMANDS.iter());
|
|
v.extend(sheet::COMMANDS.iter());
|
|
v.extend(plastic::COMMANDS.iter());
|
|
v.extend(component::COMMANDS.iter());
|
|
v.extend(joints::COMMANDS.iter());
|
|
v.extend(motion::COMMANDS.iter());
|
|
v.extend(config::COMMANDS.iter());
|
|
v.extend(parts::COMMANDS.iter());
|
|
v.extend(browser::COMMANDS.iter());
|
|
v.extend(delete::COMMANDS.iter());
|
|
v.extend(edit::COMMANDS.iter());
|
|
v.extend(clipboard::COMMANDS.iter());
|
|
v.extend(parameters::COMMANDS.iter());
|
|
v.extend(file::COMMANDS.iter());
|
|
v.extend(inspect::COMMANDS.iter());
|
|
v.extend(section::COMMANDS.iter());
|
|
v
|
|
}
|
|
|
|
pub fn find_command(id: &str) -> Option<&'static CommandSpec> {
|
|
let all = command_specs();
|
|
// Ids from before the rename still find their command.
|
|
let id = crate::legacy_ids::current_id(id);
|
|
all.iter().find(|c| c.id == id).or_else(|| all.iter().find(|c| c.id.eq_ignore_ascii_case(id))).copied()
|
|
}
|