1use runar_serializer_macros::Plain;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Plain)]
10pub struct ActionMetadata {
11 pub name: String,
12 pub description: String,
13 pub input_schema: Option<FieldSchema>,
14 pub output_schema: Option<FieldSchema>,
15}
16
17#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Plain)]
18pub struct SubscriptionMetadata {
19 pub path: String,
20 }
23
24#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Plain)]
25pub struct NodeMetadata {
26 pub services: Vec<ServiceMetadata>,
27 pub subscriptions: Vec<SubscriptionMetadata>,
28}
29
30#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Plain)]
31pub struct ServiceMetadata {
32 pub network_id: String,
33 pub service_path: String,
34 pub name: String,
35 pub version: String,
36 pub description: String,
37 pub actions: Vec<ActionMetadata>,
38 pub registration_time: u64,
40 pub last_start_time: Option<u64>,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Plain)]
45pub enum SchemaDataType {
46 String,
48 Int32,
50 Int64,
52 Float,
54 Double,
56 Boolean,
58 Timestamp,
60 Binary,
62 Object,
64 Array,
66 Reference(String),
68 Union(Vec<SchemaDataType>),
70 Any,
72}
73
74#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Plain)]
75pub struct FieldSchema {
76 pub name: String,
77 pub data_type: SchemaDataType,
78 pub description: Option<String>,
79 pub nullable: Option<bool>,
80 pub default_value: Option<String>,
81 pub properties: Option<HashMap<String, Box<FieldSchema>>>,
83 pub required: Option<Vec<String>>,
85 pub items: Option<Box<FieldSchema>>,
87 pub pattern: Option<String>,
89 pub enum_values: Option<Vec<String>>,
91 pub minimum: Option<f64>,
93 pub maximum: Option<f64>,
94 pub exclusive_minimum: Option<bool>,
95 pub exclusive_maximum: Option<bool>,
96 pub min_length: Option<usize>,
98 pub max_length: Option<usize>,
99 pub min_items: Option<usize>,
101 pub max_items: Option<usize>,
102 pub example: Option<String>,
104}
105
106impl FieldSchema {
107 pub fn new(name: &str, data_type: SchemaDataType) -> Self {
109 FieldSchema {
110 name: name.to_string(),
111 data_type,
112 description: None,
113 nullable: None,
114 default_value: None,
115 properties: None,
116 required: None,
117 items: None,
118 pattern: None,
119 enum_values: None,
120 minimum: None,
121 maximum: None,
122 exclusive_minimum: None,
123 exclusive_maximum: None,
124 min_length: None,
125 max_length: None,
126 min_items: None,
127 max_items: None,
128 example: None,
129 }
130 }
131
132 pub fn string(name: &str) -> Self {
133 FieldSchema::new(name, SchemaDataType::String)
134 }
135
136 pub fn integer(name: &str) -> Self {
137 FieldSchema::new(name, SchemaDataType::Int32)
138 }
139
140 pub fn long(name: &str) -> Self {
141 FieldSchema::new(name, SchemaDataType::Int64)
142 }
143
144 pub fn float(name: &str) -> Self {
145 FieldSchema::new(name, SchemaDataType::Float)
146 }
147
148 pub fn double(name: &str) -> Self {
149 FieldSchema::new(name, SchemaDataType::Double)
150 }
151
152 pub fn boolean(name: &str) -> Self {
153 FieldSchema::new(name, SchemaDataType::Boolean)
154 }
155
156 pub fn timestamp(name: &str) -> Self {
157 FieldSchema::new(name, SchemaDataType::Timestamp)
158 }
159
160 pub fn object(
161 name: &str,
162 properties: HashMap<String, Box<FieldSchema>>,
163 required: Option<Vec<String>>,
164 ) -> Self {
165 FieldSchema {
166 name: name.to_string(),
167 data_type: SchemaDataType::Object,
168 properties: Some(properties),
169 required,
170 ..FieldSchema::new(name, SchemaDataType::Object)
171 }
172 }
173
174 pub fn array(name: &str, items: Box<FieldSchema>) -> Self {
175 FieldSchema {
176 name: name.to_string(),
177 data_type: SchemaDataType::Array,
178 items: Some(items),
179 ..FieldSchema::new(name, SchemaDataType::Array)
180 }
181 }
182
183 pub fn reference(name: &str, reference_type: &str) -> Self {
184 FieldSchema {
185 name: name.to_string(),
186 data_type: SchemaDataType::Reference(reference_type.to_string()),
187 ..FieldSchema::new(name, SchemaDataType::Reference(reference_type.to_string()))
188 }
189 }
190
191 pub fn union(name: &str, union_types: Vec<SchemaDataType>) -> Self {
192 FieldSchema {
193 name: name.to_string(),
194 data_type: SchemaDataType::Union(union_types),
195 ..FieldSchema::new(name, SchemaDataType::Union(vec![]))
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use anyhow::Result;
204 use runar_serializer::{arc_value::AsArcValue, ArcValue};
205 use std::collections::HashMap;
206 use std::sync::Arc;
207
208 #[test]
209 fn test_service_metadata_serialization_roundtrip() -> Result<()> {
210 let service_metadata = ServiceMetadata {
212 network_id: "test-network-123".to_string(),
213 service_path: "math-service".to_string(),
214 name: "Math Service".to_string(),
215 version: "1.2.3".to_string(),
216 description: "A comprehensive mathematical operations service".to_string(),
217 actions: vec![
218 ActionMetadata {
219 name: "add".to_string(),
220 description: "Adds two numbers".to_string(),
221 input_schema: Some(FieldSchema::object(
222 "AddInput",
223 HashMap::from([
224 ("a".to_string(), Box::new(FieldSchema::double("a"))),
225 ("b".to_string(), Box::new(FieldSchema::double("b"))),
226 ]),
227 Some(vec!["a".to_string(), "b".to_string()]),
228 )),
229 output_schema: Some(FieldSchema::double("result")),
230 },
231 ActionMetadata {
232 name: "multiply".to_string(),
233 description: "Multiplies two numbers".to_string(),
234 input_schema: Some(FieldSchema::object(
235 "MultiplyInput",
236 HashMap::from([
237 ("x".to_string(), Box::new(FieldSchema::integer("x"))),
238 ("y".to_string(), Box::new(FieldSchema::integer("y"))),
239 ]),
240 Some(vec!["x".to_string(), "y".to_string()]),
241 )),
242 output_schema: Some(FieldSchema::long("result")),
243 },
244 ActionMetadata {
245 name: "calculate".to_string(),
246 description: "Performs complex calculations".to_string(),
247 input_schema: Some(FieldSchema::object(
248 "CalculateInput",
249 HashMap::from([
250 (
251 "expression".to_string(),
252 Box::new(FieldSchema::string("expression")),
253 ),
254 (
255 "variables".to_string(),
256 Box::new(FieldSchema::array(
257 "variables",
258 Box::new(FieldSchema::object(
259 "Variable",
260 HashMap::from([
261 (
262 "name".to_string(),
263 Box::new(FieldSchema::string("name")),
264 ),
265 (
266 "value".to_string(),
267 Box::new(FieldSchema::double("value")),
268 ),
269 ]),
270 Some(vec!["name".to_string(), "value".to_string()]),
271 )),
272 )),
273 ),
274 ]),
275 Some(vec!["expression".to_string()]),
276 )),
277 output_schema: Some(FieldSchema::double("result")),
278 },
279 ],
280 registration_time: 1640995200, last_start_time: Some(1640995260), };
291
292 let arc_value = ArcValue::new_struct(service_metadata.clone());
294
295 let serialized = arc_value.serialize(None)?;
297
298 let deserialized = ArcValue::deserialize(&serialized, None)?;
300
301 let extracted_metadata: Arc<ServiceMetadata> = deserialized.as_struct_ref()?;
303
304 assert_eq!(*extracted_metadata, service_metadata);
306
307 assert_eq!(extracted_metadata.network_id, "test-network-123");
309 assert_eq!(extracted_metadata.service_path, "math-service");
310 assert_eq!(extracted_metadata.name, "Math Service");
311 assert_eq!(extracted_metadata.version, "1.2.3");
312 assert_eq!(
313 extracted_metadata.description,
314 "A comprehensive mathematical operations service"
315 );
316 assert_eq!(extracted_metadata.registration_time, 1640995200);
317 assert_eq!(extracted_metadata.last_start_time, Some(1640995260));
318
319 assert_eq!(extracted_metadata.actions.len(), 3);
321 assert_eq!(extracted_metadata.actions[0].name, "add");
322 assert_eq!(extracted_metadata.actions[1].name, "multiply");
323 assert_eq!(extracted_metadata.actions[2].name, "calculate");
324
325 assert!(extracted_metadata.actions[0].input_schema.is_some());
332 assert!(extracted_metadata.actions[1].input_schema.is_some());
333 assert!(extracted_metadata.actions[2].input_schema.is_some());
334
335 assert!(extracted_metadata.actions[0].output_schema.is_some());
337 assert!(extracted_metadata.actions[1].output_schema.is_some());
338 assert!(extracted_metadata.actions[2].output_schema.is_some());
339
340 println!("✅ ServiceMetadata serialization roundtrip test passed!");
341 println!(" - Network ID: {}", extracted_metadata.network_id);
342 println!(" - Service Path: {}", extracted_metadata.service_path);
343 println!(" - Actions: {}", extracted_metadata.actions.len());
344 Ok(())
347 }
348
349 #[test]
350 fn test_field_schema_serialization() -> Result<()> {
351 let string_schema = FieldSchema::string("name");
353 let integer_schema = FieldSchema::integer("age");
354 let double_schema = FieldSchema::double("score");
355 let boolean_schema = FieldSchema::boolean("active");
356 let timestamp_schema = FieldSchema::timestamp("created_at");
357
358 let mut properties = HashMap::new();
360 properties.insert("id".to_string(), Box::new(FieldSchema::integer("id")));
361 properties.insert("name".to_string(), Box::new(FieldSchema::string("name")));
362 let object_schema = FieldSchema::object(
363 "User",
364 properties,
365 Some(vec!["id".to_string(), "name".to_string()]),
366 );
367
368 let array_schema = FieldSchema::array("tags", Box::new(FieldSchema::string("tag")));
370
371 let reference_schema = FieldSchema::reference("user", "User");
373
374 let union_schema = FieldSchema::union(
376 "value",
377 vec![
378 SchemaDataType::String,
379 SchemaDataType::Int32,
380 SchemaDataType::Double,
381 ],
382 );
383
384 let schemas = vec![
386 string_schema,
387 integer_schema,
388 double_schema,
389 boolean_schema,
390 timestamp_schema,
391 object_schema,
392 array_schema,
393 reference_schema,
394 union_schema,
395 ];
396
397 for schema in schemas {
398 let arc_value = ArcValue::new_struct(schema.clone());
399 let serialized = arc_value.serialize(None)?;
400 let deserialized = ArcValue::deserialize(&serialized, None)?;
401 let extracted: Arc<FieldSchema> = deserialized.as_struct_ref()?;
402
403 assert_eq!(*extracted, schema);
404 }
405
406 println!("✅ FieldSchema serialization test passed!");
407 Ok(())
408 }
409
410 #[test]
411 fn test_service_metadata_json_to_arcvalue() -> Result<()> {
412 let json_service_metadata = serde_json::json!({
414 "network_id": "json-test-network",
415 "service_path": "user-service",
416 "name": "User Management Service",
417 "version": "2.1.0",
418 "description": "Comprehensive user management with authentication and profiles",
419 "actions": [
420 {
421 "name": "create_user",
422 "description": "Creates a new user account",
423 "input_schema": {
424 "name": "CreateUserInput",
425 "data_type": "Object",
426 "description": null,
427 "nullable": null,
428 "default_value": null,
429 "properties": {
430 "username": {
431 "name": "username",
432 "data_type": "String",
433 "description": null,
434 "nullable": null,
435 "default_value": null,
436 "properties": null,
437 "required": null,
438 "items": null,
439 "pattern": null,
440 "enum_values": null,
441 "minimum": null,
442 "maximum": null,
443 "exclusive_minimum": null,
444 "exclusive_maximum": null,
445 "min_length": 3,
446 "max_length": 50,
447 "min_items": null,
448 "max_items": null,
449 "example": null
450 },
451 "email": {
452 "name": "email",
453 "data_type": "String",
454 "description": null,
455 "nullable": null,
456 "default_value": null,
457 "properties": null,
458 "required": null,
459 "items": null,
460 "pattern": "^[^@]+@[^@]+\\.[^@]+$",
461 "enum_values": null,
462 "minimum": null,
463 "maximum": null,
464 "exclusive_minimum": null,
465 "exclusive_maximum": null,
466 "min_length": null,
467 "max_length": null,
468 "min_items": null,
469 "max_items": null,
470 "example": null
471 }
472 },
473 "required": ["username", "email"],
474 "items": null,
475 "pattern": null,
476 "enum_values": null,
477 "minimum": null,
478 "maximum": null,
479 "exclusive_minimum": null,
480 "exclusive_maximum": null,
481 "min_length": null,
482 "max_length": null,
483 "min_items": null,
484 "max_items": null,
485 "example": null
486 },
487 "output_schema": {
488 "name": "User",
489 "data_type": "Object",
490 "description": null,
491 "nullable": null,
492 "default_value": null,
493 "properties": {
494 "id": {
495 "name": "id",
496 "data_type": "String",
497 "description": null,
498 "nullable": null,
499 "default_value": null,
500 "properties": null,
501 "required": null,
502 "items": null,
503 "pattern": null,
504 "enum_values": null,
505 "minimum": null,
506 "maximum": null,
507 "exclusive_minimum": null,
508 "exclusive_maximum": null,
509 "min_length": null,
510 "max_length": null,
511 "min_items": null,
512 "max_items": null,
513 "example": null
514 }
515 },
516 "required": null,
517 "items": null,
518 "pattern": null,
519 "enum_values": null,
520 "minimum": null,
521 "maximum": null,
522 "exclusive_minimum": null,
523 "exclusive_maximum": null,
524 "min_length": null,
525 "max_length": null,
526 "min_items": null,
527 "max_items": null,
528 "example": null
529 }
530 }
531 ],
532 "events": [
533 {
534 "path": "user.created",
535 "description": "Emitted when a new user is created",
536 "data_schema": {
537 "name": "UserCreatedEvent",
538 "data_type": "Object",
539 "description": null,
540 "nullable": null,
541 "default_value": null,
542 "properties": {
543 "user_id": {
544 "name": "user_id",
545 "data_type": "String",
546 "description": null,
547 "nullable": null,
548 "default_value": null,
549 "properties": null,
550 "required": null,
551 "items": null,
552 "pattern": null,
553 "enum_values": null,
554 "minimum": null,
555 "maximum": null,
556 "exclusive_minimum": null,
557 "exclusive_maximum": null,
558 "min_length": null,
559 "max_length": null,
560 "min_items": null,
561 "max_items": null,
562 "example": null
563 }
564 },
565 "required": null,
566 "items": null,
567 "pattern": null,
568 "enum_values": null,
569 "minimum": null,
570 "maximum": null,
571 "exclusive_minimum": null,
572 "exclusive_maximum": null,
573 "min_length": null,
574 "max_length": null,
575 "min_items": null,
576 "max_items": null,
577 "example": null
578 }
579 }
580 ],
581 "registration_time": 1640995200,
582 "last_start_time": 1640995260
583 });
584
585 let arc_value = ArcValue::new_json(json_service_metadata.clone());
587 assert_eq!(arc_value.category(), runar_serializer::ValueCategory::Json);
588
589 let back_to_json = arc_value.to_json()?;
591 assert_eq!(back_to_json, json_service_metadata);
592
593 let service_metadata: ServiceMetadata =
595 serde_json::from_value(json_service_metadata.clone())?;
596 let typed_arc_value = ArcValue::new_struct(service_metadata.clone());
597
598 let obj_instance: Arc<ServiceMetadata> = typed_arc_value.as_type_ref()?;
600
601 assert_eq!(obj_instance.network_id, "json-test-network");
603 assert_eq!(obj_instance.service_path, "user-service");
604 assert_eq!(obj_instance.name, "User Management Service");
605 assert_eq!(obj_instance.version, "2.1.0");
606 assert_eq!(
607 obj_instance.description,
608 "Comprehensive user management with authentication and profiles"
609 );
610 assert_eq!(obj_instance.registration_time, 1640995200);
611 assert_eq!(obj_instance.last_start_time, Some(1640995260));
612
613 assert_eq!(obj_instance.actions.len(), 1);
615 assert_eq!(obj_instance.actions[0].name, "create_user");
616 assert_eq!(
617 obj_instance.actions[0].description,
618 "Creates a new user account"
619 );
620 assert!(obj_instance.actions[0].input_schema.is_some());
621 assert!(obj_instance.actions[0].output_schema.is_some());
622
623 println!(" - Successfully converted JSON to typed ServiceMetadata");
628 println!(" - All fields match the input JSON structure");
629
630 println!("✅ JSON to ArcValue conversion test passed!");
635 println!(" - JSON structure preserved in ArcValue");
636 println!(" - Roundtrip JSON conversion successful");
637 println!(" - ArcValue category: {:?}", arc_value.category());
638
639 let json_field_schema = serde_json::json!({
641 "name": "test_field",
642 "data_type": "String",
643 "description": "A test field",
644 "nullable": true,
645 "min_length": 1,
646 "max_length": 100
647 });
648
649 let field_arc_value = ArcValue::new_json(json_field_schema.clone());
650 let field_back_to_json = field_arc_value.to_json()?;
651 assert_eq!(field_back_to_json, json_field_schema);
652
653 println!(" - FieldSchema JSON conversion also successful");
654
655 let service_metadata_from_json: ServiceMetadata =
657 serde_json::from_value(json_service_metadata)?;
658 let arc_value_from_struct = service_metadata_from_json.clone().into_arc_value();
659
660 let extracted_service_metadata = ServiceMetadata::from_arc_value(arc_value_from_struct)?;
662
663 assert_eq!(extracted_service_metadata, service_metadata_from_json);
665
666 println!(" - AsArcValue trait roundtrip successful");
667 println!(" - JSON -> ServiceMetadata -> ArcValue -> ServiceMetadata works perfectly!");
668
669 Ok(())
670 }
671}