agp_datapath/messages/
encoder.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    // Store the original string representation of the agent type
17    // This is useful for debugging and logging purposes
18    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    /// Create a new AgentType
68    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    /// Create a new Agent
148    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        // test encode class
205        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        // test encode agent
215        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}