Skip to main content

pixelflow_core/
plugin_abi.rs

1//! Versioned C-compatible ABI types shared by host and plugins.
2
3/// Current plugin ABI version.
4pub const PIXELFLOW_ABI_VERSION: u32 = 1;
5
6/// Versioned entry symbol exported by PixelFlow plugins.
7pub const PIXELFLOW_PLUGIN_ENTRY_SYMBOL: &[u8] = b"pixelflow_plugin_entry_v1\0";
8
9/// Borrowed UTF-8 string passed across ABI calls.
10#[repr(C)]
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct PixelflowStringView {
13    /// Pointer to UTF-8 bytes.
14    pub ptr: *const u8,
15    /// Byte length.
16    pub len: usize,
17}
18
19impl PixelflowStringView {
20    /// Creates a borrowed view from a Rust string.
21    #[must_use]
22    pub const fn from_rust_str(value: &str) -> Self {
23        Self {
24            ptr: value.as_ptr(),
25            len: value.len(),
26        }
27    }
28}
29
30/// ABI error category.
31#[repr(C)]
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub enum PixelflowErrorCategory {
34    /// No error.
35    None = 0,
36    /// Plugin loading, registration, or execution failure.
37    Plugin = 1,
38    /// Host-side internal invariant violation.
39    Internal = 2,
40}
41
42/// ABI metadata value kind.
43#[repr(C)]
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub enum PixelflowMetadataKind {
46    /// Boolean metadata.
47    Bool = 1,
48    /// Integer metadata.
49    Int = 2,
50    /// Floating-point metadata.
51    Float = 3,
52    /// UTF-8 string metadata.
53    String = 4,
54    /// Array metadata.
55    Array = 5,
56    /// Rational metadata.
57    Rational = 6,
58    /// Binary blob metadata.
59    Blob = 7,
60}
61
62/// Structured ABI status returned by plugin and host callbacks.
63#[repr(C)]
64#[derive(Clone, Copy, Debug, Eq, PartialEq)]
65pub struct PixelflowStatus {
66    /// Struct size in bytes.
67    pub size: u32,
68    /// ABI version used by this struct.
69    pub version: u32,
70    /// Zero means success; non-zero means failure.
71    pub status_code: i32,
72    /// Structured error category.
73    pub category: PixelflowErrorCategory,
74    /// Stable dotted error code.
75    pub error_code: PixelflowStringView,
76    /// Human-readable message.
77    pub message: PixelflowStringView,
78    /// Reserved fields for future compatible extension.
79    pub reserved: [usize; 4],
80}
81
82impl PixelflowStatus {
83    /// Returns success status.
84    #[must_use]
85    pub const fn ok() -> Self {
86        Self {
87            size: std::mem::size_of::<Self>() as u32,
88            version: PIXELFLOW_ABI_VERSION,
89            status_code: 0,
90            category: PixelflowErrorCategory::None,
91            error_code: PixelflowStringView::from_rust_str(""),
92            message: PixelflowStringView::from_rust_str(""),
93            reserved: [0; 4],
94        }
95    }
96
97    /// Returns plugin failure status with static diagnostic strings.
98    #[must_use]
99    pub const fn plugin_error(code: &'static str, message: &'static str) -> Self {
100        Self {
101            status_code: 1,
102            category: PixelflowErrorCategory::Plugin,
103            error_code: PixelflowStringView::from_rust_str(code),
104            message: PixelflowStringView::from_rust_str(message),
105            ..Self::ok()
106        }
107    }
108
109    /// Returns true when status is success.
110    #[must_use]
111    pub const fn is_ok(self) -> bool {
112        self.status_code == 0
113    }
114}
115
116/// Opaque host registrar handle.
117#[repr(C)]
118pub struct PixelflowRegistrar {
119    _private: [u8; 0],
120}
121
122/// C-compatible filter descriptor.
123#[repr(C)]
124#[derive(Clone, Copy, Debug)]
125pub struct PixelflowFilterDescriptorV1 {
126    /// Struct size in bytes.
127    pub size: u32,
128    /// ABI version used by this struct.
129    pub version: u32,
130    /// Stable filter name.
131    pub name: PixelflowStringView,
132    /// Publisher namespace.
133    pub publisher: PixelflowStringView,
134    /// Plugin namespace.
135    pub plugin: PixelflowStringView,
136    /// Reserved fields for future compatible extension.
137    pub reserved: [usize; 4],
138}
139
140/// Host callback table passed to plugins.
141#[repr(C)]
142#[derive(Clone, Copy)]
143pub struct PixelflowHostApiV1 {
144    /// Struct size in bytes.
145    pub size: u32,
146    /// ABI version used by this struct.
147    pub version: u32,
148    /// Registers one filter.
149    pub register_filter: unsafe extern "C" fn(
150        *mut PixelflowRegistrar,
151        *const PixelflowFilterDescriptorV1,
152    ) -> PixelflowStatus,
153    /// Registers one metadata key.
154    pub register_metadata_key: unsafe extern "C" fn(
155        *mut PixelflowRegistrar,
156        PixelflowStringView,
157        PixelflowMetadataKind,
158    ) -> PixelflowStatus,
159    /// Emits host log message.
160    pub log: unsafe extern "C" fn(*mut PixelflowRegistrar, u32, PixelflowStringView),
161    /// Reserved fields for future compatible extension.
162    pub reserved: [usize; 4],
163}
164
165/// Plugin callback table filled by entry symbol.
166#[repr(C)]
167#[derive(Clone, Copy)]
168pub struct PixelflowPluginApiV1 {
169    /// Struct size in bytes.
170    pub size: u32,
171    /// ABI version used by this struct.
172    pub version: u32,
173    /// Returns stable plugin name.
174    pub plugin_name: unsafe extern "C" fn() -> PixelflowStringView,
175    /// Registers plugin capabilities with host.
176    pub register:
177        unsafe extern "C" fn(*const PixelflowHostApiV1, *mut PixelflowRegistrar) -> PixelflowStatus,
178    /// Reserved fields for future compatible extension.
179    pub reserved: [usize; 5],
180}
181
182/// Type of `pixelflow_plugin_entry_v1`.
183pub type PixelflowPluginEntryV1 =
184    unsafe extern "C" fn(*const PixelflowHostApiV1, *mut PixelflowPluginApiV1) -> PixelflowStatus;
185
186#[cfg(test)]
187mod tests {
188    use std::mem::{align_of, size_of};
189
190    use super::{PIXELFLOW_ABI_VERSION, PixelflowPluginApiV1, PixelflowStatus};
191
192    #[test]
193    fn abi_version_is_one() {
194        assert_eq!(PIXELFLOW_ABI_VERSION, 1);
195    }
196
197    #[test]
198    fn status_success_has_zero_code() {
199        assert_eq!(PixelflowStatus::ok().status_code, 0);
200    }
201
202    #[test]
203    fn plugin_api_layout_has_stable_size_and_alignment() {
204        assert_eq!(size_of::<PixelflowPluginApiV1>(), 64);
205        assert_eq!(align_of::<PixelflowPluginApiV1>(), 8);
206    }
207}