1mod ipc_types;
126
127pub mod ipc {
129 pub use crate::ipc_types::*;
130}
131
132mod host;
137
138pub use ipc_types::{
143 BatchCommand,
144 BatchResult,
145 BatchResultsVec,
146 CExtensionMetadata,
147 CommandDefinition,
148 CommandDescriptor,
149 ErrorKind,
150 ExtensionCommand,
151 ExtensionDescriptor,
152 ExtensionError,
153 ExtensionMetadata,
154 ExtensionMetricValue,
155 ExtensionRuntimeState,
156 ExtensionStats,
157 IpcFrame,
158 IpcMessage,
160 IpcResponse,
161 MetricDataType,
162 MetricDescriptor,
163 MetricValue,
164 ParamMetricValue,
165 ParameterDefinition,
166 ParameterGroup,
167 PushOutputData,
168 PushOutputMessage,
169 Result,
170 StreamClientInfo,
171 StreamDataChunk,
172 ValidationRule,
173 ABI_VERSION,
174};
175
176pub type MetricDefinition = MetricDescriptor;
178
179pub use host::Extension;
184
185pub use host::{
186 send_push_output,
187
188 set_native_capability_bridge,
189 set_push_output_writer,
190 AvailableCapabilities,
191 CapabilityError,
192 CapabilityManifest,
193 ClientInfo,
194
195 DataChunk,
196 EventFilter,
198 EventSubscription,
199 ExtensionCapability,
201 ExtensionCapabilityProvider,
202 ExtensionContext,
203 ExtensionContextConfig,
204 FlowControl,
205 PushOutputWriterFn,
207 SessionStats,
208 StreamCapability,
209 StreamDataType,
210 StreamDirection,
212 StreamError,
213 StreamMode,
214 StreamResult,
215 StreamSession,
216};
217
218#[cfg(not(target_arch = "wasm32"))]
220pub use host::CapabilityContext;
221
222pub mod capability_constants {
224 pub use crate::host::capabilities::*;
225}
226
227#[cfg(not(target_arch = "wasm32"))]
229pub use host::{NativeCapabilityFreeFn, NativeCapabilityInvokeFn};
230
231#[cfg(target_arch = "wasm32")]
236mod wasm_types {
237 pub use crate::extension::{
238 SdkCommandDefinition as ExtensionCommand, SdkExtensionError as ExtensionError,
239 SdkExtensionMetadata as ExtensionMetadata, SdkExtensionMetricValue as ExtensionMetricValue,
240 SdkMetricDataType as MetricDataType, SdkMetricDefinition as MetricDescriptor,
241 SdkMetricValue as ParamMetricValue, SdkParameterDefinition as ParameterDefinition,
242 SdkParameterGroup as ParameterGroup,
243 };
244
245 pub type Result<T> = std::result::Result<T, crate::extension::SdkExtensionError>;
246 pub const ABI_VERSION: u32 = 3;
247
248 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
250 pub struct StreamCapability {
251 pub direction: StreamDirection,
252 pub mode: StreamMode,
253 pub max_chunk_size: usize,
254 pub preferred_chunk_size: usize,
255 pub max_concurrent_sessions: usize,
256 }
257
258 impl Default for StreamCapability {
259 fn default() -> Self {
260 Self {
261 direction: StreamDirection::None,
262 mode: StreamMode::Push,
263 max_chunk_size: 0,
264 preferred_chunk_size: 0,
265 max_concurrent_sessions: 0,
266 }
267 }
268 }
269
270 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
272 #[serde(rename_all = "lowercase")]
273 pub enum StreamDirection {
274 None,
275 Input,
276 Output,
277 Duplex,
278 }
279
280 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
282 #[serde(rename_all = "lowercase")]
283 pub enum StreamMode {
284 Stateless,
285 Stateful,
286 Push,
287 Pull,
288 }
289}
290
291#[cfg(target_arch = "wasm32")]
292pub use wasm_types::*;
293
294#[cfg(target_arch = "wasm32")]
296pub use pollster;
297
298pub use async_trait::async_trait;
303pub use serde_json::{json, Value};
304
305mod extension;
310pub use extension::*;
311
312pub use extension::{
313 ComponentSize, FrontendComponent, FrontendComponentType, FrontendManifest,
314 FrontendManifestBuilder, I18nConfig,
315};
316
317mod macros;
322pub mod prelude;
323
324#[cfg(not(target_arch = "wasm32"))]
325pub mod native;
326
327#[cfg(target_arch = "wasm32")]
328pub mod wasm;
329
330pub mod capabilities;
331pub mod utils;
332
333pub const SDK_VERSION: &str = env!("CARGO_PKG_VERSION");
339
340pub const SDK_ABI_VERSION: u32 = 3;
342
343pub const MIN_NEOMIND_VERSION: &str = "0.5.0";
345
346#[derive(Debug, Clone)]
352pub struct MetricBuilder {
353 metric: MetricDescriptor,
354}
355
356impl MetricBuilder {
357 pub fn new(name: impl Into<String>, display_name: impl Into<String>) -> Self {
358 Self {
359 metric: MetricDescriptor {
360 name: name.into(),
361 display_name: display_name.into(),
362 data_type: MetricDataType::String,
363 unit: String::new(),
364 min: None,
365 max: None,
366 required: false,
367 },
368 }
369 }
370
371 pub fn data_type(mut self, data_type: MetricDataType) -> Self {
372 self.metric.data_type = data_type;
373 self
374 }
375
376 pub fn float(self) -> Self {
377 self.data_type(MetricDataType::Float)
378 }
379
380 pub fn integer(self) -> Self {
381 self.data_type(MetricDataType::Integer)
382 }
383
384 pub fn boolean(self) -> Self {
385 self.data_type(MetricDataType::Boolean)
386 }
387
388 pub fn string(self) -> Self {
389 self.data_type(MetricDataType::String)
390 }
391
392 pub fn enum_type(self, options: Vec<String>) -> Self {
393 self.data_type(MetricDataType::Enum { options })
394 }
395
396 pub fn unit(mut self, unit: impl Into<String>) -> Self {
397 self.metric.unit = unit.into();
398 self
399 }
400
401 pub fn min(mut self, min: f64) -> Self {
402 self.metric.min = Some(min);
403 self
404 }
405
406 pub fn max(mut self, max: f64) -> Self {
407 self.metric.max = Some(max);
408 self
409 }
410
411 pub fn required(mut self) -> Self {
412 self.metric.required = true;
413 self
414 }
415
416 pub fn build(self) -> MetricDescriptor {
417 self.metric
418 }
419}
420
421#[derive(Debug, Clone)]
423pub struct CommandBuilder {
424 command: ExtensionCommand,
425}
426
427impl CommandBuilder {
428 pub fn new(name: impl Into<String>) -> Self {
429 Self {
430 command: ExtensionCommand {
431 name: name.into(),
432 display_name: String::new(),
433 description: String::new(),
434 payload_template: String::new(),
435 parameters: Vec::new(),
436 fixed_values: std::collections::HashMap::new(),
437 samples: Vec::new(),
438 parameter_groups: Vec::new(),
439 },
440 }
441 }
442
443 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
444 self.command.display_name = display_name.into();
445 self
446 }
447
448 pub fn description(mut self, description: impl Into<String>) -> Self {
449 self.command.description = description.into();
450 self
451 }
452
453 pub fn param(mut self, param: ParameterDefinition) -> Self {
454 self.command.parameters.push(param);
455 self
456 }
457
458 pub fn param_simple(
459 mut self,
460 name: impl Into<String>,
461 display_name: impl Into<String>,
462 data_type: MetricDataType,
463 ) -> Self {
464 self.command.parameters.push(ParameterDefinition {
465 name: name.into(),
466 display_name: display_name.into(),
467 description: String::new(),
468 param_type: data_type,
469 required: true,
470 default_value: None,
471 min: None,
472 max: None,
473 options: Vec::new(),
474 });
475 self
476 }
477
478 pub fn param_optional(
479 mut self,
480 name: impl Into<String>,
481 display_name: impl Into<String>,
482 data_type: MetricDataType,
483 ) -> Self {
484 self.command.parameters.push(ParameterDefinition {
485 name: name.into(),
486 display_name: display_name.into(),
487 description: String::new(),
488 param_type: data_type,
489 required: false,
490 default_value: None,
491 min: None,
492 max: None,
493 options: Vec::new(),
494 });
495 self
496 }
497
498 pub fn param_with_default(
499 mut self,
500 name: impl Into<String>,
501 display_name: impl Into<String>,
502 data_type: MetricDataType,
503 default: MetricValue,
504 ) -> Self {
505 self.command.parameters.push(ParameterDefinition {
506 name: name.into(),
507 display_name: display_name.into(),
508 description: String::new(),
509 param_type: data_type,
510 required: false,
511 default_value: Some(default),
512 min: None,
513 max: None,
514 options: Vec::new(),
515 });
516 self
517 }
518
519 pub fn sample(mut self, sample: serde_json::Value) -> Self {
520 self.command.samples.push(sample);
521 self
522 }
523
524 pub fn build(self) -> ExtensionCommand {
525 self.command
526 }
527}
528
529#[derive(Debug, Clone)]
531pub struct ParamBuilder {
532 param: ParameterDefinition,
533}
534
535impl ParamBuilder {
536 pub fn new(name: impl Into<String>, data_type: MetricDataType) -> Self {
537 Self {
538 param: ParameterDefinition {
539 name: name.into(),
540 display_name: String::new(),
541 description: String::new(),
542 param_type: data_type,
543 required: true,
544 default_value: None,
545 min: None,
546 max: None,
547 options: Vec::new(),
548 },
549 }
550 }
551
552 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
553 self.param.display_name = display_name.into();
554 self
555 }
556
557 pub fn description(mut self, description: impl Into<String>) -> Self {
558 self.param.description = description.into();
559 self
560 }
561
562 pub fn optional(mut self) -> Self {
563 self.param.required = false;
564 self
565 }
566
567 pub fn required(mut self) -> Self {
568 self.param.required = true;
569 self
570 }
571
572 pub fn default(mut self, value: MetricValue) -> Self {
573 self.param.default_value = Some(value);
574 self.param.required = false;
575 self
576 }
577
578 pub fn min(mut self, min: f64) -> Self {
579 self.param.min = Some(min);
580 self
581 }
582
583 pub fn max(mut self, max: f64) -> Self {
584 self.param.max = Some(max);
585 self
586 }
587
588 pub fn options(mut self, options: Vec<String>) -> Self {
589 self.param.options = options;
590 self
591 }
592
593 pub fn build(self) -> ParameterDefinition {
594 self.param
595 }
596}
597
598#[macro_export]
604macro_rules! static_metadata {
605 ($id:literal, $name:literal, $version:literal) => {{
606 static META: $crate::ExtensionMetadata =
607 $crate::ExtensionMetadata::new($id, $name, $version);
608 &META
609 }};
610}
611
612#[macro_export]
614macro_rules! static_metrics {
615 ($($metric:expr),* $(,)?) => {{
616 static METRICS: &[$crate::MetricDescriptor] = &[$($metric),*];
617 METRICS
618 }};
619}
620
621#[macro_export]
623macro_rules! static_commands {
624 ($($cmd:expr),* $(,)?) => {{
625 static COMMANDS: &[$crate::ExtensionCommand] = &[$($cmd),*];
626 COMMANDS
627 }};
628}
629
630#[cfg(test)]
635mod tests {
636 use super::*;
637
638 #[test]
639 fn test_capability_constants() {
640 assert_eq!(
641 capability_constants::DEVICE_METRICS_READ,
642 "device_metrics_read"
643 );
644 assert_eq!(
645 capability_constants::DEVICE_METRICS_WRITE,
646 "device_metrics_write"
647 );
648 assert_eq!(capability_constants::DEVICE_CONTROL, "device_control");
649 assert_eq!(capability_constants::STORAGE_QUERY, "storage_query");
650 assert_eq!(capability_constants::EVENT_PUBLISH, "event_publish");
651 assert_eq!(capability_constants::EVENT_SUBSCRIBE, "event_subscribe");
652 assert_eq!(capability_constants::TELEMETRY_HISTORY, "telemetry_history");
653 assert_eq!(capability_constants::METRICS_AGGREGATE, "metrics_aggregate");
654 assert_eq!(capability_constants::EXTENSION_CALL, "extension_call");
655 assert_eq!(capability_constants::AGENT_TRIGGER, "agent_trigger");
656 assert_eq!(capability_constants::RULE_TRIGGER, "rule_trigger");
657 }
658
659 #[test]
660 fn test_metric_builder() {
661 let metric = MetricBuilder::new("test", "Test Metric")
662 .float()
663 .unit("°C")
664 .min(-40.0)
665 .max(100.0)
666 .required()
667 .build();
668
669 assert_eq!(metric.name, "test");
670 assert_eq!(metric.display_name, "Test Metric");
671 assert_eq!(metric.data_type, MetricDataType::Float);
672 assert_eq!(metric.unit, "°C");
673 assert_eq!(metric.min, Some(-40.0));
674 assert_eq!(metric.max, Some(100.0));
675 assert!(metric.required);
676 }
677
678 #[test]
679 fn test_extension_metadata() {
680 let meta = ExtensionMetadata::new("test-ext", "Test Extension", "1.0.0")
681 .with_description("A test extension")
682 .with_author("Test Author");
683
684 assert_eq!(meta.id, "test-ext");
685 assert_eq!(meta.name, "Test Extension");
686 assert_eq!(meta.version, "1.0.0");
687 assert_eq!(meta.description, Some("A test extension".to_string()));
688 assert_eq!(meta.author, Some("Test Author".to_string()));
689 }
690
691 #[test]
692 fn test_abi_version() {
693 assert_eq!(ABI_VERSION, 3);
694 assert_eq!(SDK_ABI_VERSION, 3);
695 }
696}