1use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16
17pub const ABI_VERSION: u32 = 3;
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
31#[serde(rename_all = "lowercase")]
32pub enum MetricDataType {
33 Float,
34 Integer,
35 Boolean,
36 #[default]
37 String,
38 Binary,
39 Enum {
40 options: Vec<String>,
41 },
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, Default)]
46#[serde(rename_all = "snake_case")]
47pub enum MetricValue {
48 Float(f64),
49 Integer(i64),
50 Boolean(bool),
51 String(String),
52 Binary(Vec<u8>),
53 #[default]
54 Null,
55}
56
57impl From<f64> for MetricValue {
59 fn from(v: f64) -> Self {
60 Self::Float(v)
61 }
62}
63
64impl From<i64> for MetricValue {
65 fn from(v: i64) -> Self {
66 Self::Integer(v)
67 }
68}
69
70impl From<bool> for MetricValue {
71 fn from(v: bool) -> Self {
72 Self::Boolean(v)
73 }
74}
75
76impl From<String> for MetricValue {
77 fn from(v: String) -> Self {
78 Self::String(v)
79 }
80}
81
82impl From<&str> for MetricValue {
83 fn from(v: &str) -> Self {
84 Self::String(v.to_string())
85 }
86}
87
88impl From<Vec<u8>> for MetricValue {
89 fn from(v: Vec<u8>) -> Self {
90 Self::Binary(v)
91 }
92}
93
94pub type ParamMetricValue = MetricValue;
100
101#[derive(Debug, Clone, Serialize, Deserialize, Default)]
107pub struct MetricDescriptor {
108 pub name: String,
110 #[serde(default)]
112 pub display_name: String,
113 #[serde(default)]
115 pub data_type: MetricDataType,
116 #[serde(default)]
118 pub unit: String,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub min: Option<f64>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub max: Option<f64>,
125 #[serde(default)]
127 pub required: bool,
128}
129
130impl MetricDescriptor {
131 pub fn new(
133 name: impl Into<String>,
134 display_name: impl Into<String>,
135 data_type: MetricDataType,
136 ) -> Self {
137 Self {
138 name: name.into(),
139 display_name: display_name.into(),
140 data_type,
141 unit: String::new(),
142 min: None,
143 max: None,
144 required: false,
145 }
146 }
147
148 pub fn with_unit(mut self, unit: impl Into<String>) -> Self {
150 self.unit = unit.into();
151 self
152 }
153
154 pub fn with_range(mut self, min: f64, max: f64) -> Self {
156 self.min = Some(min);
157 self.max = Some(max);
158 self
159 }
160
161 pub fn required(mut self) -> Self {
163 self.required = true;
164 self
165 }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, Default)]
174pub struct ParameterDefinition {
175 pub name: String,
177 #[serde(default)]
179 pub display_name: String,
180 #[serde(default)]
182 pub description: String,
183 #[serde(default)]
185 pub param_type: MetricDataType,
186 #[serde(default)]
188 pub required: bool,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub default_value: Option<MetricValue>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub min: Option<f64>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub max: Option<f64>,
198 #[serde(default)]
200 pub options: Vec<String>,
201}
202
203impl ParameterDefinition {
204 pub fn new(name: impl Into<String>, param_type: MetricDataType) -> Self {
206 Self {
207 name: name.into(),
208 display_name: String::new(),
209 description: String::new(),
210 param_type,
211 required: true,
212 default_value: None,
213 min: None,
214 max: None,
215 options: Vec::new(),
216 }
217 }
218
219 pub fn with_display_name(mut self, display_name: impl Into<String>) -> Self {
221 self.display_name = display_name.into();
222 self
223 }
224
225 pub fn with_description(mut self, description: impl Into<String>) -> Self {
227 self.description = description.into();
228 self
229 }
230
231 pub fn with_default(mut self, default: MetricValue) -> Self {
233 self.default_value = Some(default);
234 self.required = false;
235 self
236 }
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize, Default)]
245pub struct ParameterGroup {
246 pub name: String,
248 #[serde(default)]
250 pub display_name: String,
251 #[serde(default)]
253 pub description: String,
254 #[serde(default)]
256 pub parameters: Vec<String>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, Default)]
265pub struct CommandDescriptor {
266 pub name: String,
268 #[serde(default)]
270 pub display_name: String,
271 #[serde(default)]
273 pub description: String,
274 #[serde(default)]
276 pub payload_template: String,
277 #[serde(default)]
279 pub parameters: Vec<ParameterDefinition>,
280 #[serde(default)]
282 pub fixed_values: HashMap<String, serde_json::Value>,
283 #[serde(default)]
285 pub samples: Vec<serde_json::Value>,
286 #[serde(default)]
288 pub parameter_groups: Vec<ParameterGroup>,
289}
290
291impl CommandDescriptor {
292 pub fn new(name: impl Into<String>) -> Self {
294 Self {
295 name: name.into(),
296 ..Default::default()
297 }
298 }
299
300 pub fn with_display_name(mut self, display_name: impl Into<String>) -> Self {
302 self.display_name = display_name.into();
303 self
304 }
305
306 pub fn with_description(mut self, description: impl Into<String>) -> Self {
308 self.description = description.into();
309 self
310 }
311
312 pub fn param(mut self, param: ParameterDefinition) -> Self {
314 self.parameters.push(param);
315 self
316 }
317
318 pub fn sample(mut self, sample: serde_json::Value) -> Self {
320 self.samples.push(sample);
321 self
322 }
323}
324
325pub type ExtensionCommand = CommandDescriptor;
331
332pub type CommandDefinition = CommandDescriptor;
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct ExtensionMetadata {
342 pub id: String,
344 pub name: String,
346 pub version: String,
348 #[serde(skip_serializing_if = "Option::is_none")]
350 pub description: Option<String>,
351 #[serde(skip_serializing_if = "Option::is_none")]
353 pub author: Option<String>,
354 #[serde(skip_serializing_if = "Option::is_none")]
356 pub homepage: Option<String>,
357 #[serde(skip_serializing_if = "Option::is_none")]
359 pub license: Option<String>,
360 #[serde(skip)]
362 pub file_path: Option<std::path::PathBuf>,
363 #[serde(skip_serializing_if = "Option::is_none")]
365 pub config_parameters: Option<Vec<ParameterDefinition>>,
366}
367
368impl ExtensionMetadata {
369 pub fn new(id: impl Into<String>, name: impl Into<String>, version: impl Into<String>) -> Self {
371 Self {
372 id: id.into(),
373 name: name.into(),
374 version: version.into(),
375 description: None,
376 author: None,
377 homepage: None,
378 license: None,
379 file_path: None,
380 config_parameters: None,
381 }
382 }
383
384 pub fn new_with_semver(
386 id: impl Into<String>,
387 name: impl Into<String>,
388 version: semver::Version,
389 ) -> Self {
390 Self::new(id, name, version.to_string())
391 }
392
393 pub fn with_description(mut self, description: impl Into<String>) -> Self {
395 self.description = Some(description.into());
396 self
397 }
398
399 pub fn with_author(mut self, author: impl Into<String>) -> Self {
401 self.author = Some(author.into());
402 self
403 }
404
405 pub fn with_homepage(mut self, homepage: impl Into<String>) -> Self {
407 self.homepage = Some(homepage.into());
408 self
409 }
410
411 pub fn with_license(mut self, license: impl Into<String>) -> Self {
413 self.license = Some(license.into());
414 self
415 }
416
417 pub fn with_config_parameters(mut self, params: Vec<ParameterDefinition>) -> Self {
419 self.config_parameters = Some(params);
420 self
421 }
422
423 pub fn parse_version(&self) -> std::result::Result<semver::Version, semver::Error> {
425 semver::Version::parse(&self.version)
426 }
427
428 pub fn validate(&self) -> std::result::Result<(), &'static str> {
436 const MAX_ID_LEN: usize = 256;
437 const MAX_NAME_LEN: usize = 512;
438 const MAX_VERSION_LEN: usize = 64;
439 const MAX_DESCRIPTION_LEN: usize = 4096;
440 const MAX_AUTHOR_LEN: usize = 256;
441 const MAX_HOMEPAGE_LEN: usize = 1024;
442 const MAX_LICENSE_LEN: usize = 128;
443
444 if self.id.len() > MAX_ID_LEN {
445 return Err("Extension ID exceeds maximum length (256 bytes)");
446 }
447 if self.name.len() > MAX_NAME_LEN {
448 return Err("Extension name exceeds maximum length (512 bytes)");
449 }
450 if self.version.len() > MAX_VERSION_LEN {
451 return Err("Extension version exceeds maximum length (64 bytes)");
452 }
453 if let Some(ref desc) = self.description {
454 if desc.len() > MAX_DESCRIPTION_LEN {
455 return Err("Extension description exceeds maximum length (4096 bytes)");
456 }
457 }
458 if let Some(ref author) = self.author {
459 if author.len() > MAX_AUTHOR_LEN {
460 return Err("Extension author exceeds maximum length (256 bytes)");
461 }
462 }
463 if let Some(ref homepage) = self.homepage {
464 if homepage.len() > MAX_HOMEPAGE_LEN {
465 return Err("Extension homepage exceeds maximum length (1024 bytes)");
466 }
467 }
468 if let Some(ref license) = self.license {
469 if license.len() > MAX_LICENSE_LEN {
470 return Err("Extension license exceeds maximum length (128 bytes)");
471 }
472 }
473
474 if !self
476 .id
477 .chars()
478 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
479 {
480 return Err("Extension ID contains invalid characters (only alphanumeric, hyphen, underscore allowed)");
481 }
482
483 Ok(())
484 }
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct ExtensionDescriptor {
494 pub metadata: ExtensionMetadata,
496 #[serde(default)]
498 pub commands: Vec<CommandDescriptor>,
499 #[serde(default)]
501 pub metrics: Vec<MetricDescriptor>,
502}
503
504impl ExtensionDescriptor {
505 pub fn new(metadata: ExtensionMetadata) -> Self {
507 Self {
508 metadata,
509 commands: Vec::new(),
510 metrics: Vec::new(),
511 }
512 }
513
514 pub fn with_capabilities(
516 metadata: ExtensionMetadata,
517 commands: Vec<CommandDescriptor>,
518 metrics: Vec<MetricDescriptor>,
519 ) -> Self {
520 Self {
521 metadata,
522 commands,
523 metrics,
524 }
525 }
526
527 pub fn id(&self) -> &str {
529 &self.metadata.id
530 }
531
532 pub fn name(&self) -> &str {
534 &self.metadata.name
535 }
536
537 pub fn has_config(&self) -> bool {
539 false
540 }
541
542 pub fn config_parameters(&self) -> Option<&[ParameterDefinition]> {
544 None
545 }
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct ExtensionMetricValue {
555 pub name: String,
557 pub value: MetricValue,
559 pub timestamp: i64,
561}
562
563impl ExtensionMetricValue {
564 pub fn new(name: impl Into<String>, value: MetricValue) -> Self {
566 Self {
567 name: name.into(),
568 value,
569 timestamp: current_timestamp_ms(),
570 }
571 }
572
573 pub fn with_timestamp(name: impl Into<String>, value: MetricValue, timestamp: i64) -> Self {
575 Self {
576 name: name.into(),
577 value,
578 timestamp,
579 }
580 }
581}
582
583#[derive(Debug, Clone, Serialize, Deserialize)]
589pub enum ExtensionError {
590 CommandNotFound(String),
592 MetricNotFound(String),
594 InvalidArguments(String),
596 ExecutionFailed(String),
598 Timeout(String),
600 NotFound(String),
602 InvalidFormat(String),
604 LoadFailed(String),
606 SecurityError(String),
608 SymbolNotFound(String),
610 IncompatibleVersion { expected: u32, got: u32 },
612 NullPointer,
614 AlreadyRegistered(String),
616 NotSupported(String),
618 InvalidStreamData(String),
620 SessionNotFound(String),
622 SessionAlreadyExists(String),
624 InferenceFailed(String),
626 Io(String),
628 Json(String),
630 ConfigurationError(String),
632 InternalError(String),
634 Other(String),
636}
637
638impl std::fmt::Display for ExtensionError {
639 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
640 match self {
641 Self::CommandNotFound(cmd) => write!(f, "Command not found: {}", cmd),
642 Self::MetricNotFound(metric) => write!(f, "Metric not found: {}", metric),
643 Self::InvalidArguments(msg) => write!(f, "Invalid arguments: {}", msg),
644 Self::ExecutionFailed(msg) => write!(f, "Execution failed: {}", msg),
645 Self::Timeout(msg) => write!(f, "Timeout: {}", msg),
646 Self::NotFound(msg) => write!(f, "Not found: {}", msg),
647 Self::InvalidFormat(msg) => write!(f, "Invalid format: {}", msg),
648 Self::LoadFailed(msg) => write!(f, "Load failed: {}", msg),
649 Self::SecurityError(msg) => write!(f, "Security error: {}", msg),
650 Self::SymbolNotFound(msg) => write!(f, "Symbol not found: {}", msg),
651 Self::IncompatibleVersion { expected, got } => {
652 write!(
653 f,
654 "Incompatible version: expected {}, got {}",
655 expected, got
656 )
657 }
658 Self::NullPointer => write!(f, "Null pointer"),
659 Self::AlreadyRegistered(msg) => write!(f, "Already registered: {}", msg),
660 Self::NotSupported(msg) => write!(f, "Not supported: {}", msg),
661 Self::InvalidStreamData(msg) => write!(f, "Invalid stream data: {}", msg),
662 Self::SessionNotFound(msg) => write!(f, "Session not found: {}", msg),
663 Self::SessionAlreadyExists(msg) => write!(f, "Session already exists: {}", msg),
664 Self::InferenceFailed(msg) => write!(f, "Inference failed: {}", msg),
665 Self::Io(msg) => write!(f, "IO error: {}", msg),
666 Self::Json(msg) => write!(f, "JSON error: {}", msg),
667 Self::ConfigurationError(msg) => write!(f, "Configuration error: {}", msg),
668 Self::InternalError(msg) => write!(f, "Internal error: {}", msg),
669 Self::Other(msg) => write!(f, "Error: {}", msg),
670 }
671 }
672}
673
674impl std::error::Error for ExtensionError {}
675
676impl From<serde_json::Error> for ExtensionError {
677 fn from(e: serde_json::Error) -> Self {
678 Self::Json(e.to_string())
679 }
680}
681
682impl From<std::io::Error> for ExtensionError {
683 fn from(e: std::io::Error) -> Self {
684 Self::Io(e.to_string())
685 }
686}
687
688pub type Result<T> = std::result::Result<T, ExtensionError>;
690
691#[derive(Debug, Clone, Serialize, Deserialize, Default)]
697pub struct ExtensionRuntimeState {
698 pub is_running: bool,
700 pub is_isolated: bool,
702 pub loaded_at: Option<i64>,
704 pub restart_count: u64,
706 pub last_restart_at: Option<i64>,
708 pub start_count: u64,
710 pub stop_count: u64,
712 pub error_count: u64,
714 pub last_error: Option<String>,
716}
717
718impl ExtensionRuntimeState {
719 pub fn new() -> Self {
721 Self::default()
722 }
723
724 pub fn isolated() -> Self {
726 Self {
727 is_isolated: true,
728 ..Self::default()
729 }
730 }
731
732 pub fn mark_running(&mut self) {
734 self.is_running = true;
735 self.start_count += 1;
736 if self.loaded_at.is_none() {
737 self.loaded_at = Some(current_timestamp_secs());
738 }
739 }
740
741 pub fn mark_stopped(&mut self) {
743 self.is_running = false;
744 self.stop_count += 1;
745 }
746
747 pub fn record_error(&mut self, error: String) {
749 self.error_count += 1;
750 self.last_error = Some(error);
751 }
752
753 pub fn increment_restart(&mut self) {
755 self.restart_count += 1;
756 self.last_restart_at = Some(current_timestamp_secs());
757 }
758}
759
760#[derive(Debug, Clone, Default, Serialize, Deserialize)]
766pub struct ExtensionStats {
767 pub metrics_produced: u64,
769 pub commands_executed: u64,
771 pub total_execution_time_ms: u64,
773 pub last_execution_time_ms: Option<i64>,
775 pub start_count: u64,
777 pub stop_count: u64,
779 pub error_count: u64,
781 pub last_error: Option<String>,
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize, Default)]
791pub struct ValidationRule {
792 #[serde(default)]
794 pub rule_type: String,
795 #[serde(default)]
797 pub params: HashMap<String, serde_json::Value>,
798}
799
800mod base64_vec {
802 use ::base64::{engine::general_purpose::STANDARD, Engine};
803 use ::serde::de::Error as DeError;
804 use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
805
806 pub fn serialize<S: Serializer>(data: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
807 STANDARD.encode(data).serialize(s)
808 }
809
810 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
811 let value = ::serde_json::Value::deserialize(d)?;
812 match &value {
813 ::serde_json::Value::String(s) => STANDARD.decode(s).map_err(DeError::custom),
814 ::serde_json::Value::Array(arr) => Ok(arr
815 .iter()
816 .filter_map(|v| v.as_u64().map(|n| n as u8))
817 .collect()),
818 _ => Err(DeError::custom("expected base64 string or number array")),
819 }
820 }
821}
822
823#[derive(Debug, Clone, Serialize, Deserialize)]
829pub struct PushOutputMessage {
830 pub session_id: String,
832 pub sequence: u64,
834 #[serde(with = "base64_vec")]
836 pub data: Vec<u8>,
837 pub data_type: String,
839 pub timestamp: i64,
841 #[serde(skip_serializing_if = "Option::is_none")]
843 pub metadata: Option<serde_json::Value>,
844}
845
846impl PushOutputMessage {
847 pub fn new(
849 session_id: impl Into<String>,
850 sequence: u64,
851 data: Vec<u8>,
852 data_type: impl Into<String>,
853 ) -> Self {
854 Self {
855 session_id: session_id.into(),
856 sequence,
857 data,
858 data_type: data_type.into(),
859 timestamp: current_timestamp_ms(),
860 metadata: None,
861 }
862 }
863
864 pub fn json(
866 session_id: impl Into<String>,
867 sequence: u64,
868 value: serde_json::Value,
869 ) -> serde_json::Result<Self> {
870 Ok(Self {
871 session_id: session_id.into(),
872 sequence,
873 data: serde_json::to_vec(&value)?,
874 data_type: "application/json".to_string(),
875 timestamp: current_timestamp_ms(),
876 metadata: None,
877 })
878 }
879
880 pub fn image_jpeg(session_id: impl Into<String>, sequence: u64, data: Vec<u8>) -> Self {
882 Self {
883 session_id: session_id.into(),
884 sequence,
885 data,
886 data_type: "image/jpeg".to_string(),
887 timestamp: current_timestamp_ms(),
888 metadata: None,
889 }
890 }
891
892 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
894 self.metadata = Some(metadata);
895 self
896 }
897}
898
899#[repr(C)]
905#[derive(Debug, Clone, Copy)]
906pub struct CExtensionMetadata {
907 pub abi_version: u32,
909 pub id: *const std::os::raw::c_char,
911 pub name: *const std::os::raw::c_char,
913 pub version: *const std::os::raw::c_char,
915 pub description: *const std::os::raw::c_char,
917 pub author: *const std::os::raw::c_char,
919 pub metric_count: usize,
921 pub command_count: usize,
923}
924
925#[cfg(not(target_arch = "wasm32"))]
931pub fn current_timestamp_ms() -> i64 {
932 chrono::Utc::now().timestamp_millis()
933}
934
935#[cfg(target_arch = "wasm32")]
937pub fn current_timestamp_ms() -> i64 {
938 0
939}
940
941#[cfg(not(target_arch = "wasm32"))]
943pub fn current_timestamp_secs() -> i64 {
944 chrono::Utc::now().timestamp()
945}
946
947#[cfg(target_arch = "wasm32")]
949pub fn current_timestamp_secs() -> i64 {
950 0
951}
952
953#[derive(Debug, Clone, Serialize, Deserialize)]
963pub struct BatchCommand {
964 pub command: String,
966 pub args: serde_json::Value,
968}
969
970#[derive(Debug, Clone, Serialize, Deserialize)]
972pub struct BatchResult {
973 pub command: String,
975 pub success: bool,
977 #[serde(skip_serializing_if = "Option::is_none")]
979 pub data: Option<serde_json::Value>,
980 #[serde(skip_serializing_if = "Option::is_none")]
982 pub error: Option<String>,
983 pub elapsed_ms: f64,
985}
986
987#[derive(Debug, Clone, Serialize, Deserialize)]
989pub struct BatchResultsVec {
990 pub results: Vec<BatchResult>,
992 pub total_elapsed_ms: f64,
994}
995
996#[derive(Debug, Clone, Serialize, Deserialize)]
998pub struct StreamClientInfo {
999 pub client_id: String,
1000 pub ip_addr: Option<String>,
1001 pub user_agent: Option<String>,
1002}
1003
1004#[derive(Debug, Clone, Serialize, Deserialize)]
1006pub struct StreamDataChunk {
1007 pub sequence: u64,
1008 pub data_type: String,
1009 pub data: Vec<u8>,
1010 pub timestamp: i64,
1011 pub is_last: bool,
1012}
1013
1014#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1016pub enum ErrorKind {
1017 CommandNotFound,
1019 InvalidArguments,
1021 ExecutionFailed,
1023 Timeout,
1025 NotFound,
1027 InvalidFormat,
1029 NotInitialized,
1031 Internal,
1033 Security,
1035}
1036
1037impl From<ExtensionError> for ErrorKind {
1038 fn from(error: ExtensionError) -> Self {
1039 match error {
1040 ExtensionError::CommandNotFound(_) => ErrorKind::CommandNotFound,
1041 ExtensionError::InvalidArguments(_) => ErrorKind::InvalidArguments,
1042 ExtensionError::ExecutionFailed(_) => ErrorKind::ExecutionFailed,
1043 ExtensionError::Timeout(_) => ErrorKind::Timeout,
1044 ExtensionError::NotFound(_) => ErrorKind::NotFound,
1045 ExtensionError::InvalidFormat(_) => ErrorKind::InvalidFormat,
1046 ExtensionError::MetricNotFound(_) => ErrorKind::NotFound,
1047 ExtensionError::LoadFailed(_) => ErrorKind::Internal,
1048 ExtensionError::SecurityError(_) => ErrorKind::Security,
1049 ExtensionError::SymbolNotFound(_) => ErrorKind::Internal,
1050 ExtensionError::IncompatibleVersion { .. } => ErrorKind::Internal,
1051 ExtensionError::NullPointer => ErrorKind::Internal,
1052 ExtensionError::AlreadyRegistered(_) => ErrorKind::Internal,
1053 ExtensionError::NotSupported(_) => ErrorKind::Internal,
1054 ExtensionError::InvalidStreamData(_) => ErrorKind::InvalidFormat,
1055 ExtensionError::SessionNotFound(_) => ErrorKind::NotFound,
1056 ExtensionError::SessionAlreadyExists(_) => ErrorKind::Internal,
1057 ExtensionError::InferenceFailed(_) => ErrorKind::ExecutionFailed,
1058 ExtensionError::ConfigurationError(_) => ErrorKind::Internal,
1059 ExtensionError::InternalError(_) => ErrorKind::Internal,
1060 ExtensionError::Io(_) => ErrorKind::Internal,
1061 ExtensionError::Json(_) => ErrorKind::InvalidFormat,
1062 ExtensionError::Other(_) => ErrorKind::Internal,
1063 }
1064 }
1065}
1066
1067#[derive(Debug, Clone, Serialize, Deserialize)]
1069pub enum IpcMessage {
1070 Init {
1072 config: serde_json::Value,
1074 },
1075
1076 ExecuteCommand {
1078 command: String,
1080 args: serde_json::Value,
1082 request_id: u64,
1084 },
1085
1086 ProduceMetrics {
1088 request_id: u64,
1090 },
1091
1092 HealthCheck {
1094 request_id: u64,
1096 },
1097
1098 GetMetadata {
1100 request_id: u64,
1102 },
1103
1104 GetEventSubscriptions {
1106 request_id: u64,
1108 },
1109
1110 GetStats {
1112 request_id: u64,
1114 },
1115
1116 Shutdown,
1118
1119 Ping {
1121 timestamp: i64,
1123 },
1124
1125 InitStreamSession {
1130 request_id: u64,
1132 session_id: String,
1134 extension_id: String,
1136 config: serde_json::Value,
1138 client_info: StreamClientInfo,
1140 },
1141
1142 CloseStreamSession {
1144 request_id: u64,
1146 session_id: String,
1148 },
1149
1150 ProcessStreamChunk {
1152 request_id: u64,
1154 session_id: String,
1156 chunk: StreamDataChunk,
1158 },
1159
1160 GetStreamCapability {
1162 request_id: u64,
1164 },
1165
1166 ProcessChunk {
1171 request_id: u64,
1173 chunk: StreamDataChunk,
1175 },
1176
1177 StartPush {
1182 request_id: u64,
1184 session_id: String,
1186 },
1187
1188 StopPush {
1190 request_id: u64,
1192 session_id: String,
1194 },
1195
1196 ExecuteBatch {
1198 commands: Vec<BatchCommand>,
1200 request_id: u64,
1202 },
1203
1204 InvokeCapability {
1209 request_id: u64,
1211 capability: String,
1213 params: serde_json::Value,
1215 },
1216
1217 SubscribeEvents {
1219 request_id: u64,
1221 event_types: Vec<String>,
1223 filter: Option<serde_json::Value>,
1225 },
1226
1227 UnsubscribeEvents {
1229 request_id: u64,
1231 subscription_id: String,
1233 },
1234
1235 PollEvents {
1237 request_id: u64,
1239 subscription_id: String,
1241 },
1242
1243 EventPush {
1245 event_type: String,
1247 payload: serde_json::Value,
1249 timestamp: i64,
1251 },
1252
1253 CapabilityResult {
1255 request_id: u64,
1257 result: serde_json::Value,
1259 error: Option<String>,
1261 },
1262
1263 ConfigUpdate {
1265 config: serde_json::Value,
1267 },
1268}
1269
1270impl IpcMessage {
1271 pub fn to_bytes(&self) -> serde_json::Result<Vec<u8>> {
1273 serde_json::to_vec(self)
1274 }
1275
1276 pub fn from_bytes(bytes: &[u8]) -> serde_json::Result<Self> {
1278 serde_json::from_slice(bytes)
1279 }
1280}
1281
1282#[derive(Debug, Clone, Serialize, Deserialize)]
1284pub enum IpcResponse {
1285 Ready {
1287 descriptor: ExtensionDescriptor,
1289 },
1290
1291 Success {
1293 request_id: u64,
1295 data: serde_json::Value,
1297 },
1298
1299 Error {
1301 request_id: u64,
1303 error: String,
1305 kind: ErrorKind,
1307 },
1308
1309 Metrics {
1311 request_id: u64,
1313 metrics: Vec<ExtensionMetricValue>,
1315 },
1316
1317 Health {
1319 request_id: u64,
1321 healthy: bool,
1323 },
1324
1325 Metadata {
1327 request_id: u64,
1329 metadata: ExtensionMetadata,
1331 },
1332
1333 EventSubscriptions {
1335 request_id: u64,
1337 event_types: Vec<String>,
1339 },
1340
1341 Stats {
1343 request_id: u64,
1345 start_count: u64,
1347 stop_count: u64,
1349 error_count: u64,
1351 last_error: Option<String>,
1353 },
1354
1355 Pong {
1357 timestamp: i64,
1359 },
1360
1361 StreamSessionInit {
1366 request_id: u64,
1368 session_id: String,
1370 success: bool,
1372 error: Option<String>,
1374 },
1375
1376 StreamSessionClosed {
1378 request_id: u64,
1380 session_id: String,
1382 total_frames: u64,
1384 duration_ms: u64,
1386 },
1387
1388 StreamChunkResult {
1390 request_id: u64,
1392 session_id: String,
1394 input_sequence: u64,
1396 output_sequence: u64,
1398 data: Vec<u8>,
1400 data_type: String,
1402 processing_ms: f32,
1404 },
1405
1406 StreamCapability {
1408 request_id: u64,
1410 capability: Option<serde_json::Value>,
1412 },
1413
1414 ChunkResult {
1419 request_id: u64,
1421 input_sequence: u64,
1423 output_sequence: u64,
1425 data: Vec<u8>,
1427 data_type: String,
1429 processing_ms: f32,
1431 metadata: Option<serde_json::Value>,
1433 },
1434
1435 PushStarted {
1440 request_id: u64,
1442 session_id: String,
1444 success: bool,
1446 error: Option<String>,
1448 },
1449
1450 PushStopped {
1452 request_id: u64,
1454 session_id: String,
1456 success: bool,
1458 },
1459
1460 PushOutput {
1465 session_id: String,
1467 sequence: u64,
1469 data: Vec<u8>,
1471 data_type: String,
1473 timestamp: i64,
1475 metadata: Option<serde_json::Value>,
1477 },
1478
1479 StreamError {
1481 request_id: u64,
1483 session_id: String,
1485 code: String,
1487 message: String,
1489 },
1490
1491 BatchResults {
1493 request_id: u64,
1495 results: Vec<BatchResult>,
1497 total_elapsed_ms: f64,
1499 },
1500
1501 CapabilityResult {
1506 request_id: u64,
1508 result: serde_json::Value,
1510 error: Option<String>,
1512 },
1513
1514 EventSubscriptionResult {
1516 request_id: u64,
1518 subscription_id: Option<String>,
1520 error: Option<String>,
1522 },
1523
1524 EventPollResult {
1526 request_id: u64,
1528 events: Vec<serde_json::Value>,
1530 },
1531
1532 CapabilityRequest {
1537 request_id: u64,
1539 capability: String,
1541 params: serde_json::Value,
1543 },
1544
1545 ShutdownAck,
1547
1548 ConfigUpdated {
1550 success: bool,
1552 error: Option<String>,
1554 },
1555}
1556
1557impl IpcResponse {
1558 pub fn to_bytes(&self) -> serde_json::Result<Vec<u8>> {
1560 serde_json::to_vec(self)
1561 }
1562
1563 pub fn from_bytes(bytes: &[u8]) -> serde_json::Result<Self> {
1565 serde_json::from_slice(bytes)
1566 }
1567
1568 pub fn is_error(&self) -> bool {
1570 matches!(self, Self::Error { .. })
1571 }
1572
1573 pub fn is_push_output(&self) -> bool {
1575 matches!(self, Self::PushOutput { .. })
1576 }
1577
1578 pub fn is_stream_error(&self) -> bool {
1580 matches!(self, Self::StreamError { .. })
1581 }
1582
1583 pub fn is_capability_request(&self) -> bool {
1585 matches!(self, Self::CapabilityRequest { .. })
1586 }
1587
1588 pub fn request_id(&self) -> Option<u64> {
1590 match self {
1591 Self::Ready { .. } => None,
1592 Self::Success { request_id, .. } => Some(*request_id),
1593 Self::Error { request_id, .. } => Some(*request_id),
1594 Self::Metrics { request_id, .. } => Some(*request_id),
1595 Self::Health { request_id, .. } => Some(*request_id),
1596 Self::Metadata { request_id, .. } => Some(*request_id),
1597 Self::EventSubscriptions { request_id, .. } => Some(*request_id),
1598 Self::Pong { .. } => None,
1599 Self::StreamSessionInit { request_id, .. } => Some(*request_id),
1600 Self::StreamSessionClosed { request_id, .. } => Some(*request_id),
1601 Self::StreamChunkResult { request_id, .. } => Some(*request_id),
1602 Self::StreamCapability { request_id, .. } => Some(*request_id),
1603 Self::ChunkResult { request_id, .. } => Some(*request_id),
1604 Self::PushStarted { request_id, .. } => Some(*request_id),
1605 Self::PushStopped { request_id, .. } => Some(*request_id),
1606 Self::PushOutput { .. } => None,
1607 Self::StreamError { request_id, .. } => Some(*request_id),
1608 Self::BatchResults { request_id, .. } => Some(*request_id),
1609 Self::Stats { request_id, .. } => Some(*request_id),
1610 Self::CapabilityResult { request_id, .. } => Some(*request_id),
1611 Self::EventSubscriptionResult { request_id, .. } => Some(*request_id),
1612 Self::EventPollResult { request_id, .. } => Some(*request_id),
1613 Self::CapabilityRequest { request_id, .. } => Some(*request_id),
1614 Self::ShutdownAck => None,
1615 Self::ConfigUpdated { .. } => None,
1616 }
1617 }
1618}
1619
1620#[derive(Debug, Clone, Serialize, Deserialize)]
1623pub struct PushOutputData {
1624 pub session_id: String,
1626 pub sequence: u64,
1628 pub data: Vec<u8>,
1630 pub data_type: String,
1632 pub timestamp: i64,
1634 pub metadata: Option<serde_json::Value>,
1636}
1637
1638impl From<IpcResponse> for Option<PushOutputData> {
1639 fn from(response: IpcResponse) -> Self {
1640 match response {
1641 IpcResponse::PushOutput {
1642 session_id,
1643 sequence,
1644 data,
1645 data_type,
1646 timestamp,
1647 metadata,
1648 } => Some(PushOutputData {
1649 session_id,
1650 sequence,
1651 data,
1652 data_type,
1653 timestamp,
1654 metadata,
1655 }),
1656 _ => None,
1657 }
1658 }
1659}
1660
1661#[derive(Debug, Clone)]
1667pub struct IpcFrame {
1668 pub payload: Vec<u8>,
1670}
1671
1672pub const MAX_IPC_FRAME_SIZE: usize = 16 * 1024 * 1024;
1676
1677impl IpcFrame {
1678 pub fn new(payload: Vec<u8>) -> Self {
1680 Self { payload }
1681 }
1682
1683 pub fn encode(&self) -> Vec<u8> {
1685 let len = self.payload.len() as u32;
1686 let mut bytes = Vec::with_capacity(4 + self.payload.len());
1687 bytes.extend_from_slice(&len.to_le_bytes());
1688 bytes.extend_from_slice(&self.payload);
1689 bytes
1690 }
1691
1692 pub fn decode(bytes: &[u8]) -> std::result::Result<(Self, usize), &'static str> {
1698 if bytes.len() < 4 {
1699 return Err("Not enough bytes for length prefix");
1700 }
1701
1702 let len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;
1703
1704 if len > MAX_IPC_FRAME_SIZE {
1706 return Err("Frame exceeds maximum allowed size (16 MB)");
1707 }
1708
1709 if bytes.len() < 4 + len {
1710 return Err("Not enough bytes for payload");
1711 }
1712
1713 let payload = bytes[4..4 + len].to_vec();
1714 Ok((Self { payload }, 4 + len))
1715 }
1716}
1717
1718#[cfg(test)]
1723mod tests {
1724 use super::*;
1725
1726 #[test]
1727 fn test_metric_data_type_serialization() {
1728 let types = vec![
1729 (MetricDataType::Float, r#""float""#),
1730 (MetricDataType::Integer, r#""integer""#),
1731 (MetricDataType::Boolean, r#""boolean""#),
1732 (MetricDataType::String, r#""string""#),
1733 (MetricDataType::Binary, r#""binary""#),
1734 ];
1735
1736 for (dtype, expected) in types {
1737 let json = serde_json::to_string(&dtype).unwrap();
1738 assert_eq!(json, expected);
1739
1740 let deserialized: MetricDataType = serde_json::from_str(expected).unwrap();
1741 assert_eq!(dtype, deserialized);
1742 }
1743 }
1744
1745 #[test]
1746 fn test_metric_value_from() {
1747 let f: MetricValue = 42.0.into();
1748 assert!(matches!(f, MetricValue::Float(42.0)));
1749
1750 let i: MetricValue = 42i64.into();
1751 assert!(matches!(i, MetricValue::Integer(42)));
1752
1753 let b: MetricValue = true.into();
1754 assert!(matches!(b, MetricValue::Boolean(true)));
1755
1756 let s: MetricValue = "test".into();
1757 assert!(matches!(s, MetricValue::String(_)));
1758 }
1759
1760 #[test]
1761 fn test_extension_metadata() {
1762 let meta = ExtensionMetadata::new("test-ext", "Test Extension", "1.0.0")
1763 .with_description("A test extension")
1764 .with_author("Test Author");
1765
1766 assert_eq!(meta.id, "test-ext");
1767 assert_eq!(meta.name, "Test Extension");
1768 assert_eq!(meta.description, Some("A test extension".to_string()));
1769 assert_eq!(meta.author, Some("Test Author".to_string()));
1770 }
1771
1772 #[test]
1773 fn test_extension_descriptor_serialization() {
1774 let descriptor = ExtensionDescriptor::with_capabilities(
1775 ExtensionMetadata::new("test", "Test", "1.0.0"),
1776 vec![CommandDescriptor::new("cmd1")],
1777 vec![MetricDescriptor::new("m1", "M1", MetricDataType::Float)],
1778 );
1779
1780 let json = serde_json::to_string(&descriptor).unwrap();
1781 let deserialized: ExtensionDescriptor = serde_json::from_str(&json).unwrap();
1782
1783 assert_eq!(descriptor.metadata.id, deserialized.metadata.id);
1784 assert_eq!(descriptor.commands.len(), deserialized.commands.len());
1785 assert_eq!(descriptor.metrics.len(), deserialized.metrics.len());
1786 }
1787
1788 #[test]
1789 fn test_extension_error_display() {
1790 let err = ExtensionError::CommandNotFound("test".to_string());
1791 assert!(err.to_string().contains("Command not found"));
1792 }
1793
1794 #[test]
1795 fn test_abi_version() {
1796 assert_eq!(ABI_VERSION, 3);
1797 }
1798
1799 #[test]
1800 fn test_ipc_message_serialization() {
1801 let msg = IpcMessage::ExecuteCommand {
1802 command: "test".to_string(),
1803 args: serde_json::json!({"arg": 1}),
1804 request_id: 1,
1805 };
1806
1807 let bytes = msg.to_bytes().unwrap();
1808 let decoded = IpcMessage::from_bytes(&bytes).unwrap();
1809
1810 match decoded {
1811 IpcMessage::ExecuteCommand {
1812 command,
1813 args,
1814 request_id,
1815 } => {
1816 assert_eq!(command, "test");
1817 assert_eq!(request_id, 1);
1818 assert_eq!(args, serde_json::json!({"arg": 1}));
1819 }
1820 _ => panic!("Wrong message type"),
1821 }
1822 }
1823
1824 #[test]
1825 fn test_ipc_frame_encoding() {
1826 let payload = b"hello world";
1827 let frame = IpcFrame::new(payload.to_vec());
1828 let encoded = frame.encode();
1829
1830 assert_eq!(encoded.len(), 4 + payload.len());
1831 assert_eq!(&encoded[0..4], &(payload.len() as u32).to_le_bytes());
1832 assert_eq!(&encoded[4..], payload);
1833
1834 let (decoded, consumed) = IpcFrame::decode(&encoded).unwrap();
1835 assert_eq!(consumed, encoded.len());
1836 assert_eq!(decoded.payload, payload);
1837 }
1838
1839 #[test]
1840 fn test_error_kind_from_extension_error() {
1841 let err = ExtensionError::CommandNotFound("test".to_string());
1842 let kind: ErrorKind = err.into();
1843 assert_eq!(kind, ErrorKind::CommandNotFound);
1844
1845 let err = ExtensionError::Timeout("timeout".to_string());
1846 let kind: ErrorKind = err.into();
1847 assert_eq!(kind, ErrorKind::Timeout);
1848 }
1849}