mod ipc_types;
pub mod ipc {
pub use crate::ipc_types::*;
}
mod host;
pub use ipc_types::{
ABI_VERSION, CExtensionMetadata, CommandDefinition, CommandDescriptor,
ExtensionCommand, ExtensionDescriptor, ExtensionError, ExtensionMetadata,
ExtensionMetricValue, ExtensionRuntimeState, ExtensionStats, MetricDataType,
MetricDescriptor, MetricValue, ParamMetricValue,
ParameterDefinition, ParameterGroup, PushOutputMessage, Result, ValidationRule,
IpcMessage, IpcResponse, IpcFrame, ErrorKind,
BatchCommand, BatchResult, BatchResultsVec,
StreamClientInfo, StreamDataChunk, PushOutputData,
};
pub type MetricDefinition = MetricDescriptor;
#[cfg(not(target_arch = "wasm32"))]
pub use host::Extension;
pub use host::{
ExtensionCapability, CapabilityManifest, ExtensionCapabilityProvider,
ExtensionContext, ExtensionContextConfig, AvailableCapabilities,
CapabilityError, CapabilityContext,
set_native_capability_bridge,
StreamDirection, StreamMode, StreamDataType, DataChunk,
StreamResult, StreamError, StreamCapability, StreamSession,
SessionStats, FlowControl, ClientInfo,
EventFilter, EventSubscription,
};
pub mod capability_constants {
pub use crate::host::capabilities::*;
}
#[cfg(not(target_arch = "wasm32"))]
pub use host::{
NativeCapabilityFreeFn, NativeCapabilityInvokeFn,
};
#[cfg(target_arch = "wasm32")]
mod wasm_types {
pub use crate::extension::{
SdkExtensionMetadata as ExtensionMetadata,
SdkMetricDataType as MetricDataType,
SdkMetricDefinition as MetricDescriptor,
SdkCommandDefinition as ExtensionCommand,
SdkParameterDefinition as ParameterDefinition,
SdkExtensionMetricValue as ExtensionMetricValue,
SdkMetricValue as ParamMetricValue,
SdkExtensionError as ExtensionError,
SdkParameterGroup as ParameterGroup,
};
pub type Result<T> = std::result::Result<T, crate::extension::SdkExtensionError>;
pub const ABI_VERSION: u32 = 3;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StreamCapability {
pub direction: StreamDirection,
pub mode: StreamMode,
pub max_chunk_size: usize,
pub preferred_chunk_size: usize,
pub max_concurrent_sessions: usize,
}
impl Default for StreamCapability {
fn default() -> Self {
Self {
direction: StreamDirection::None,
mode: StreamMode::Push,
max_chunk_size: 0,
preferred_chunk_size: 0,
max_concurrent_sessions: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StreamDirection {
None,
Input,
Output,
Duplex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StreamMode {
Stateless,
Stateful,
Push,
Pull,
}
}
#[cfg(target_arch = "wasm32")]
pub use wasm_types::*;
#[cfg(target_arch = "wasm32")]
mod wasm_extension {
use super::*;
#[async_trait::async_trait]
pub trait Extension: Send + Sync {
fn metadata(&self) -> &ExtensionMetadata;
fn metrics(&self) -> Vec<MetricDescriptor> { Vec::new() }
fn commands(&self) -> Vec<ExtensionCommand> { Vec::new() }
async fn execute_command(
&self,
command: &str,
args: &serde_json::Value,
) -> Result<serde_json::Value>;
fn produce_metrics(&self) -> Result<Vec<ExtensionMetricValue>> {
Ok(Vec::new())
}
async fn health_check(&self) -> Result<bool> {
Ok(true)
}
fn get_stats(&self) -> super::extension::ExtensionStats {
super::extension::ExtensionStats::default()
}
fn stream_capability(&self) -> Option<StreamCapability> {
None
}
}
}
#[cfg(target_arch = "wasm32")]
pub use wasm_extension::Extension;
#[cfg(target_arch = "wasm32")]
pub use pollster;
pub use async_trait::async_trait;
pub use serde_json::{json, Value};
mod extension;
pub use extension::*;
pub use extension::{
FrontendManifest, FrontendComponent, FrontendComponentType,
FrontendManifestBuilder, ComponentSize, I18nConfig,
};
pub mod prelude;
mod macros;
#[cfg(not(target_arch = "wasm32"))]
pub mod native;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
pub mod utils;
pub mod capabilities;
pub const SDK_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const SDK_ABI_VERSION: u32 = 3;
pub const MIN_NEOMIND_VERSION: &str = "0.5.0";
#[derive(Debug, Clone)]
pub struct MetricBuilder {
metric: MetricDescriptor,
}
impl MetricBuilder {
pub fn new(name: impl Into<String>, display_name: impl Into<String>) -> Self {
Self {
metric: MetricDescriptor {
name: name.into(),
display_name: display_name.into(),
data_type: MetricDataType::String,
unit: String::new(),
min: None,
max: None,
required: false,
},
}
}
pub fn data_type(mut self, data_type: MetricDataType) -> Self {
self.metric.data_type = data_type;
self
}
pub fn float(self) -> Self {
self.data_type(MetricDataType::Float)
}
pub fn integer(self) -> Self {
self.data_type(MetricDataType::Integer)
}
pub fn boolean(self) -> Self {
self.data_type(MetricDataType::Boolean)
}
pub fn string(self) -> Self {
self.data_type(MetricDataType::String)
}
pub fn enum_type(self, options: Vec<String>) -> Self {
self.data_type(MetricDataType::Enum { options })
}
pub fn unit(mut self, unit: impl Into<String>) -> Self {
self.metric.unit = unit.into();
self
}
pub fn min(mut self, min: f64) -> Self {
self.metric.min = Some(min);
self
}
pub fn max(mut self, max: f64) -> Self {
self.metric.max = Some(max);
self
}
pub fn required(mut self) -> Self {
self.metric.required = true;
self
}
pub fn build(self) -> MetricDescriptor {
self.metric
}
}
#[derive(Debug, Clone)]
pub struct CommandBuilder {
command: ExtensionCommand,
}
impl CommandBuilder {
pub fn new(name: impl Into<String>) -> Self {
Self {
command: ExtensionCommand {
name: name.into(),
display_name: String::new(),
description: String::new(),
payload_template: String::new(),
parameters: Vec::new(),
fixed_values: std::collections::HashMap::new(),
samples: Vec::new(),
parameter_groups: Vec::new(),
},
}
}
pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
self.command.display_name = display_name.into();
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.command.description = description.into();
self
}
pub fn param(mut self, param: ParameterDefinition) -> Self {
self.command.parameters.push(param);
self
}
pub fn param_simple(mut self, name: impl Into<String>, display_name: impl Into<String>, data_type: MetricDataType) -> Self {
self.command.parameters.push(ParameterDefinition {
name: name.into(),
display_name: display_name.into(),
description: String::new(),
param_type: data_type,
required: true,
default_value: None,
min: None,
max: None,
options: Vec::new(),
});
self
}
pub fn param_optional(mut self, name: impl Into<String>, display_name: impl Into<String>, data_type: MetricDataType) -> Self {
self.command.parameters.push(ParameterDefinition {
name: name.into(),
display_name: display_name.into(),
description: String::new(),
param_type: data_type,
required: false,
default_value: None,
min: None,
max: None,
options: Vec::new(),
});
self
}
pub fn param_with_default(mut self, name: impl Into<String>, display_name: impl Into<String>, data_type: MetricDataType, default: MetricValue) -> Self {
self.command.parameters.push(ParameterDefinition {
name: name.into(),
display_name: display_name.into(),
description: String::new(),
param_type: data_type,
required: false,
default_value: Some(default),
min: None,
max: None,
options: Vec::new(),
});
self
}
pub fn sample(mut self, sample: serde_json::Value) -> Self {
self.command.samples.push(sample);
self
}
pub fn build(self) -> ExtensionCommand {
self.command
}
}
#[derive(Debug, Clone)]
pub struct ParamBuilder {
param: ParameterDefinition,
}
impl ParamBuilder {
pub fn new(name: impl Into<String>, data_type: MetricDataType) -> Self {
Self {
param: ParameterDefinition {
name: name.into(),
display_name: String::new(),
description: String::new(),
param_type: data_type,
required: true,
default_value: None,
min: None,
max: None,
options: Vec::new(),
},
}
}
pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
self.param.display_name = display_name.into();
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.param.description = description.into();
self
}
pub fn optional(mut self) -> Self {
self.param.required = false;
self
}
pub fn required(mut self) -> Self {
self.param.required = true;
self
}
pub fn default(mut self, value: MetricValue) -> Self {
self.param.default_value = Some(value);
self.param.required = false;
self
}
pub fn min(mut self, min: f64) -> Self {
self.param.min = Some(min);
self
}
pub fn max(mut self, max: f64) -> Self {
self.param.max = Some(max);
self
}
pub fn options(mut self, options: Vec<String>) -> Self {
self.param.options = options;
self
}
pub fn build(self) -> ParameterDefinition {
self.param
}
}
#[macro_export]
macro_rules! static_metadata {
($id:literal, $name:literal, $version:literal) => {{
static META: $crate::ExtensionMetadata = $crate::ExtensionMetadata::new(
$id,
$name,
$version,
);
&META
}};
}
#[macro_export]
macro_rules! static_metrics {
($($metric:expr),* $(,)?) => {{
static METRICS: &[$crate::MetricDescriptor] = &[$($metric),*];
METRICS
}};
}
#[macro_export]
macro_rules! static_commands {
($($cmd:expr),* $(,)?) => {{
static COMMANDS: &[$crate::ExtensionCommand] = &[$($cmd),*];
COMMANDS
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capability_constants() {
assert_eq!(capability_constants::DEVICE_METRICS_READ, "device_metrics_read");
assert_eq!(capability_constants::DEVICE_METRICS_WRITE, "device_metrics_write");
assert_eq!(capability_constants::DEVICE_CONTROL, "device_control");
assert_eq!(capability_constants::STORAGE_QUERY, "storage_query");
assert_eq!(capability_constants::EVENT_PUBLISH, "event_publish");
assert_eq!(capability_constants::EVENT_SUBSCRIBE, "event_subscribe");
assert_eq!(capability_constants::TELEMETRY_HISTORY, "telemetry_history");
assert_eq!(capability_constants::METRICS_AGGREGATE, "metrics_aggregate");
assert_eq!(capability_constants::EXTENSION_CALL, "extension_call");
assert_eq!(capability_constants::AGENT_TRIGGER, "agent_trigger");
assert_eq!(capability_constants::RULE_TRIGGER, "rule_trigger");
}
#[test]
fn test_metric_builder() {
let metric = MetricBuilder::new("test", "Test Metric")
.float()
.unit("°C")
.min(-40.0)
.max(100.0)
.required()
.build();
assert_eq!(metric.name, "test");
assert_eq!(metric.display_name, "Test Metric");
assert_eq!(metric.data_type, MetricDataType::Float);
assert_eq!(metric.unit, "°C");
assert_eq!(metric.min, Some(-40.0));
assert_eq!(metric.max, Some(100.0));
assert!(metric.required);
}
#[test]
fn test_extension_metadata() {
let meta = ExtensionMetadata::new("test-ext", "Test Extension", "1.0.0")
.with_description("A test extension")
.with_author("Test Author");
assert_eq!(meta.id, "test-ext");
assert_eq!(meta.name, "Test Extension");
assert_eq!(meta.version, "1.0.0");
assert_eq!(meta.description, Some("A test extension".to_string()));
assert_eq!(meta.author, Some("Test Author".to_string()));
}
#[test]
fn test_abi_version() {
assert_eq!(ABI_VERSION, 3);
assert_eq!(SDK_ABI_VERSION, 3);
}
}