nostr_types/types/
id.rs

1use crate::Error;
2use derive_more::{AsMut, AsRef, Deref, Display, From, FromStr, Into};
3use serde::de::{Deserializer, Visitor};
4use serde::ser::Serializer;
5use serde::{Deserialize, Serialize};
6#[cfg(feature = "speedy")]
7use speedy::{Readable, Writable};
8use std::fmt;
9
10/// An event identifier, constructed as a SHA256 hash of the event fields according to NIP-01
11#[derive(
12    AsMut, AsRef, Clone, Copy, Debug, Deref, Eq, From, Hash, Into, Ord, PartialEq, PartialOrd,
13)]
14#[cfg_attr(feature = "speedy", derive(Readable, Writable))]
15pub struct Id(pub [u8; 32]);
16
17impl Id {
18    /// Render into a hexadecimal string
19    ///
20    /// Consider converting `.into()` an `IdHex` which is a wrapped type rather than a naked `String`
21    pub fn as_hex_string(&self) -> String {
22        hex::encode(self.0)
23    }
24
25    /// Create from a hexadecimal string
26    pub fn try_from_hex_string(v: &str) -> Result<Id, Error> {
27        let vec: Vec<u8> = hex::decode(v)?;
28        Ok(Id(vec
29            .try_into()
30            .map_err(|_| Error::WrongLengthHexString)?))
31    }
32
33    /// Export as a bech32 encoded string ("note")
34    pub fn as_bech32_string(&self) -> String {
35        bech32::encode::<bech32::Bech32>(*crate::HRP_NOTE, &self.0).unwrap()
36    }
37
38    /// Import from a bech32 encoded string ("note")
39    pub fn try_from_bech32_string(s: &str) -> Result<Id, Error> {
40        let data = bech32::decode(s)?;
41        if data.0 != *crate::HRP_NOTE {
42            Err(Error::WrongBech32(
43                crate::HRP_NOTE.to_lowercase(),
44                data.0.to_lowercase(),
45            ))
46        } else if data.1.len() != 32 {
47            Err(Error::InvalidId)
48        } else {
49            match <[u8; 32]>::try_from(data.1) {
50                Ok(array) => Ok(Id(array)),
51                _ => Err(Error::InvalidId),
52            }
53        }
54    }
55
56    // Mock data for testing
57    #[allow(dead_code)]
58    pub(crate) fn mock() -> Id {
59        Id::try_from_hex_string("5df64b33303d62afc799bdc36d178c07b2e1f0d824f31b7dc812219440affab6")
60            .unwrap()
61    }
62}
63
64impl Serialize for Id {
65    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66    where
67        S: Serializer,
68    {
69        serializer.serialize_str(&hex::encode(self.0))
70    }
71}
72
73impl<'de> Deserialize<'de> for Id {
74    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75    where
76        D: Deserializer<'de>,
77    {
78        deserializer.deserialize_str(IdVisitor)
79    }
80}
81
82struct IdVisitor;
83
84impl Visitor<'_> for IdVisitor {
85    type Value = Id;
86
87    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        write!(f, "a lowercase hexadecimal string representing 32 bytes")
89    }
90
91    fn visit_str<E>(self, v: &str) -> Result<Id, E>
92    where
93        E: serde::de::Error,
94    {
95        let vec: Vec<u8> = hex::decode(v).map_err(|e| serde::de::Error::custom(format!("{e}")))?;
96
97        Ok(Id(vec.try_into().map_err(|e: Vec<u8>| {
98            E::custom(format!(
99                "Id is not 32 bytes long. Was {} bytes long",
100                e.len()
101            ))
102        })?))
103    }
104}
105
106/// An event identifier, constructed as a SHA256 hash of the event fields according to NIP-01, as a hex string
107///
108/// You can convert from an `Id` into this with `From`/`Into`.  You can convert this back to an `Id` with `TryFrom`/`TryInto`.
109#[derive(
110    AsMut,
111    AsRef,
112    Clone,
113    Debug,
114    Deref,
115    Display,
116    Eq,
117    From,
118    FromStr,
119    Hash,
120    Into,
121    Ord,
122    PartialEq,
123    PartialOrd,
124)]
125#[cfg_attr(feature = "speedy", derive(Readable, Writable))]
126pub struct IdHex(String);
127
128impl IdHex {
129    // Mock data for testing
130    #[allow(dead_code)]
131    pub(crate) fn mock() -> IdHex {
132        From::from(Id::mock())
133    }
134
135    /// Try from &str
136    pub fn try_from_str(s: &str) -> Result<IdHex, Error> {
137        Self::try_from_string(s.to_owned())
138    }
139
140    /// Try from String
141    pub fn try_from_string(s: String) -> Result<IdHex, Error> {
142        if s.len() != 64 {
143            return Err(Error::InvalidId);
144        }
145        let vec: Vec<u8> = hex::decode(&s)?;
146        if vec.len() != 32 {
147            return Err(Error::InvalidId);
148        }
149        Ok(IdHex(s))
150    }
151
152    /// As &str
153    pub fn as_str(&self) -> &str {
154        &self.0
155    }
156
157    /// Into String
158    pub fn into_string(self) -> String {
159        self.0
160    }
161}
162
163impl TryFrom<&str> for IdHex {
164    type Error = Error;
165
166    fn try_from(s: &str) -> Result<IdHex, Error> {
167        IdHex::try_from_str(s)
168    }
169}
170
171impl From<Id> for IdHex {
172    fn from(i: Id) -> IdHex {
173        IdHex(i.as_hex_string())
174    }
175}
176
177impl From<IdHex> for Id {
178    fn from(h: IdHex) -> Id {
179        // could only fail if IdHex is invalid
180        Id::try_from_hex_string(&h.0).unwrap()
181    }
182}
183
184impl Serialize for IdHex {
185    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186    where
187        S: Serializer,
188    {
189        serializer.serialize_str(&self.0)
190    }
191}
192
193impl<'de> Deserialize<'de> for IdHex {
194    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
195    where
196        D: Deserializer<'de>,
197    {
198        deserializer.deserialize_str(IdHexVisitor)
199    }
200}
201
202struct IdHexVisitor;
203
204impl Visitor<'_> for IdHexVisitor {
205    type Value = IdHex;
206
207    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
208        write!(f, "a lowercase hexadecimal string representing 32 bytes")
209    }
210
211    fn visit_str<E>(self, v: &str) -> Result<IdHex, E>
212    where
213        E: serde::de::Error,
214    {
215        if v.len() != 64 {
216            return Err(serde::de::Error::custom("IdHex is not 64 characters long"));
217        }
218
219        let vec: Vec<u8> = hex::decode(v).map_err(|e| serde::de::Error::custom(format!("{e}")))?;
220        if vec.len() != 32 {
221            return Err(serde::de::Error::custom("Invalid IdHex"));
222        }
223
224        Ok(IdHex(v.to_owned()))
225    }
226}
227
228#[cfg(test)]
229mod test {
230    use super::*;
231
232    test_serde! {Id, test_id_serde}
233    test_serde! {IdHex, test_id_hex_serde}
234
235    #[test]
236    fn test_id_bech32() {
237        let bech32 = Id::mock().as_bech32_string();
238        println!("{bech32}");
239        assert_eq!(Id::mock(), Id::try_from_bech32_string(&bech32).unwrap());
240    }
241}