1use std::hash::{DefaultHasher, Hash, Hasher};
5
6use crate::pubsub::ProtoAgent;
7
8pub const DEFAULT_AGENT_ID: u64 = u64::MAX;
9
10#[derive(Debug, Clone, Default)]
11pub struct AgentType {
12 organization: u64,
13 namespace: u64,
14 agent_type: u64,
15
16 strings: Option<Box<(String, String, String)>>,
19}
20
21impl Hash for AgentType {
22 fn hash<H: Hasher>(&self, state: &mut H) {
23 self.organization.hash(state);
24 self.namespace.hash(state);
25 self.agent_type.hash(state);
26 }
27}
28
29impl PartialEq for AgentType {
30 fn eq(&self, other: &Self) -> bool {
31 self.organization == other.organization
32 && self.namespace == other.namespace
33 && self.agent_type == other.agent_type
34 }
35}
36
37impl Eq for AgentType {}
38
39impl std::fmt::Display for AgentType {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(
42 f,
43 "{:x}/{:x}/{:x}",
44 self.organization, self.namespace, self.agent_type
45 )?;
46
47 if let Some(strings) = &self.strings {
48 write!(f, " ({}/{}/{})", strings.0, strings.1, strings.2)?;
49 }
50
51 Ok(())
52 }
53}
54
55impl From<&ProtoAgent> for AgentType {
56 fn from(agent: &ProtoAgent) -> Self {
57 Self {
58 organization: agent.organization,
59 namespace: agent.namespace,
60 agent_type: agent.agent_type,
61 strings: None,
62 }
63 }
64}
65
66impl AgentType {
67 pub fn new(organization: u64, namespace: u64, agent_type: u64) -> Self {
69 Self {
70 organization,
71 namespace,
72 agent_type,
73 strings: None,
74 }
75 }
76
77 pub fn from_strings(organization: &str, namespace: &str, agent_type: &str) -> Self {
78 Self {
79 organization: calculate_hash(organization),
80 namespace: calculate_hash(namespace),
81 agent_type: calculate_hash(agent_type),
82 strings: Some(Box::new((
83 organization.to_string(),
84 namespace.to_string(),
85 agent_type.to_string(),
86 ))),
87 }
88 }
89
90 pub fn with_organization(self, organization: u64) -> Self {
91 Self {
92 organization,
93 ..self
94 }
95 }
96
97 pub fn with_namespace(self, namespace: u64) -> Self {
98 Self { namespace, ..self }
99 }
100
101 pub fn with_agent_type(self, agent_type: u64) -> Self {
102 Self { agent_type, ..self }
103 }
104
105 pub fn organization(&self) -> u64 {
106 self.organization
107 }
108
109 pub fn namespace(&self) -> u64 {
110 self.namespace
111 }
112
113 pub fn agent_type(&self) -> u64 {
114 self.agent_type
115 }
116}
117
118#[derive(Hash, Eq, PartialEq, Debug, Clone, Default)]
119pub struct Agent {
120 agent_type: AgentType,
121 agent_id: u64,
122}
123
124impl std::fmt::Display for Agent {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(
127 f,
128 "{:x}/{:x}/{:x}/{:x}",
129 self.agent_type.organization(),
130 self.agent_type.namespace(),
131 self.agent_type.agent_type(),
132 self.agent_id
133 )
134 }
135}
136
137impl From<&ProtoAgent> for Agent {
138 fn from(agent: &ProtoAgent) -> Self {
139 Self {
140 agent_type: AgentType::from(agent),
141 agent_id: agent.agent_id.expect("agent id not found"),
142 }
143 }
144}
145
146impl Agent {
147 pub fn new(agent_type: AgentType, agent_id: u64) -> Self {
149 Self {
150 agent_type,
151 agent_id,
152 }
153 }
154
155 pub fn from_strings(
156 organization: &str,
157 namespace: &str,
158 agent_type: &str,
159 agent_id: u64,
160 ) -> Self {
161 Self {
162 agent_type: AgentType::from_strings(organization, namespace, agent_type),
163 agent_id,
164 }
165 }
166
167 pub fn with_agent_id(self, agent_id: u64) -> Self {
168 Self { agent_id, ..self }
169 }
170
171 pub fn with_agent_type(self, agent_type: AgentType) -> Self {
172 Self { agent_type, ..self }
173 }
174
175 pub fn agent_type(&self) -> &AgentType {
176 &self.agent_type
177 }
178
179 pub fn agent_id(&self) -> u64 {
180 self.agent_id
181 }
182
183 pub fn agent_id_option(&self) -> Option<u64> {
184 if self.agent_id == DEFAULT_AGENT_ID {
185 return None;
186 }
187
188 Some(self.agent_id)
189 }
190}
191
192pub fn calculate_hash<T: Hash + ?Sized>(t: &T) -> u64 {
193 let mut s = DefaultHasher::new();
194 t.hash(&mut s);
195 s.finish()
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_name_encoder() {
204 let encode1 = AgentType::from_strings("Cisco", "Default", "Agent_ONE");
206 let encode2 = AgentType::from_strings("Cisco", "Default", "Agent_ONE");
207 assert_eq!(encode1, encode2);
208 let encode3 = AgentType::from_strings("not_Cisco", "not_Default", "not_Agent_ONE");
209 assert_ne!(encode1, encode3);
210
211 let encode4 = AgentType::from_strings("Cisco", "Cisco", "Agent_ONE");
212 assert_eq!(encode4.organization(), encode4.namespace());
213
214 let agent_type = AgentType::from_strings("Cisco", "Default", "Agent_ONE");
216 let agent_id = Agent::from_strings("Cisco", "Default", "Agent_ONE", 1);
217 assert_eq!(agent_type, *agent_id.agent_type());
218 }
219}