hyveos_core/
dht.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    error::{Error, Result},
6    grpc,
7};
8
9#[derive(Debug, Clone, Hash, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11pub struct Key {
12    pub topic: String,
13    pub key: Vec<u8>,
14}
15
16impl Key {
17    /// Create a vector of bytes from the key.
18    ///
19    /// The key is a concatenation of the topic and the key, separated by a slash.
20    ///
21    /// # Errors
22    ///
23    /// Returns an error if the topic contains a slash.
24    ///
25    /// # Example
26    ///
27    /// ```
28    /// use hyveos_core::dht::Key;
29    ///
30    /// let key = Key {
31    ///    topic: "topic".to_string(),
32    ///    key: "key".into(),
33    /// };
34    ///
35    /// let bytes = key.into_bytes().unwrap();
36    ///
37    /// assert_eq!(bytes, b"topic/key");
38    /// ```
39    pub fn into_bytes(self) -> Result<Vec<u8>> {
40        let topic_bytes = self.topic.into_bytes();
41
42        if topic_bytes.contains(&b'/') {
43            return Err(Error::InvalidTopic);
44        }
45
46        Ok(topic_bytes
47            .into_iter()
48            .chain(Some(b'/'))
49            .chain(self.key)
50            .collect())
51    }
52
53    /// Create a key from a vector of bytes.
54    ///
55    /// The bytes should be a concatenation of the topic and the key, separated by a slash.
56    ///
57    /// # Errors
58    ///
59    /// Returns an error if the bytes do not contain a slash or if the topic is not valid UTF-8.
60    ///
61    /// # Example
62    ///
63    /// ```
64    /// use hyveos_core::dht::Key;
65    ///
66    /// let bytes = b"topic/key".to_vec();
67    /// let key = Key::from_bytes(bytes).unwrap();
68    ///
69    /// assert_eq!(key.topic, "topic");
70    /// assert_eq!(&key.key, b"key");
71    /// ```
72    pub fn from_bytes(mut bytes: Vec<u8>) -> Result<Self> {
73        let index = bytes
74            .iter()
75            .position(|b| *b == b'/')
76            .ok_or(Error::InvalidKey("Should contain '/'".to_string()))?;
77
78        let key = bytes.split_off(index + 1);
79        bytes.pop();
80
81        let topic = String::from_utf8(bytes).map_err(|e| Error::InvalidKey(e.to_string()))?;
82
83        Ok(Self { topic, key })
84    }
85}
86
87impl From<Key> for grpc::DhtKey {
88    fn from(key: Key) -> Self {
89        Self {
90            topic: grpc::Topic { topic: key.topic },
91            key: key.key,
92        }
93    }
94}
95
96impl From<grpc::DhtKey> for Key {
97    fn from(key: grpc::DhtKey) -> Self {
98        Self {
99            topic: key.topic.topic,
100            key: key.key,
101        }
102    }
103}