Skip to main content

appctl_plugin_sdk/
lib.rs

1//! `appctl-plugin-sdk`
2//!
3//! Stable schema types and the dynamic-plugin ABI for the `appctl` CLI agent.
4//!
5//! Consumers fall into two groups:
6//!
7//! * **Built-in sync plugins** inside the main `appctl` crate — they implement
8//!   the async [`SyncPlugin`] trait directly.
9//! * **Out-of-process dynamic plugins** shipped as `cdylib`s living under
10//!   `~/.appctl/plugins/` — they expose a single `#[no_mangle] extern "C"`
11//!   entry point returning a [`PluginManifest`] plus a JSON-in / JSON-out
12//!   `introspect` function. See [`declare_plugin!`].
13
14pub mod ffi;
15pub mod schema;
16
17pub use ffi::{PluginManifest, PluginVtable, SDK_ABI_VERSION};
18pub use schema::*;
19
20use anyhow::Result;
21use async_trait::async_trait;
22
23/// Runtime inputs passed to a sync plugin.
24#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
25pub struct SyncInput {
26    #[serde(default)]
27    pub source: Option<String>,
28    #[serde(default)]
29    pub base_url: Option<String>,
30    #[serde(default)]
31    pub options: serde_json::Map<String, serde_json::Value>,
32}
33
34/// A sync plugin introspects an application and returns a [`Schema`].
35///
36/// Built-in plugins in the main `appctl` crate implement this trait directly.
37/// Dynamic plugins use the C ABI in [`ffi`] and are adapted by the loader.
38#[async_trait]
39pub trait SyncPlugin: Send + Sync {
40    /// Stable identifier, e.g. `"openapi"`, `"rails"`, `"airtable"`.
41    fn name(&self) -> &str;
42
43    /// Introspect the target and build a schema.
44    async fn introspect(&self) -> Result<Schema>;
45}
46
47/// Convenience re-export for plugin authors.
48pub mod prelude {
49    pub use super::ffi::{PluginManifest, PluginVtable, SDK_ABI_VERSION};
50    pub use super::schema::*;
51    pub use super::{SyncInput, SyncPlugin};
52    pub use crate::declare_plugin;
53    pub use anyhow::{Result, anyhow, bail};
54    pub use async_trait::async_trait;
55}