refactor(plugin): extract ocs_plugin_api crate for the stable add-on contract
Move the dependency-free, semver-versioned half of the plugin contract into a standalone workspace crate `crates/ocs_plugin_api`: - manifest types: PluginManifest, ApiVersion, API_VERSION - ribbon vocabulary: CadModule trait + ToolDef/RibbonGroup/RibbonItem/IconKind/ ModuleEvent/StyleKey The host re-exports them from `crate::plugin::manifest` and `crate::modules`, so every existing call site is unchanged. The acadrust-typed runtime surface (HostSession) stays in the host binary; lifting it behind a HostApi trait in the same crate is the remaining phase-1b step. Part of #100. Docs updated in docs/plugin-architecture.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7a4ddbc62e
commit
72ec1146df
9 changed files with 280 additions and 154 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -24,6 +24,7 @@ dependencies = [
|
|||
"image",
|
||||
"inventory",
|
||||
"lzma-sys",
|
||||
"ocs_plugin_api",
|
||||
"open",
|
||||
"printpdf",
|
||||
"rayon",
|
||||
|
|
@ -3646,6 +3647,10 @@ dependencies = [
|
|||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ocs_plugin_api"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ version = "0.5.6"
|
|||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/ocs_plugin_api"]
|
||||
|
||||
[dependencies]
|
||||
# Stable, dependency-free add-on contract (manifest + ribbon/CadModule types).
|
||||
# Plugin authors target this crate's semver, not OpenCADStudio internals.
|
||||
ocs_plugin_api = { path = "crates/ocs_plugin_api" }
|
||||
# vtkio (pulled in transitively via truck-meshalgo) depends on xz2 → lzma-sys,
|
||||
# which by default links the system liblzma dynamically. On macOS that bakes a
|
||||
# Homebrew dylib path (/opt/homebrew/.../liblzma.5.dylib) into the binary, so
|
||||
|
|
|
|||
8
crates/ocs_plugin_api/Cargo.toml
Normal file
8
crates/ocs_plugin_api/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "ocs_plugin_api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Stable, dependency-free contract for Open CAD Studio add-on packages: plugin manifest, version handshake, and ribbon/CadModule types."
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
25
crates/ocs_plugin_api/src/lib.rs
Normal file
25
crates/ocs_plugin_api/src/lib.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//! # Open CAD Studio plugin API
|
||||
//!
|
||||
//! 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
|
||||
//! depend on it cheaply.
|
||||
//!
|
||||
//! Two pieces live here:
|
||||
//!
|
||||
//! - [`manifest`] — plugin identity ([`PluginManifest`]) and the host ABI
|
||||
//! version handshake ([`ApiVersion`]).
|
||||
//! - [`ribbon`] — the [`CadModule`] trait and the plain-data ribbon types
|
||||
//! ([`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
|
||||
//! binary for now; see `docs/plugin-architecture.md` (phase 1b).
|
||||
|
||||
pub mod manifest;
|
||||
pub mod ribbon;
|
||||
|
||||
pub use manifest::{ApiVersion, PluginManifest, API_VERSION};
|
||||
pub use ribbon::{
|
||||
CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem, StyleKey, ToolDef,
|
||||
};
|
||||
51
crates/ocs_plugin_api/src/manifest.rs
Normal file
51
crates/ocs_plugin_api/src/manifest.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//! Plugin identity and capability declaration.
|
||||
|
||||
/// Host plugin API version. Bump when the host runtime surface breaks
|
||||
/// compatibility.
|
||||
pub const API_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ApiVersion {
|
||||
pub major: u32,
|
||||
}
|
||||
|
||||
impl ApiVersion {
|
||||
pub const CURRENT: Self = Self { major: API_VERSION };
|
||||
|
||||
pub fn is_compatible_with(host: ApiVersion) -> bool {
|
||||
Self::CURRENT.major == host.major
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn current_matches_const() {
|
||||
assert_eq!(ApiVersion::CURRENT.major, API_VERSION);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_major_is_compatible() {
|
||||
assert!(ApiVersion::is_compatible_with(ApiVersion::CURRENT));
|
||||
assert!(!ApiVersion::is_compatible_with(ApiVersion {
|
||||
major: API_VERSION + 1,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Static metadata every plugin supplies at registration time.
|
||||
/// Keep fields in sync with `plugin.toml` beside the package.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PluginManifest {
|
||||
pub id: &'static str,
|
||||
pub name: &'static str,
|
||||
pub version: &'static str,
|
||||
pub description: &'static str,
|
||||
pub api_version: ApiVersion,
|
||||
/// Sort key for add-on ribbon tabs (lower = further left among plugins).
|
||||
pub ribbon_order: i32,
|
||||
pub xdata_apps: &'static [&'static str],
|
||||
pub command_prefixes: &'static [&'static str],
|
||||
}
|
||||
159
crates/ocs_plugin_api/src/ribbon.rs
Normal file
159
crates/ocs_plugin_api/src/ribbon.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
//! Ribbon description types — the plain-data vocabulary a [`CadModule`] uses to
|
||||
//! declare its tab. No UI-framework dependency: the host renders these.
|
||||
|
||||
// ── Events ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Events a module tool can emit to the host application.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ModuleEvent {
|
||||
/// Fire a named CAD command (e.g. "LINE", "CIRCLE").
|
||||
Command(String),
|
||||
/// Open the OS file dialog.
|
||||
OpenFileDialog,
|
||||
/// Remove all loaded models from the scene.
|
||||
#[allow(dead_code)]
|
||||
ClearModels,
|
||||
/// Toggle wireframe rendering.
|
||||
SetWireframe(bool),
|
||||
/// Toggle the layer manager panel.
|
||||
ToggleLayers,
|
||||
}
|
||||
|
||||
// ── Data types ────────────────────────────────────────────────────────────
|
||||
|
||||
/// Icon source for a ribbon tool button.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum IconKind {
|
||||
/// Unicode glyph rendered as text (fast, no file needed).
|
||||
Glyph(&'static str),
|
||||
/// Raw SVG bytes embedded at compile time via `include_bytes!`.
|
||||
Svg(&'static [u8]),
|
||||
}
|
||||
|
||||
/// A single tool button shown in the ribbon.
|
||||
#[derive(Clone)]
|
||||
pub struct ToolDef {
|
||||
/// Unique command id, e.g. "LINE".
|
||||
pub id: &'static str,
|
||||
/// Short label shown under the icon.
|
||||
pub label: &'static str,
|
||||
/// Icon — either a unicode glyph or embedded SVG bytes.
|
||||
pub icon: IconKind,
|
||||
/// Event emitted when the tool is clicked.
|
||||
pub event: ModuleEvent,
|
||||
}
|
||||
|
||||
/// One item in a ribbon group — plain button or dropdown, in small (1-row) or large (3-row) size.
|
||||
#[derive(Clone)]
|
||||
pub enum RibbonItem {
|
||||
/// 1-row button — icon only, no label.
|
||||
Tool(ToolDef),
|
||||
/// 3-row button — icon + label below; full ribbon height.
|
||||
LargeTool(ToolDef),
|
||||
/// 1-row dropdown — icon + ▾ on right, no label.
|
||||
Dropdown {
|
||||
id: &'static str,
|
||||
icon: IconKind,
|
||||
items: Vec<(&'static str, &'static str, IconKind)>,
|
||||
default: &'static str,
|
||||
},
|
||||
/// 3-row dropdown — icon + label + ▾ below label; full ribbon height.
|
||||
LargeDropdown {
|
||||
id: &'static str,
|
||||
label: &'static str,
|
||||
icon: IconKind,
|
||||
items: Vec<(&'static str, &'static str, IconKind)>,
|
||||
default: &'static str,
|
||||
},
|
||||
/// Layer combo + two rows of small tools below.
|
||||
/// row2: operates on the layer of a selected object (off/freeze/lock/make-current)
|
||||
/// row3: all-layers operations + match (on/thaw/unlock/match)
|
||||
LayerComboGroup {
|
||||
row2: Vec<ToolDef>,
|
||||
row3: Vec<ToolDef>,
|
||||
},
|
||||
/// Match Properties (large button) + Color / Linetype / Lineweight combos on the right.
|
||||
PropertiesGroup { match_prop: ToolDef },
|
||||
/// A style selector combobox (text / dim / mleader / table style) with
|
||||
/// optional small tool rows below it.
|
||||
StyleComboGroup {
|
||||
/// Which style domain this combo controls.
|
||||
style_key: StyleKey,
|
||||
/// Unique dropdown id (must be unique across the ribbon).
|
||||
combo_id: &'static str,
|
||||
/// Optional command to run when the user opens the style manager.
|
||||
manager_cmd: Option<&'static str>,
|
||||
/// Small tool rows rendered below the combo (0–2 rows).
|
||||
rows: Vec<Vec<ToolDef>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Identifies which style list a `StyleComboGroup` refers to.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum StyleKey {
|
||||
TextStyle,
|
||||
DimStyle,
|
||||
MLeaderStyle,
|
||||
TableStyle,
|
||||
}
|
||||
|
||||
impl From<ToolDef> for RibbonItem {
|
||||
fn from(t: ToolDef) -> Self {
|
||||
RibbonItem::Tool(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// A named group of tool buttons shown together in the ribbon.
|
||||
pub struct RibbonGroup {
|
||||
pub title: &'static str,
|
||||
pub tools: Vec<RibbonItem>,
|
||||
}
|
||||
|
||||
// ── Trait ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A CAD module owns a set of ribbon groups shown when its tab is active.
|
||||
/// Each module is a stateless unit struct — all UI state lives in Ribbon.
|
||||
pub trait CadModule: Send + Sync {
|
||||
#[allow(dead_code)]
|
||||
fn id(&self) -> &'static str;
|
||||
fn title(&self) -> &'static str;
|
||||
fn ribbon_groups(&self) -> Vec<RibbonGroup>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tool_def_converts_into_small_tool() {
|
||||
let tool = ToolDef {
|
||||
id: "LINE",
|
||||
label: "Line",
|
||||
icon: IconKind::Glyph("/"),
|
||||
event: ModuleEvent::Command("LINE".to_string()),
|
||||
};
|
||||
assert!(matches!(RibbonItem::from(tool), RibbonItem::Tool(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cad_module_is_object_safe() {
|
||||
struct Demo;
|
||||
impl CadModule for Demo {
|
||||
fn id(&self) -> &'static str {
|
||||
"demo"
|
||||
}
|
||||
fn title(&self) -> &'static str {
|
||||
"Demo"
|
||||
}
|
||||
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
|
||||
vec![RibbonGroup {
|
||||
title: "Group",
|
||||
tools: vec![],
|
||||
}]
|
||||
}
|
||||
}
|
||||
let m: Box<dyn CadModule> = Box::new(Demo);
|
||||
assert_eq!(m.title(), "Demo");
|
||||
assert_eq!(m.ribbon_groups().len(), 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ This document is the **authoritative spec** for how add-on packages integrate wi
|
|||
| Goal | Rationale |
|
||||
|------|-----------|
|
||||
| **One package, one registration** | Ribbon tab, commands, and manifest ship together — no duplicate hooks in `build.rs` and `commands.rs`. |
|
||||
| **Stable host surface** | Plugin authors target `HostSession` / future `ocs_plugin_api` with semver, not `OpenCADStudio` internals. |
|
||||
| **Stable host surface** | Plugin authors target the semver-versioned `ocs_plugin_api` crate (manifest + ribbon today) and `HostSession`, not `OpenCADStudio` internals. |
|
||||
| **Open-source add-on ergonomics** | Separate git repo + workspace crate is supported; in-tree built-ins use the same layout. |
|
||||
| **DWG round-trip** | Domain data on entities (XDATA), not opaque plugin databases. |
|
||||
| **Engine reuse** | Headless crates (`stormsewer`, …) run in WASM/CLI without the CAD host. |
|
||||
|
|
@ -69,7 +69,7 @@ This document is the **authoritative spec** for how add-on packages integrate wi
|
|||
|------|-----------------|
|
||||
| `metadata.txt` (name, version, author, …) | `plugin.toml` beside the package |
|
||||
| `classFactory(iface)` in `__init__.py` | `inventory::submit!(PluginRegistration { construct })` in `register.rs` |
|
||||
| `iface` stable API | `HostSession` → future `ocs_plugin_api` crate |
|
||||
| `iface` stable API | `ocs_plugin_api` crate (manifest + ribbon) + `HostSession` |
|
||||
| User folder `…/python/plugins/<id>/` | Phase 2: `%APPDATA%/OpenCADStudio/plugins/<id>/` |
|
||||
| Plugin repository (plugins.qgis.org) | Future: curated index; today = git + in-tree |
|
||||
| `qgisMinimumVersion` | `api_version` in manifest (host ABI major) |
|
||||
|
|
@ -182,7 +182,14 @@ Plugins use `HostSession`, not `OpenCADStudio`:
|
|||
| Command line | `push_info`, `push_output`, `push_error`, `set_active_command` |
|
||||
| Undo / dirty | `push_undo`, `set_dirty` |
|
||||
|
||||
Phase 1b: extract to `crates/ocs_plugin_api` with semver when the surface stabilizes.
|
||||
**Status:** The dependency-free half of the contract — `PluginManifest` /
|
||||
`ApiVersion` (manifest) and `CadModule` + the ribbon types (`ToolDef`,
|
||||
`RibbonGroup`, …) — now lives in the standalone, semver-versioned
|
||||
[`crates/ocs_plugin_api`](../crates/ocs_plugin_api) crate. The host re-exports it
|
||||
(`crate::plugin::manifest`, `crate::modules`) so in-tree paths are unchanged.
|
||||
The `acadrust`-typed runtime surface in the table above (`document_mut`,
|
||||
`add_entity`, `set_active_command`, …) stays in the host binary for now; lifting
|
||||
it behind a `HostApi` trait in the same crate is the remaining phase-1b step.
|
||||
|
||||
### Command routing
|
||||
|
||||
|
|
@ -247,7 +254,7 @@ This mirrors QGIS: the application ships core menus; plugins add tabs/tools with
|
|||
- [x] Per-tab `plugin_state`
|
||||
- [x] Storm Sewer off `commands.rs` monolith
|
||||
- [x] Single registration (`plugin.toml` + `BuiltinPlugin::ribbon`)
|
||||
- [ ] Extract `ocs_plugin_api` crate
|
||||
- [~] Extract `ocs_plugin_api` crate — manifest + ribbon/`CadModule` done; `acadrust`-typed host surface pending
|
||||
- [ ] Plugin manager UI stub (list installed, versions)
|
||||
|
||||
### Phase 2 — Dynamic loading (desktop)
|
||||
|
|
@ -321,7 +328,7 @@ OpenCADStudio/
|
|||
storm_sewer/ # add-on (has plugin.toml)
|
||||
crates/
|
||||
stormsewer/ # Layer C engine
|
||||
ocs_plugin_api/ # (phase 1b) stable host API
|
||||
ocs_plugin_api/ # stable contract: manifest + ribbon (host API: phase 1b)
|
||||
plugins/ # (phase 2) third-party cdylibs
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -13,124 +13,14 @@
|
|||
// - mod.rs : module definition (ribbon groups + tool layout)
|
||||
// - <tool>.rs : one file per tool (ribbon def + future command logic)
|
||||
|
||||
// ── Events ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Events a module tool can emit to the host application.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ModuleEvent {
|
||||
/// Fire a named CAD command (e.g. "LINE", "CIRCLE").
|
||||
Command(String),
|
||||
/// Open the OS file dialog.
|
||||
OpenFileDialog,
|
||||
/// Remove all loaded models from the scene.
|
||||
#[allow(dead_code)]
|
||||
ClearModels,
|
||||
/// Toggle wireframe rendering.
|
||||
SetWireframe(bool),
|
||||
/// Toggle the layer manager panel.
|
||||
ToggleLayers,
|
||||
}
|
||||
|
||||
// ── Data types ────────────────────────────────────────────────────────────
|
||||
|
||||
/// Icon source for a ribbon tool button.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum IconKind {
|
||||
/// Unicode glyph rendered as text (fast, no file needed).
|
||||
Glyph(&'static str),
|
||||
/// Raw SVG bytes embedded at compile time via `include_bytes!`.
|
||||
Svg(&'static [u8]),
|
||||
}
|
||||
|
||||
/// A single tool button shown in the ribbon.
|
||||
#[derive(Clone)]
|
||||
pub struct ToolDef {
|
||||
/// Unique command id, e.g. "LINE".
|
||||
pub id: &'static str,
|
||||
/// Short label shown under the icon.
|
||||
pub label: &'static str,
|
||||
/// Icon — either a unicode glyph or embedded SVG bytes.
|
||||
pub icon: IconKind,
|
||||
/// Event emitted when the tool is clicked.
|
||||
pub event: ModuleEvent,
|
||||
}
|
||||
|
||||
/// One item in a ribbon group — plain button or dropdown, in small (1-row) or large (3-row) size.
|
||||
#[derive(Clone)]
|
||||
pub enum RibbonItem {
|
||||
/// 1-row button — icon only, no label.
|
||||
Tool(ToolDef),
|
||||
/// 3-row button — icon + label below; full ribbon height.
|
||||
LargeTool(ToolDef),
|
||||
/// 1-row dropdown — icon + ▾ on right, no label.
|
||||
Dropdown {
|
||||
id: &'static str,
|
||||
icon: IconKind,
|
||||
items: Vec<(&'static str, &'static str, IconKind)>,
|
||||
default: &'static str,
|
||||
},
|
||||
/// 3-row dropdown — icon + label + ▾ below label; full ribbon height.
|
||||
LargeDropdown {
|
||||
id: &'static str,
|
||||
label: &'static str,
|
||||
icon: IconKind,
|
||||
items: Vec<(&'static str, &'static str, IconKind)>,
|
||||
default: &'static str,
|
||||
},
|
||||
/// Layer combo + two rows of small tools below.
|
||||
/// row2: operates on the layer of a selected object (off/freeze/lock/make-current)
|
||||
/// row3: all-layers operations + match (on/thaw/unlock/match)
|
||||
LayerComboGroup {
|
||||
row2: Vec<ToolDef>,
|
||||
row3: Vec<ToolDef>,
|
||||
},
|
||||
/// Match Properties (large button) + Color / Linetype / Lineweight combos on the right.
|
||||
PropertiesGroup { match_prop: ToolDef },
|
||||
/// A style selector combobox (text / dim / mleader / table style) with
|
||||
/// optional small tool rows below it.
|
||||
StyleComboGroup {
|
||||
/// Which style domain this combo controls.
|
||||
style_key: StyleKey,
|
||||
/// Unique dropdown id (must be unique across the ribbon).
|
||||
combo_id: &'static str,
|
||||
/// Optional command to run when the user opens the style manager.
|
||||
manager_cmd: Option<&'static str>,
|
||||
/// Small tool rows rendered below the combo (0–2 rows).
|
||||
rows: Vec<Vec<ToolDef>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Identifies which style list a `StyleComboGroup` refers to.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum StyleKey {
|
||||
TextStyle,
|
||||
DimStyle,
|
||||
MLeaderStyle,
|
||||
TableStyle,
|
||||
}
|
||||
|
||||
impl From<ToolDef> for RibbonItem {
|
||||
fn from(t: ToolDef) -> Self {
|
||||
RibbonItem::Tool(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// A named group of tool buttons shown together in the ribbon.
|
||||
pub struct RibbonGroup {
|
||||
pub title: &'static str,
|
||||
pub tools: Vec<RibbonItem>,
|
||||
}
|
||||
|
||||
// ── Trait ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A CAD module owns a set of ribbon groups shown when its tab is active.
|
||||
/// Each module is a stateless unit struct — all UI state lives in Ribbon.
|
||||
pub trait CadModule: Send + Sync {
|
||||
#[allow(dead_code)]
|
||||
fn id(&self) -> &'static str;
|
||||
fn title(&self) -> &'static str;
|
||||
fn ribbon_groups(&self) -> Vec<RibbonGroup>;
|
||||
}
|
||||
// ── Ribbon vocabulary (CadModule, ToolDef, RibbonGroup, …) ─────────────────
|
||||
//
|
||||
// These types moved to the dependency-free `ocs_plugin_api` crate so add-ons
|
||||
// can target a semver-stable contract. Re-exported here to keep the long-used
|
||||
// `crate::modules::{CadModule, ToolDef, …}` paths stable across the codebase.
|
||||
pub use ocs_plugin_api::ribbon::{
|
||||
CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem, StyleKey, ToolDef,
|
||||
};
|
||||
|
||||
// ── Module declarations ───────────────────────────────────────────────────
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,7 @@
|
|||
// Plugin identity and capability declaration.
|
||||
//! Plugin identity and capability declaration.
|
||||
//!
|
||||
//! These types now live in the standalone, dependency-free `ocs_plugin_api`
|
||||
//! crate so external add-ons can target a semver-stable contract. Re-exported
|
||||
//! here to keep the `crate::plugin::manifest::*` path stable for in-tree use.
|
||||
|
||||
/// Host plugin API version. Bump when HostApi breaks compatibility.
|
||||
pub const API_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ApiVersion {
|
||||
pub major: u32,
|
||||
}
|
||||
|
||||
impl ApiVersion {
|
||||
pub const CURRENT: Self = Self { major: API_VERSION };
|
||||
|
||||
pub fn is_compatible_with(host: ApiVersion) -> bool {
|
||||
Self::CURRENT.major == host.major
|
||||
}
|
||||
}
|
||||
|
||||
/// Static metadata every plugin supplies at registration time.
|
||||
/// Keep fields in sync with `plugin.toml` beside the package.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PluginManifest {
|
||||
pub id: &'static str,
|
||||
pub name: &'static str,
|
||||
pub version: &'static str,
|
||||
pub description: &'static str,
|
||||
pub api_version: ApiVersion,
|
||||
/// Sort key for add-on ribbon tabs (lower = further left among plugins).
|
||||
pub ribbon_order: i32,
|
||||
pub xdata_apps: &'static [&'static str],
|
||||
pub command_prefixes: &'static [&'static str],
|
||||
}
|
||||
pub use ocs_plugin_api::manifest::*;
|
||||
|
|
|
|||
Loading…
Reference in a new issue