nobble_addon_sdk/lib.rs
1//! Write a Nobble addon.
2//!
3//! An addon is whatever knows how to make another application do something —
4//! skip a track, switch an OBS scene, start a Fusion 360 command. You implement
5//! [`Addon`], hand it to [`run`], and the daemon does the rest.
6//!
7//! ```no_run
8//! use nobble_addon_sdk::{Addon, AddonAction, AddonError, Availability, Invocation, Trigger, run};
9//!
10//! struct Hello;
11//!
12//! const ACTIONS: &[AddonAction] = &[AddonAction {
13//! id: "wave",
14//! name: "Wave",
15//! description: "Says hello.",
16//! trigger: Trigger::Momentary,
17//! params: &[],
18//! // Everything this action does not need. Finishing a declaration with
19//! // `..BASE` is how a later field costs you nothing.
20//! ..AddonAction::BASE
21//! }];
22//!
23//! impl Addon for Hello {
24//! fn id(&self) -> &'static str { "hello" }
25//! fn name(&self) -> &'static str { "Hello" }
26//! fn description(&self) -> &'static str { "An addon that waves." }
27//! fn actions(&self) -> &'static [AddonAction] { ACTIONS }
28//! fn availability(&self) -> Availability { Availability::Ready }
29//! fn perform(&mut self, action: &str, _i: &Invocation<'_>) -> Result<(), AddonError> {
30//! match action {
31//! "wave" => Ok(()),
32//! other => Err(AddonError::NoSuchAction(other.to_owned())),
33//! }
34//! }
35//! }
36//!
37//! fn main() { run(Hello); }
38//! ```
39//!
40//! # Why this is a separate process
41//!
42//! [ADR-0016](../../../docs/decisions/0016-addon-process-boundary.md). An addon
43//! runs as a child of the daemon and speaks the protocol in [`protocol`] over
44//! its stdin and stdout. That is what makes FR-045 true — a crash, a hang or a
45//! leak in your addon cannot reach input, MIDI, or anybody else's addon — and
46//! it is what lets [`Credentials`] be scoped to you rather than handed out as a
47//! key to everyone's.
48//!
49//! You do not write any of that. [`run`] owns the loop and the framing; you
50//! write the trait.
51//!
52//! # What is deliberately not here
53//!
54//! No device, input, layout or module types. An addon contributes actions,
55//! signals, settings and configuration UI (FR-047) and has no business knowing
56//! what an `InputId` is. If you find yourself needing one, that is a gap in
57//! this SDK to be reported rather than routed around — FR-050 says official
58//! addons get no private interfaces either, so the gap is real for everyone.
59
60mod addon;
61pub mod protocol;
62mod run;
63
64pub use addon::{
65 Addon, AddonAction, AddonChoice, AddonChoices, AddonError, AddonParam, AddonSetting,
66 AddonSignal, Availability, Choice, CredentialHandle, Credentials, DeviceAction,
67 DeviceKeystroke, FADER_MAX, Invocation, ParamKind, ParamValue, Permission, Reading, Store,
68 StoreHandle, Trigger, app_matches,
69};
70pub use run::run;