phytrace_sdk/models/domains/
identity.rs

1//! Identity Domain - Robot/system identification.
2//!
3//! Contains identifiers and metadata for the telemetry source.
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8use crate::models::enums::SourceType;
9
10/// Identity domain containing source identification.
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct IdentityDomain {
13    /// Unique identifier for this robot/system
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub source_id: Option<String>,
16
17    /// Type classification of the source
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub source_type: Option<SourceType>,
20
21    /// Platform/manufacturer identifier (e.g., "locus_robotics", "fetch")
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub platform: Option<String>,
24
25    /// Platform model name
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub model: Option<String>,
28
29    /// Serial number
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub serial_number: Option<String>,
32
33    /// Firmware/software version
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub firmware_version: Option<String>,
36
37    /// Hardware version
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub hardware_version: Option<String>,
40
41    /// Fleet identifier
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub fleet_id: Option<String>,
44
45    /// Site/facility identifier
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub site_id: Option<String>,
48
49    /// Organization identifier
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub organization_id: Option<String>,
52
53    /// Human-readable name for this robot
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub display_name: Option<String>,
56
57    /// Descriptive tags for categorization
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub tags: Option<Vec<String>>,
60
61    /// Custom metadata key-value pairs
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub metadata: Option<HashMap<String, String>>,
64
65    /// MAC address of primary network interface
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub mac_address: Option<String>,
68
69    /// IP address
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub ip_address: Option<String>,
72}
73
74impl IdentityDomain {
75    /// Create a new identity domain with required fields.
76    pub fn new(source_id: impl Into<String>, source_type: SourceType) -> Self {
77        Self {
78            source_id: Some(source_id.into()),
79            source_type: Some(source_type),
80            ..Default::default()
81        }
82    }
83
84    /// Builder-style method to add platform info.
85    pub fn with_platform(mut self, platform: impl Into<String>, model: impl Into<String>) -> Self {
86        self.platform = Some(platform.into());
87        self.model = Some(model.into());
88        self
89    }
90
91    /// Builder-style method to add fleet info.
92    pub fn with_fleet(mut self, fleet_id: impl Into<String>, site_id: impl Into<String>) -> Self {
93        self.fleet_id = Some(fleet_id.into());
94        self.site_id = Some(site_id.into());
95        self
96    }
97
98    /// Builder-style method to add organization.
99    pub fn with_organization(mut self, org_id: impl Into<String>) -> Self {
100        self.organization_id = Some(org_id.into());
101        self
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_identity_creation() {
111        let identity = IdentityDomain::new("robot-001", SourceType::Amr)
112            .with_platform("locus", "origin")
113            .with_fleet("fleet-1", "site-chicago");
114
115        assert_eq!(identity.source_id, Some("robot-001".to_string()));
116        assert_eq!(identity.platform, Some("locus".to_string()));
117    }
118
119    #[test]
120    fn test_identity_serialization() {
121        let identity = IdentityDomain::new("robot-001", SourceType::Amr);
122        let json = serde_json::to_string(&identity).unwrap();
123        assert!(json.contains("robot-001"));
124        assert!(json.contains("amr"));
125    }
126}