Cesar Rodas 2 роки тому
батько
коміт
ecc1e17d87

+ 3 - 0
crates/types/src/client/mod.rs

@@ -1,3 +1,6 @@
+//! # Nostr Protocol Types. Client side
+//!
+//! Messages that are going to be sent by a client
 pub mod request;
 
 pub use request::Request;

+ 10 - 0
crates/types/src/client/request.rs

@@ -1,9 +1,19 @@
+//! # Nostr Protocol Types. Request
+//!
+//! Used to request events and subscribe to new updates.
 use crate::types;
 use serde::ser::{self, SerializeSeq, Serializer};
 
+/// Request: used to request events and subscribe to new updates.
+///
+/// More details at https://github.com/nostr-protocol/nips/blob/master/01.md#communication-between-clients-and-relays
 #[derive(Debug, Clone, Default)]
 pub struct Request {
+    /// The new subscription ID. If not provided, a default one will be created
+    /// automatically
     pub subscription_id: types::SubscriptionId,
+    /// List of filters to requests events and subscribe to new updates
+    /// Events that match any of the filters are to be returned, i.e., multiple filters are to be interpreted as || conditions.
     pub filters: Vec<types::Filter>,
 }
 

+ 43 - 16
crates/types/src/lib.rs

@@ -1,28 +1,49 @@
+//! # Nostr Protocol Types
+//!
+//! The types needed to interact with a Nostr relayer, or to be become one.
+//#![deny(missing_docs, warnings)]
+
 use serde::{
     de::{self, Deserializer},
     Deserialize, Serialize,
 };
 use std::convert::TryFrom;
-use thiserror::Error;
 use types::{Event, SubscriptionId};
 
 pub mod client;
 pub mod types;
 
-#[derive(Debug, Error)]
-pub enum Error {}
-
 #[derive(Serialize, Debug, Clone)]
+/// Message
+///
+/// All the messages needed to interact with a Nostr protocol, are abstracted in
+/// this structure.
+///
+/// This Message can be used to compose messages to communicate with a relayer,
+/// or by a relayer to parse messages from a client
 pub enum Message {
+    /// Close a subscription
     Close(types::SubscriptionId),
+    /// An Event from a client.  This is when a client wants to publish an
+    /// event. The event must be signed.
     EventFromClient(types::Event),
-    EventFromServer(types::SubscriptionId, types::Event),
+    /// The relayer sends an event. This happens when an event matches
+    /// subscription that was created by a request
+    EventFromRelayer(types::SubscriptionId, types::Event),
+    /// Creates a subscription. Client tells the relayer which kind of events
+    /// they'd like to hear about
     Request(client::Request),
+    /// This is how server communicates about errors (most likely protocol
+    /// errors) to the client
     Notice(String),
+    /// The relayer sent all the events that matches your request already, from
+    /// now on, new events will be relayed as they appear and matches your
+    /// request
     EndOfStoredEvents(String),
 }
 
 impl Message {
+    /// Returns the current message as a Request, if possible
     pub fn as_request(&self) -> Option<&client::Request> {
         match self {
             Self::Request(x) => Some(x),
@@ -30,6 +51,7 @@ impl Message {
         }
     }
 
+    /// Returns the current Message as an EndOfStorageEvent, if possible
     pub fn as_end_of_stored_events(&self) -> Option<&str> {
         match self {
             Self::EndOfStoredEvents(subscription_id) => Some(subscription_id),
@@ -37,13 +59,16 @@ impl Message {
         }
     }
 
-    pub fn as_event_from_server(&self) -> Option<(&types::SubscriptionId, &types::Event)> {
+    /// Returns the message as current server as an event generated by a
+    /// relayer, if possible
+    pub fn as_event_from_relayer(&self) -> Option<(&types::SubscriptionId, &types::Event)> {
         match self {
-            Self::EventFromServer(id, event) => Some((id, event)),
+            Self::EventFromRelayer(id, event) => Some((id, event)),
             _ => None,
         }
     }
 
+    /// Returns the current message as an event from the client, if possible
     pub fn as_event_from_client(&self) -> Option<&types::Event> {
         match self {
             Self::EventFromClient(event) => Some(event),
@@ -51,6 +76,7 @@ impl Message {
         }
     }
 
+    /// Returns the current message as a notice, if possible
     pub fn as_notice(&self) -> Option<&str> {
         match self {
             Self::Notice(x) => Some(x),
@@ -58,6 +84,7 @@ impl Message {
         }
     }
 
+    /// Returns the current message as a close subscription id if possible
     pub fn as_close_subscription_id(&self) -> Option<&types::SubscriptionId> {
         match self {
             Self::Close(x) => Some(x),
@@ -117,7 +144,7 @@ impl<'de> de::Deserialize<'de> for Message {
                         .is_valid()
                         .map_err(|e| de::Error::custom(e.to_string()))?;
 
-                    Ok(Self::EventFromServer(subscription_id, event))
+                    Ok(Self::EventFromRelayer(subscription_id, event))
                 }
                 2 => {
                     let event: Event = serde_json::from_value(array[1].clone())
@@ -197,9 +224,9 @@ mod test {
     fn event_from_server() {
         let json = "[\"EVENT\",\"640bddcc93eae\",{\"content\":\"🤙\",\"created_at\":1676637072,\"id\":\"a3eaa71e4f46c1a69ac0596ae7c2af35807fc0b0d3b208b79a36eef67ef51743\",\"kind\":7,\"pubkey\":\"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78\",\"sig\":\"93a3e9c4f6cb9a704885c4f77f6d7b16153e8eedc967603602606147f8c78d426f547d54120b80b33bd2101de638c06f2932df4daf53d66ca9b1341f2fd45729\",\"tags\":[[\"p\",\"8fe53b37518e3dbe9bab26d912292001d8b882de9456b7b08b615f912dc8bf4a\",\"\",\"mention\"],[\"e\",\"eb278e983fcedbb0d143c4250c879d078d037586c5dca8e1cf1a104f9846a460\"],[\"p\",\"2bda4f03446bc1c6ff00594e350a1e7ec57fb9cdc4a7b52694223d56ce0599e9\"]]}]";
         let message: Message = serde_json::from_str(json).expect("valid message");
-        assert!(message.as_event_from_server().is_some());
+        assert!(message.as_event_from_relayer().is_some());
 
-        let (_, event) = message.as_event_from_server().expect("event");
+        let (_, event) = message.as_event_from_relayer().expect("event");
         assert_eq!(
             "🤙".to_owned(),
             event.data.content.try_to_string().expect("string")
@@ -210,9 +237,9 @@ mod test {
     fn follow_list() {
         let json = r#"["EVENT","640e914a22321",{"content":"{\"wss:\\/\\/nos.lol\":{\"write\":true,\"read\":true},\"wss:\\/\\/relay.damus.io\":{\"write\":true,\"read\":true},\"wss:\\/\\/brb.io\":{\"write\":true,\"read\":true},\"wss:\\/\\/nostr.orangepill.dev\":{\"write\":true,\"read\":true},\"wss:\\/\\/relay.current.fyi\":{\"write\":true,\"read\":true},\"wss:\\/\\/eden.nostr.land\":{\"write\":true,\"read\":true},\"wss:\\/\\/relay.snort.social\":{\"write\":true,\"read\":true}}","created_at":1678476548,"id":"b8d7f6a19c3d9625b9aade947166708fbc6ab2dd7e3f3af84f1de08ea10d6f38","kind":3,"pubkey":"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78","sig":"352485a162805e72a1e278a4a7bc33facd54a71e0f4c23934f35eee57eaa38c62e6260bf7624e2bd96bc6bcf93373b06c5245e28a266bfe2fbf064224e713fd6","tags":[["p","3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"],["p","b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78"],["p","387519cafd325668ecffe59577f37238638da4cf2d985b82f932fc81d33da1e8"],["p","81d0ccce4591fc4e19e3ef752a2b003ef23a986cb31e7835ea7d8d7cd96d47ea"],["p","0861144c765ea10e39a48473a51bee604886e18abd0f831cc5ed7651e68a1caf"],["p","1779284c21126b5e1af6dcb84949ceacad781ce4ce0d1691292a41229465a54a"],["p","d7df5567015930b17c125b3a7cf29bef23aa5a68d09cd6518d291359606aab7b"],["p","82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2"],["p","32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],["p","a47457722e10ba3a271fbe7040259a3c4da2cf53bfd1e198138214d235064fc2"],["p","e1055729d51e037b3c14e8c56e2c79c22183385d94aadb32e5dc88092cd0fef4"],["p","2bda4f03446bc1c6ff00594e350a1e7ec57fb9cdc4a7b52694223d56ce0599e9"],["p","c57717ec7a6af20b836a9468282948dc0adba64d30abfb40aa8c6664dde3cfc7"],["p","6094dd769f94fab1a2915c1a3d8360e49cec84977f433a872bea1763466db784"]]}]"#;
         let message: Message = serde_json::from_str(json).expect("valid message");
-        assert!(message.as_event_from_server().is_some());
+        assert!(message.as_event_from_relayer().is_some());
 
-        let (_, event) = message.as_event_from_server().expect("event");
+        let (_, event) = message.as_event_from_relayer().expect("event");
         assert_eq!(event.data.tags.len(), 14);
         event
             .data
@@ -228,15 +255,15 @@ mod test {
     fn direct_message() {
         let json = r#"["EVENT","640e9851d461a",{"content":"/9Xk4PfEF8hU+C1wq4grww==?iv=XB/ytLhL0WKQ1of4wXIXCg==","created_at":1677726088,"id":"4643c79276730e25a8510163622335bb9d44aecdd0c596409804abb29910652d","kind":4,"pubkey":"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78","sig":"23790fae080eaa87f2abf2df4e5fd49cecc67e58271910d7e871fb197641294cad92c2a6fff1501982d776648eecb1e4650ccfbb742a2d901226a9b00c310489","tags":[["p","b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78"]]}]"#;
         let message: Message = serde_json::from_str(json).expect("valid message");
-        assert!(message.as_event_from_server().is_some());
+        assert!(message.as_event_from_relayer().is_some());
 
-        let (_, event) = message.as_event_from_server().expect("event");
-        let content = Content::DirectMessage(EncryptedData {
+        let (_, event) = message.as_event_from_relayer().expect("event");
+        let content = Content::EncryptedDirectMessage(EncryptedData {
             encrypted_message: vec![
                 255, 213, 228, 224, 247, 196, 23, 200, 84, 248, 45, 112, 171, 136, 43, 195,
             ],
             iv: vec![
-                255, 213, 228, 224, 247, 196, 23, 200, 84, 248, 45, 112, 171, 136, 43, 195,
+                92, 31, 242, 180, 184, 75, 209, 98, 144, 214, 135, 248, 193, 114, 23, 10,
             ],
         });
         assert_eq!(event.data.tags.len(), 1);

+ 45 - 30
crates/types/src/types/addr.rs

@@ -1,3 +1,9 @@
+//! Addresses
+//!
+//! Abstract public-key addresses, and their prefixes.
+//!
+//! It can also parse the bech32 version of the public key if provided (used by
+//! clients mostly)
 use bech32::{self, FromBase32, ToBase32, Variant};
 use serde::{
     de::{self, Deserialize, Deserializer},
@@ -5,69 +11,81 @@ use serde::{
 };
 use thiserror::Error;
 
+/// Errors
 #[derive(Error, Debug)]
 pub enum Error {
+    /// Invalid Bech32, invalid human readable part.
     #[error("Unexpected HRP (Human readable part)")]
     UnexpectedHrp,
 
+    /// Error decoding, provided addresses is not a hex-encoded string
     #[error("Error decoding: {0}")]
     DecodingError(#[from] hex::FromHexError),
 
+    /// Data is longer than 32-bytes (when converted to binary/Vec<u32>)
     #[error("Data is too long ({0} bytes). Addresses are up to 32 bytes")]
     TooLong(usize),
 
+    /// Error decoding/encoding to Bech32
     #[error("Bech32 error: {0}")]
     Bech32(#[from] bech32::Error),
 }
 
+/// Human Readable Part
+///
+/// Which HDR has been used to encode this address with Bech32
 #[derive(Debug, Clone, PartialEq, Eq, Copy)]
-pub enum Type {
+pub enum HumanReadablePart {
+    /// Public Key / Account Address
     NPub,
+    /// Private key
     NSec,
+    /// A user note / posts
     Note,
-    Unknown,
 }
 
-impl ToString for Type {
+impl ToString for HumanReadablePart {
     fn to_string(&self) -> String {
         match *self {
             Self::NPub => "npub".to_owned(),
             Self::NSec => "nsec".to_owned(),
             Self::Note => "note".to_owned(),
-            Self::Unknown => "".to_owned(),
         }
     }
 }
 
-impl Default for Type {
-    fn default() -> Self {
-        Self::Unknown
-    }
-}
-
+/// Public-key based Address
+///
+/// Clients may want to use the Bech32 encoded address *but* the protocol only
+/// cares about hex-encoded binary data.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Addr {
+    /// Bytes (up to 32 bytes)
     pub bytes: Vec<u8>,
-    pub typ: Type,
+    /// HDR if provided
+    pub hrp: Option<HumanReadablePart>,
 }
 
 impl Addr {
-    pub fn try_from_public_key_str(typ: Type, pk: &str) -> Result<Self, Error> {
-        Addr::try_from_bytes(typ, hex::decode(pk)?)
+    pub fn try_from_public_key_str(
+        hrp: Option<HumanReadablePart>,
+        pk: &str,
+    ) -> Result<Self, Error> {
+        Addr::try_from_bytes(hrp, hex::decode(pk)?)
     }
 
-    pub fn try_from_bytes(typ: Type, bytes: Vec<u8>) -> Result<Self, Error> {
+    pub fn try_from_bytes(hrp: Option<HumanReadablePart>, bytes: Vec<u8>) -> Result<Self, Error> {
         if bytes.len() > 32 {
             return Err(Error::TooLong(bytes.len()));
         }
 
-        Ok(Addr { bytes, typ })
+        Ok(Addr { bytes, hrp })
     }
 
-    pub fn change_type(self, new_type: Type) -> Self {
+    pub fn change_type(self, new_hrp: Option<HumanReadablePart>) -> Self {
         Self {
             bytes: self.bytes,
-            typ: new_type,
+            hrp: new_hrp,
         }
     }
 
@@ -77,7 +95,7 @@ impl Addr {
 
     pub fn try_to_human(&self) -> Result<String, Error> {
         Ok(bech32::encode(
-            &self.typ.to_string(),
+            self.hrp.map(|t| t.to_string()).unwrap_or_default().as_str(),
             self.bytes.to_base32(),
             Variant::Bech32,
         )?)
@@ -89,22 +107,19 @@ impl TryFrom<String> for Addr {
 
     fn try_from(value: String) -> Result<Self, Self::Error> {
         if let Ok(bytes) = hex::decode(&value) {
-            return Ok(Self {
-                bytes,
-                typ: Type::Unknown,
-            });
+            return Ok(Self { bytes, hrp: None });
         }
         let (hrp, bytes, _) = bech32::decode(&value)?;
-        let typ = match hrp.to_lowercase().as_str() {
-            "npub" => Ok(Type::NPub),
-            "nsec" => Ok(Type::NSec),
-            "note" => Ok(Type::Note),
+        let hrp = match hrp.to_lowercase().as_str() {
+            "npub" => Ok(HumanReadablePart::NPub),
+            "nsec" => Ok(HumanReadablePart::NSec),
+            "note" => Ok(HumanReadablePart::Note),
             _ => Err(Error::UnexpectedHrp),
         }?;
 
         Ok(Self {
             bytes: Vec::<u8>::from_base32(&bytes)?,
-            typ,
+            hrp: Some(hrp),
         })
     }
 }
@@ -148,10 +163,10 @@ mod test {
     fn deserializes_json() {
         let json_str = "\"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78\"";
         let x: Addr = serde_json::from_str(json_str).expect("valid pk");
-        assert_eq!(Type::Unknown, x.typ);
+        assert_eq!(None, x.hrp);
         assert_eq!(
             "npub1k2q4dqk0eqlu6tp6m5zhsh852u7a8zz9wp5ewnxxmrx2q6eu8duq3ydzzr",
-            x.change_type(Type::NPub)
+            x.change_type(Some(HumanReadablePart::NPub))
                 .try_to_human()
                 .expect("valid addr"),
         );
@@ -160,7 +175,7 @@ mod test {
     #[test]
     fn deserializes_from_public_key_and_json() {
         let addr1 = Addr::try_from_public_key_str(
-            Type::NPub,
+            Some(HumanReadablePart::NPub),
             "b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78",
         )
         .expect("valid addr");

+ 27 - 30
crates/types/src/types/content/mod.rs

@@ -6,6 +6,8 @@ use serde::{
 use std::collections::HashMap;
 use thiserror::Error;
 
+use super::Kind;
+
 pub mod profile;
 
 #[derive(Error, Debug)]
@@ -31,12 +33,12 @@ pub struct EncryptedData {
 
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub enum Content {
-    Profile(profile::Profile),
-    Text(String),
-    RecommendServer(String),
-    ContactList(HashMap<String, RelaySettings>),
-    DirectMessage(EncryptedData),
-    Unknown(u32, String),
+    Metadata(profile::Profile),
+    ShortTextNote(String),
+    RecommendRelayer(String),
+    Contacts(HashMap<String, RelaySettings>),
+    EncryptedDirectMessage(EncryptedData),
+    Unknown(Kind, String),
 }
 
 impl ser::Serialize for Content {
@@ -53,53 +55,48 @@ impl ser::Serialize for Content {
 }
 
 impl Content {
-    pub fn kind(&self) -> u32 {
-        match self {
-            Self::Profile(_) => 0,
-            Self::Text(_) => 1,
-            Self::RecommendServer(_) => 2,
-            Self::ContactList(_) => 3,
-            Self::DirectMessage(_) => 4,
-            Self::Unknown(k, _) => *k,
-        }
+    pub fn kind(&self) -> Kind {
+        self.into()
     }
 
-    pub fn deserialize(kind: u32, content: &str) -> Result<Self, Error> {
+    pub fn deserialize(kind: Kind, content: &str) -> Result<Self, Error> {
         match kind {
-            0 => Ok(Self::Profile(serde_json::from_str(content)?)),
-            1 => Ok(Self::Text(content.to_owned())),
-            2 => Ok(Self::RecommendServer(content.to_owned())),
-            3 => Ok(Self::ContactList(serde_json::from_str(content)?)),
-            4 => {
+            Kind::Metadata => Ok(Self::Metadata(serde_json::from_str(content)?)),
+            Kind::ShortTextNote => Ok(Self::ShortTextNote(content.to_owned())),
+            Kind::RecommendRelayer => Ok(Self::RecommendRelayer(content.to_owned())),
+            Kind::Contacts => Ok(Self::Contacts(serde_json::from_str(content)?)),
+            Kind::EncryptedDirectMessages => {
                 let parts = content.split("?iv=").collect::<Vec<&str>>();
                 if parts.len() == 2 {
-                    let encrypted_message = general_purpose::STANDARD.decode(&parts[0])?;
-                    let iv = general_purpose::STANDARD.decode(&parts[0])?;
+                    let encrypted_message = general_purpose::STANDARD.decode(parts[0])?;
+                    let iv = general_purpose::STANDARD.decode(parts[1])?;
 
-                    Ok(Self::DirectMessage(EncryptedData {
+                    Ok(Self::EncryptedDirectMessage(EncryptedData {
                         encrypted_message,
                         iv,
                     }))
                 } else {
-                    Ok(Self::Unknown(4, content.to_owned()))
+                    Ok(Self::Unknown(Kind::Unknown(4), content.to_owned()))
                 }
             }
-            _ => Ok(Self::Unknown(kind, content.to_owned())),
+            kind => Ok(Self::Unknown(kind, content.to_owned())),
         }
     }
 
     pub fn try_to_string(&self) -> Result<String, Error> {
         match self {
-            Self::Profile(p) => Ok(serde_json::to_string(&p)?),
-            Self::ContactList(p) => Ok(serde_json::to_string(&p)?),
-            Self::DirectMessage(message) => {
+            Self::Metadata(p) => Ok(serde_json::to_string(&p)?),
+            Self::Contacts(p) => Ok(serde_json::to_string(&p)?),
+            Self::EncryptedDirectMessage(message) => {
                 let encrypted_message =
                     general_purpose::STANDARD.encode(&message.encrypted_message);
                 let iv = general_purpose::STANDARD.encode(&message.iv);
                 let to_encode = format!("{}?iv={}", encrypted_message, iv);
                 Ok(serde_json::to_string(&to_encode)?)
             }
-            Self::Text(t) | Self::RecommendServer(t) | Self::Unknown(_, t) => Ok(t.to_owned()),
+            Self::ShortTextNote(t) | Self::RecommendRelayer(t) | Self::Unknown(_, t) => {
+                Ok(t.to_owned())
+            }
         }
     }
 }

+ 3 - 3
crates/types/src/types/event.rs

@@ -1,4 +1,4 @@
-use super::{content, Content, Id, Signature};
+use super::{content, Content, Id, Kind, Signature};
 use chrono::{DateTime, Utc};
 use secp256k1::{schnorr, Message, XOnlyPublicKey};
 use serde::{
@@ -33,7 +33,7 @@ pub struct Data {
     pub public_key: Id,
     #[serde(with = "super::ts_seconds")]
     pub created_at: DateTime<Utc>,
-    pub kind: u32,
+    pub kind: Kind,
     pub tags: Vec<super::Tag>,
     #[serde(skip_serializing)]
     pub content: Content,
@@ -52,7 +52,7 @@ impl<'de> de::Deserialize<'de> for Data {
             pub public_key: Id,
             #[serde(with = "super::ts_seconds")]
             pub created_at: DateTime<Utc>,
-            pub kind: u32,
+            pub kind: Kind,
             pub tags: Vec<super::Tag>,
             pub content: String,
         }

+ 74 - 0
crates/types/src/types/kind.rs

@@ -0,0 +1,74 @@
+use super::Content;
+use serde::{
+    de::{self, Deserializer},
+    ser::{self, Serializer},
+    Deserialize,
+};
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Kind {
+    Metadata,
+    ShortTextNote,
+    RecommendRelayer,
+    Contacts,
+    EncryptedDirectMessages,
+    Unknown(u32),
+}
+
+impl From<Kind> for u32 {
+    fn from(kind: Kind) -> Self {
+        match kind {
+            Kind::Metadata => 0,
+            Kind::ShortTextNote => 1,
+            Kind::RecommendRelayer => 2,
+            Kind::Contacts => 3,
+            Kind::EncryptedDirectMessages => 4,
+            Kind::Unknown(t) => t,
+        }
+    }
+}
+
+impl From<u32> for Kind {
+    fn from(kind: u32) -> Self {
+        match kind {
+            0 => Kind::Metadata,
+            1 => Kind::ShortTextNote,
+            2 => Kind::RecommendRelayer,
+            3 => Kind::Contacts,
+            4 => Kind::EncryptedDirectMessages,
+            any => Kind::Unknown(any),
+        }
+    }
+}
+
+impl ser::Serialize for Kind {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_u32((*self).into())
+    }
+}
+
+impl<'de> de::Deserialize<'de> for Kind {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let raw: u32 = Deserialize::deserialize(deserializer)?;
+        Ok(raw.into())
+    }
+}
+
+impl From<&Content> for Kind {
+    fn from(content: &Content) -> Self {
+        match content {
+            Content::Metadata(_) => Kind::Metadata,
+            Content::ShortTextNote(_) => Kind::ShortTextNote,
+            Content::RecommendRelayer(_) => Kind::RecommendRelayer,
+            Content::Contacts(_) => Kind::Contacts,
+            Content::EncryptedDirectMessage(_) => Kind::EncryptedDirectMessages,
+            Content::Unknown(kind, _) => *kind,
+        }
+    }
+}

+ 3 - 0
crates/types/src/types/mod.rs

@@ -1,8 +1,10 @@
+//! Common shared types between clients and relayers
 pub mod addr;
 pub mod content;
 pub mod event;
 pub mod filter;
 pub mod id;
+pub mod kind;
 pub mod signature;
 pub mod subscription_id;
 pub mod tag;
@@ -77,6 +79,7 @@ pub use self::{
     event::{Data, Event},
     filter::Filter,
     id::Id,
+    kind::Kind,
     signature::Signature,
     subscription_id::SubscriptionId,
     tag::Tag,

+ 8 - 0
crates/types/src/types/tag/key.rs

@@ -0,0 +1,8 @@
+use super::Addr;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Event {
+    pub id: Addr,
+    pub relayer_url: Option<String>,
+    pub marker: Option<String>,
+}

+ 55 - 32
crates/types/src/types/tag.rs → crates/types/src/types/tag/mod.rs

@@ -5,32 +5,43 @@ use serde::{
 };
 use thiserror::Error;
 
+mod key;
+mod public_key;
+
+pub use self::{key::*, public_key::*};
+
 #[derive(Error, Debug)]
 pub enum Error {
     #[error("Subscription ID is limited to 64 characters")]
     TooLong,
 }
 
+/// Tags are how events relates to each other.
+///
+/// So far, there are two standard tags, Events and Public Key. This an event
+/// can be related to another, with extra options (like a relayer url, and a pet
+/// name).
+///
+/// Any non standard tag will be parsed as Unknown with a vector of strings as
+/// their parameters
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum Tag {
-    Event(Addr, Option<String>, Vec<String>),
-    PubKey(Addr, Option<String>, Vec<String>),
+    Event(Event),
+    /// Tag another public key
+    PubKey(PubKey),
+    /// Any non standard tag
     Unknown(String, Vec<String>),
 }
 
 impl Tag {
+    /// Is the tag a public key?
     pub fn is_pubkey(&self) -> bool {
-        match self {
-            Self::PubKey(_, _, _) => true,
-            _ => false,
-        }
+        matches!(self, Self::PubKey(_))
     }
 
+    /// Is the tag an event?
     pub fn is_event(&self) -> bool {
-        match self {
-            Self::Event(_, _, _) => true,
-            _ => false,
-        }
+        matches!(self, Self::Event(_))
     }
 }
 
@@ -40,8 +51,8 @@ impl ser::Serialize for Tag {
         S: Serializer,
     {
         let typ = match self {
-            Tag::Event(_, _, _) => "e",
-            Tag::PubKey(_, _, _) => "p",
+            Tag::Event(_) => "e",
+            Tag::PubKey(_) => "p",
             Tag::Unknown(u, _) => u,
         };
 
@@ -49,12 +60,21 @@ impl ser::Serialize for Tag {
         seq.serialize_element(typ)?;
 
         match self {
-            Tag::Event(addr, relayer, extra) | Tag::PubKey(addr, relayer, extra) => {
-                seq.serialize_element(&addr.to_hex())?;
-                if let Some(relayer) = relayer {
+            Tag::Event(event) => {
+                seq.serialize_element(&event.id.to_hex())?;
+                if let Some(relayer) = &event.relayer_url {
                     seq.serialize_element(&relayer)?;
-                    for extra in extra.iter() {
-                        seq.serialize_element(extra)?;
+                    if let Some(marker) = &event.marker {
+                        seq.serialize_element(&marker)?;
+                    }
+                }
+            }
+            Tag::PubKey(key) => {
+                seq.serialize_element(&key.id.to_hex())?;
+                if let Some(relayer) = &key.relayer_url {
+                    seq.serialize_element(relayer)?;
+                    if let Some(pet_name) = &key.pet_name {
+                        seq.serialize_element(pet_name)?;
                     }
                 }
             }
@@ -80,22 +100,26 @@ impl<'de> Deserialize<'de> for Tag {
 
         Ok(match parts[0].as_str() {
             "e" | "p" => {
-                let addr = parts[1]
+                let id: Addr = parts[1]
                     .as_str()
                     .try_into()
                     .map_err(|e: super::addr::Error| de::Error::custom(e.to_string()))?;
 
-                let relayer_addr = parts.get(2).cloned();
-                let extra = if parts.len() < 3 {
-                    vec![]
-                } else {
-                    parts[3..].to_owned()
-                };
+                let relayer_url = parts.get(2).cloned();
+                let extra = parts.get(3).cloned();
 
                 if "e" == parts[0].as_str() {
-                    Tag::Event(addr, relayer_addr, extra)
+                    Tag::Event(Event {
+                        id,
+                        relayer_url,
+                        marker: extra,
+                    })
                 } else {
-                    Tag::PubKey(addr, relayer_addr, extra)
+                    Tag::PubKey(PubKey {
+                        id,
+                        relayer_url,
+                        pet_name: extra,
+                    })
                 }
             }
             extra => Tag::Unknown(extra.to_owned(), parts[1..].to_owned()),
@@ -127,12 +151,11 @@ mod test {
         let json = "[\"p\",\"a0b0c0d0\"]";
         let tag: Tag = serde_json::from_str(&json).expect("valid json");
         assert_eq!(
-            Tag::PubKey(
-                Addr::try_from_public_key_str(crate::types::addr::Type::Unknown, "a0b0c0d0")
-                    .expect("valid addr"),
-                None,
-                vec![]
-            ),
+            Tag::PubKey(PubKey {
+                id: Addr::try_from_public_key_str(None, "a0b0c0d0").expect("valid addr"),
+                relayer_url: None,
+                pet_name: None,
+            }),
             tag
         );
     }

+ 8 - 0
crates/types/src/types/tag/public_key.rs

@@ -0,0 +1,8 @@
+use super::Addr;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct PubKey {
+    pub id: Addr,
+    pub relayer_url: Option<String>,
+    pub pet_name: Option<String>,
+}