Browse Source

Adding server responses

Cesar Rodas 2 years ago
parent
commit
e8893ba702

+ 531 - 1
Cargo.lock

@@ -3,6 +3,15 @@
 version = 3
 
 [[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "android_system_properties"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -19,6 +28,12 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
 version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
@@ -30,6 +45,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
 
 [[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
 name = "block-buffer"
 version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -45,6 +66,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 
 [[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
 name = "cc"
 version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -82,6 +115,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
 name = "core-foundation-sys"
 version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -107,6 +150,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "custom_derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
+
+[[package]]
 name = "cxx"
 version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -161,6 +210,61 @@ dependencies = [
 ]
 
 [[package]]
+name = "enum_derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "406ac2a8c9eedf8af9ee1489bee9e50029278a6456c740f7454cf8a158abc816"
+
+[[package]]
+name = "env_logger"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
 name = "generic-array"
 version = "0.14.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -182,12 +286,41 @@ dependencies = [
 ]
 
 [[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
 name = "hex"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
 [[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
 name = "iana-time-zone"
 version = "0.1.53"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -212,6 +345,38 @@ dependencies = [
 ]
 
 [[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
+dependencies = [
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
 name = "itoa"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -242,6 +407,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
+[[package]]
 name = "log"
 version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -251,16 +422,32 @@ dependencies = [
 ]
 
 [[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
 name = "nostr"
 version = "0.1.0"
+dependencies = [
+ "env_logger",
+ "nostr-protocol-types",
+ "serde_json",
+ "tokio",
+ "tungstenite",
+ "url",
+]
 
 [[package]]
 name = "nostr-protocol-types"
 version = "0.1.0"
 dependencies = [
- "base64",
+ "base64 0.21.0",
  "bech32",
  "chrono",
+ "custom_derive",
+ "enum_derive",
  "hex",
  "rand",
  "secp256k1",
@@ -296,6 +483,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
 [[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
 name = "ppv-lite86"
 version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -350,18 +555,116 @@ dependencies = [
 ]
 
 [[package]]
+name = "regex"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rustix"
+version = "0.36.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
+dependencies = [
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
+dependencies = [
+ "base64 0.21.0",
+]
+
+[[package]]
 name = "ryu"
 version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
 
 [[package]]
+name = "schannel"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
+dependencies = [
+ "windows-sys 0.42.0",
+]
+
+[[package]]
 name = "scratch"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
 
 [[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
 name = "secp256k1"
 version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -380,6 +683,29 @@ dependencies = [
 ]
 
 [[package]]
+name = "security-framework"
+version = "2.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
 name = "serde"
 version = "1.0.153"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -411,6 +737,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "sha1"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
 name = "sha2"
 version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -422,6 +759,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
 name = "syn"
 version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -473,24 +816,110 @@ dependencies = [
 ]
 
 [[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
+dependencies = [
+ "autocfg",
+ "pin-project-lite",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "tungstenite"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788"
+dependencies = [
+ "base64 0.13.1",
+ "byteorder",
+ "bytes",
+ "http",
+ "httparse",
+ "log",
+ "rand",
+ "rustls",
+ "rustls-native-certs",
+ "sha1",
+ "thiserror",
+ "url",
+ "utf-8",
+ "webpki",
+]
+
+[[package]]
 name = "typenum"
 version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
 
 [[package]]
+name = "unicode-bidi"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
 
 [[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
 name = "unicode-width"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "url"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
 name = "version_check"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -563,6 +992,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
 
 [[package]]
+name = "web-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -592,3 +1041,84 @@ name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"

+ 7 - 0
Cargo.toml

@@ -9,3 +9,10 @@ members = [
 ]
 
 [dependencies]
+tungstenite = { version = "0.18.0", features = ["rustls", "rustls-native-certs", "rustls-tls-native-roots"]}
+nostr-protocol-types = { path = "crates/types" }
+tokio = "1.26.0"
+url = "2.3.1"
+env_logger = "0.10.0"
+serde_json = "1.0.94"
+

+ 3 - 1
crates/types/Cargo.toml

@@ -1,5 +1,5 @@
 [package]
-name = "nostr-protocol-types"
+name = "nostr-rs-types"
 version = "0.1.0"
 edition = "2021"
 
@@ -9,6 +9,8 @@ edition = "2021"
 base64 = "0.21.0"
 bech32 = "0.9.1"
 chrono = "0.4.23"
+custom_derive = "0.1.7"
+enum_derive = "0.1.7"
 hex = "0.4"
 rand = "0.8.5"
 secp256k1 = { version = "0.26.0", features = ["global-context"] }

+ 71 - 0
crates/types/src/client/close.rs

@@ -0,0 +1,71 @@
+//! Closes a subscription
+//!
+//! The client signals to the relayer to stop listening new events for the given
+//! subscription-ID
+use std::{collections::VecDeque, ops::Deref};
+
+use crate::{
+    common::SerializeDeserialize,
+    types::{subscription_id::Error, SubscriptionId},
+};
+use serde_json::Value;
+
+/// SubscriptionID to stop listening new events to
+#[derive(Clone, Debug)]
+pub struct Close(pub SubscriptionId);
+
+impl Deref for Close {
+    type Target = SubscriptionId;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl SerializeDeserialize for Close {
+    fn get_tag() -> &'static str {
+        "CLOSE"
+    }
+
+    fn serialize(&self) -> Result<Vec<Value>, String> {
+        Ok(vec![
+            Value::String(Self::get_tag().to_owned()),
+            Value::String((*self.0).to_owned()),
+        ])
+    }
+
+    fn deserialize(args: VecDeque<Value>) -> Result<Self, String> {
+        if args.is_empty() {
+            return Err("Invalid length".to_owned());
+        }
+        let subscription_id: SubscriptionId = args[0]
+            .as_str()
+            .ok_or_else(|| "Invalid message, expected string".to_owned())?
+            .try_into()
+            .map_err(|e: Error| e.to_string())?;
+        Ok(Self(subscription_id))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::Request;
+
+    #[test]
+    fn parse() {
+        let json = r#"["CLOSE", "test"]"#;
+        let message: Request = serde_json::from_str(json).expect("message");
+        assert!(message.as_close().is_some());
+    }
+
+    #[test]
+    fn serialize() {
+        let notice = Close("test".try_into().expect("v"));
+        let m: Request = notice.into();
+        assert_eq!(
+            r#"["CLOSE","test"]"#,
+            serde_json::to_string(&m).expect("valid json")
+        );
+    }
+}

+ 39 - 0
crates/types/src/client/event.rs

@@ -0,0 +1,39 @@
+//! An event
+//!
+//! The client publishes an events to the relayer
+use crate::{common::SerializeDeserialize, types};
+use std::{collections::VecDeque, ops::Deref};
+
+/// Publishes an event. It must be signed by a client's public key
+#[derive(Debug, Clone)]
+pub struct Event(pub types::Event);
+
+impl Deref for Event {
+    type Target = types::Event;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl SerializeDeserialize for Event {
+    fn get_tag() -> &'static str {
+        "EVENT"
+    }
+
+    fn serialize(&self) -> Result<Vec<serde_json::Value>, String> {
+        Ok(vec![
+            serde_json::Value::String(Self::get_tag().to_owned()),
+            serde_json::to_value(&self.0).map_err(|e| e.to_string())?,
+        ])
+    }
+
+    fn deserialize(mut args: VecDeque<serde_json::Value>) -> Result<Self, String>
+    where
+        Self: Sized,
+    {
+        Ok(Self(
+            serde_json::from_value(args.pop_front().ok_or("Invalid argument length")?)
+                .map_err(|e| e.to_string())?,
+        ))
+    }
+}

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

@@ -1,6 +1,10 @@
 //! # Nostr Protocol Types. Client side
 //!
-//! Messages that are going to be sent by a client
-pub mod request;
+//! Messages that are going to be sent by a client to a relayers are defined in
+//! this mod. All inner types are defined here and they can be converted to the
+//! top-level Message to be send to a relayer
+pub mod close;
+pub mod event;
+pub mod subscribe;
 
-pub use request::Request;
+pub use self::{close::Close, event::Event, subscribe::Subscribe};

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

@@ -1,69 +0,0 @@
-//! # 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>,
-}
-
-impl ser::Serialize for Request {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let mut seq = serializer.serialize_seq(Some(self.filters.len() + 2))?;
-        seq.serialize_element("REQ")?;
-        seq.serialize_element(&self.subscription_id)?;
-        for element in self.filters.iter() {
-            seq.serialize_element(element)?;
-        }
-        seq.end()
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::Message;
-    use chrono::Utc;
-
-    #[test]
-    fn encoding_decoding() {
-        let r = Request {
-            filters: vec![
-                types::Filter {
-                    authors: vec![
-                        "npub1h0h2zfcp3z7acvc9l0qvpgfqznkalj77whsdkmmlwanfssys3ngsx336a8"
-                            .try_into()
-                            .expect("some addr object"),
-                    ],
-                    until: Some(Utc::now()),
-                    limit: 30,
-                    ..Default::default()
-                },
-                types::Filter {
-                    limit: 20,
-                    ..Default::default()
-                },
-            ],
-            ..Default::default()
-        };
-
-        let serialized = serde_json::to_string(&r).expect("valid json string");
-        let obj: Message = serde_json::from_str(&serialized).expect("valid request");
-        let obj = obj.as_request().expect("request");
-        assert_eq!(r.subscription_id, obj.subscription_id);
-        assert_eq!(r.filters.len(), obj.filters.len());
-    }
-}

+ 112 - 0
crates/types/src/client/subscribe.rs

@@ -0,0 +1,112 @@
+//! # Nostr Protocol Types. Request
+//!
+//! Used to request events and subscribe to new updates.
+use crate::{common::SerializeDeserialize, types};
+use serde_json::Value;
+use std::collections::VecDeque;
+
+/// 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)]
+pub struct Subscribe {
+    /// 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>,
+}
+
+impl Default for Subscribe {
+    fn default() -> Self {
+        Self {
+            subscription_id: types::SubscriptionId::default(),
+            filters: vec![types::Filter::default()],
+        }
+    }
+}
+
+impl SerializeDeserialize for Subscribe {
+    fn get_tag() -> &'static str {
+        "REQ"
+    }
+
+    fn serialize(&self) -> Result<Vec<Value>, String> {
+        let mut r = vec![
+            Value::String(Self::get_tag().to_owned()),
+            Value::String((*self.subscription_id).to_owned()),
+        ];
+
+        for element in self.filters.iter() {
+            r.push(serde_json::to_value(element).map_err(|e| e.to_string())?);
+        }
+
+        Ok(r)
+    }
+
+    fn deserialize(mut array: VecDeque<Value>) -> Result<Self, String>
+    where
+        Self: Sized,
+    {
+        let subscription_id = array
+            .pop_front()
+            .ok_or("Missing element 1 in the array")?
+            .as_str()
+            .ok_or("Invalid type for element 1, expecting a string")?
+            .try_into()
+            .map_err(|e: types::subscription_id::Error| {
+                format!("Invalid subscription id: {}", e)
+            })?;
+
+        Ok(Subscribe {
+            subscription_id,
+            filters: if !array.is_empty() {
+                serde_json::from_value::<Vec<types::Filter>>(serde_json::Value::Array(
+                    array.into_iter().collect::<Vec<serde_json::Value>>(),
+                ))
+                .map_err(|e: serde_json::Error| e.to_string())?
+            } else {
+                vec![]
+            },
+        })
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::Request as ClientRequest;
+    use chrono::Utc;
+
+    #[test]
+    fn encoding_decoding() {
+        let r: ClientRequest = Subscribe {
+            filters: vec![
+                types::Filter {
+                    authors: vec![
+                        "npub1h0h2zfcp3z7acvc9l0qvpgfqznkalj77whsdkmmlwanfssys3ngsx336a8"
+                            .try_into()
+                            .expect("some addr object"),
+                    ],
+                    until: Some(Utc::now()),
+                    limit: 30,
+                    ..Default::default()
+                },
+                types::Filter {
+                    limit: 20,
+                    ..Default::default()
+                },
+            ],
+            ..Default::default()
+        }
+        .into();
+
+        let serialized = serde_json::to_string(&r).expect("valid json string");
+        let obj: ClientRequest = serde_json::from_str(&serialized).expect("valid request");
+        let obj = obj.as_request().expect("request");
+        let r = r.as_request().expect("request");
+        assert_eq!(r.subscription_id, obj.subscription_id);
+        assert_eq!(r.filters.len(), obj.filters.len());
+    }
+}

+ 21 - 0
crates/types/src/common.rs

@@ -0,0 +1,21 @@
+//! Common features shared between requests and responses
+//!
+use serde_json::Value;
+use std::collections::VecDeque;
+
+/// Serialize and Deserialize a message type
+///
+/// All messages inner types must implement this trait
+pub trait SerializeDeserialize {
+    /// The tag to identity the message by
+    fn get_tag() -> &'static str;
+
+    /// How to serialize into JSON. It must return a vector of serde_json::Value
+    fn serialize(&self) -> Result<Vec<Value>, String>;
+
+    /// How to deserialize, the first element (the tag) is already removed
+    /// before passing to this method
+    fn deserialize(array: VecDeque<Value>) -> Result<Self, String>
+    where
+        Self: Sized;
+}

+ 10 - 275
crates/types/src/lib.rs

@@ -1,283 +1,18 @@
 //! # 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 types::{Event, SubscriptionId};
+#![deny(missing_docs, warnings)]
 
 pub mod client;
+pub mod common;
+pub mod relayer;
+pub mod request;
+pub mod response;
 pub mod types;
 
-#[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),
-    /// 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),
-            _ => None,
-        }
-    }
-
-    /// 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),
-            _ => None,
-        }
-    }
-
-    /// 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::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),
-            _ => None,
-        }
-    }
-
-    /// Returns the current message as a notice, if possible
-    pub fn as_notice(&self) -> Option<&str> {
-        match self {
-            Self::Notice(x) => Some(x),
-            _ => None,
-        }
-    }
-
-    /// 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),
-            _ => None,
-        }
-    }
-}
-
-impl<'de> de::Deserialize<'de> for Message {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let array: Vec<serde_json::Value> = Deserialize::deserialize(deserializer)?;
-        if array.is_empty() {
-            return Err(de::Error::custom(
-                "Invalid array length, expecting at least one",
-            ));
-        }
-
-        let tag = array
-            .get(0)
-            .ok_or_else(|| de::Error::custom("Invalid array length, expecting at least one"))?
-            .as_str()
-            .ok_or_else(|| de::Error::custom("Invalid type for element 0 of the array"))?;
-
-        match tag {
-            "EOSE" => {
-                if array.len() != 2 {
-                    Err(de::Error::custom("Invalid length for EOSE"))
-                } else {
-                    Ok(Self::EndOfStoredEvents(
-                        array[1]
-                            .as_str()
-                            .ok_or_else(|| {
-                                de::Error::custom("Invalid subscription_id, expecting string")
-                            })?
-                            .to_owned(),
-                    ))
-                }
-            }
-            "EVENT" => match array.len() {
-                3 => {
-                    let subscription_id: SubscriptionId = array[1]
-                        .as_str()
-                        .map(TryFrom::try_from)
-                        .transpose()
-                        .map_err(|e: types::subscription_id::Error| {
-                            de::Error::custom(e.to_string())
-                        })?
-                        .ok_or_else(|| de::Error::custom("Invalid subscription ID"))?;
-
-                    let event: Event = serde_json::from_value(array[2].clone())
-                        .map_err(|e: serde_json::Error| de::Error::custom(e.to_string()))?;
-
-                    event
-                        .is_valid()
-                        .map_err(|e| de::Error::custom(e.to_string()))?;
-
-                    Ok(Self::EventFromRelayer(subscription_id, event))
-                }
-                2 => {
-                    let event: Event = serde_json::from_value(array[1].clone())
-                        .map_err(|e: serde_json::Error| de::Error::custom(e.to_string()))?;
-
-                    event
-                        .is_valid()
-                        .map_err(|e| de::Error::custom(e.to_string()))?;
-
-                    Ok(Self::EventFromClient(event))
-                }
-                _ => Err(de::Error::custom("Invalid length for EVENT")),
-            },
-
-            "NOTICE" => Ok(Self::Notice(
-                serde_json::from_value(
-                    array
-                        .get(1)
-                        .ok_or_else(|| de::Error::custom("Missing element 1 of the array"))?
-                        .clone(),
-                )
-                .map_err(|e: serde_json::Error| de::Error::custom(e.to_string()))?,
-            )),
-            "CLOSE" => Ok(Self::Close(
-                serde_json::from_value(
-                    array
-                        .get(1)
-                        .ok_or_else(|| de::Error::custom("Missing element 1 of the array"))?
-                        .clone(),
-                )
-                .map_err(|e: serde_json::Error| de::Error::custom(e.to_string()))?,
-            )),
-            "REQ" => {
-                let subscription_id = array
-                    .get(1)
-                    .ok_or_else(|| de::Error::custom("Missing element 1 in the array"))?
-                    .as_str()
-                    .ok_or_else(|| {
-                        de::Error::custom("Invalid type for element 1, expecting a string")
-                    })?
-                    .try_into()
-                    .map_err(|e: types::subscription_id::Error| {
-                        de::Error::custom(format!("Invalid subscription id: {}", e))
-                    })?;
-
-                Ok(Self::Request(client::Request {
-                    subscription_id,
-                    filters: if array.len() > 2 {
-                        serde_json::from_value::<Vec<types::Filter>>(serde_json::Value::Array(
-                            array[2..].to_owned(),
-                        ))
-                        .map_err(|e: serde_json::Error| de::Error::custom(e.to_string()))?
-                    } else {
-                        vec![]
-                    },
-                }))
-            }
-            tag => Err(de::Error::custom(format!("{} is not a support tag", tag))),
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::types::{content::EncryptedData, Content};
-
-    use super::*;
-
-    #[test]
-    fn unsupported() {
-        let json = "[\"CLOSEX\", \"foo\"]";
-        let message: Result<Message, _> = serde_json::from_str(json);
-        assert!(message.is_err());
-    }
-
-    #[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_relayer().is_some());
-
-        let (_, event) = message.as_event_from_relayer().expect("event");
-        assert_eq!(
-            "🤙".to_owned(),
-            event.data.content.try_to_string().expect("string")
-        );
-    }
-
-    #[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_relayer().is_some());
-
-        let (_, event) = message.as_event_from_relayer().expect("event");
-        assert_eq!(event.data.tags.len(), 14);
-        event
-            .data
-            .tags
-            .iter()
-            .map(|tag| {
-                tag.is_pubkey();
-            })
-            .for_each(drop);
-    }
-
-    #[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_relayer().is_some());
-
-        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![
-                92, 31, 242, 180, 184, 75, 209, 98, 144, 214, 135, 248, 193, 114, 23, 10,
-            ],
-        });
-        assert_eq!(event.data.tags.len(), 1);
-        assert_eq!(event.data.content, content);
-    }
-
-    #[test]
-    fn close() {
-        let json = "[\"CLOSE\", \"foo\"]";
-        let message: Message = serde_json::from_str(json).expect("valid message");
-        let subscription_id = message
-            .as_close_subscription_id()
-            .expect("valid subscription_id");
+pub use self::{request::Request, response::Response};
 
-        assert_eq!("foo".to_owned(), subscription_id.to_string());
-    }
-}
+#[macro_use]
+extern crate custom_derive;
+#[macro_use]
+extern crate enum_derive;

+ 73 - 0
crates/types/src/relayer/eose.rs

@@ -0,0 +1,73 @@
+//! End of Storage Events
+//!
+//! This is how to the relayer signals the client they gave all the stored
+//! messages with the requested filter. Future events will be sent, as they
+//! appear to this relayer
+use crate::{
+    common::SerializeDeserialize,
+    types::{subscription_id::Error, SubscriptionId},
+};
+use serde_json::Value;
+use std::{collections::VecDeque, ops::Deref};
+
+/// This is how to the relayer signals the client they gave all the stored
+/// messages with the requested filter. Future events will be sent, as they
+/// appear to this relayer
+#[derive(Clone, Debug)]
+pub struct EndOfStoredEvents(pub SubscriptionId);
+
+impl Deref for EndOfStoredEvents {
+    type Target = SubscriptionId;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl SerializeDeserialize for EndOfStoredEvents {
+    fn get_tag() -> &'static str {
+        "EOSE"
+    }
+
+    fn serialize(&self) -> Result<Vec<Value>, String> {
+        Ok(vec![
+            Value::String(Self::get_tag().to_owned()),
+            Value::String((*self.0).to_owned()),
+        ])
+    }
+
+    fn deserialize(args: VecDeque<Value>) -> Result<Self, String> {
+        if args.is_empty() {
+            return Err("Invalid length".to_owned());
+        }
+        let subscription_id: SubscriptionId = args[0]
+            .as_str()
+            .ok_or_else(|| "Invalid message, expected string".to_owned())?
+            .try_into()
+            .map_err(|e: Error| e.to_string())?;
+        Ok(EndOfStoredEvents(subscription_id))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::Response;
+
+    #[test]
+    fn parse() {
+        let json = r#"["EOSE", "test"]"#;
+        let message: Response = serde_json::from_str(json).expect("message");
+        assert!(message.as_end_of_stored_events().is_some());
+    }
+
+    #[test]
+    fn serialize() {
+        let notice = EndOfStoredEvents("test".try_into().expect("v"));
+        let m: Response = notice.into();
+        assert_eq!(
+            r#"["EOSE","test"]"#,
+            serde_json::to_string(&m).expect("valid json")
+        );
+    }
+}

+ 74 - 0
crates/types/src/relayer/event.rs

@@ -0,0 +1,74 @@
+//! Events sent by the relayer to client
+use crate::{
+    common::SerializeDeserialize,
+    types::{self, subscription_id::Error, SubscriptionId},
+};
+use serde_json::Value;
+use std::collections::VecDeque;
+
+/// An event sent to clients
+#[derive(Clone, Debug)]
+pub struct Event {
+    /// The subscription ID that matched the inner event
+    pub subscription_id: SubscriptionId,
+    /// The inner event
+    pub event: types::Event,
+}
+
+impl SerializeDeserialize for Event {
+    fn get_tag() -> &'static str {
+        "EVENT"
+    }
+
+    fn serialize(&self) -> Result<Vec<Value>, String> {
+        Ok(vec![
+            Value::String(Self::get_tag().to_owned()),
+            Value::String((*(self.subscription_id)).to_owned()),
+            serde_json::to_value(&self.event).map_err(|e| e.to_string())?,
+        ])
+    }
+
+    fn deserialize(mut args: VecDeque<Value>) -> Result<Self, String> {
+        if args.len() != 2 {
+            return Err("Invalid length".to_owned());
+        }
+        let subscription_id: SubscriptionId = args
+            .pop_front()
+            .ok_or_else(|| "Invalid array length".to_owned())?
+            .as_str()
+            .ok_or_else(|| "Invalid message, expected string".to_owned())?
+            .try_into()
+            .map_err(|e: Error| e.to_string())?;
+
+        let event: types::Event =
+            serde_json::from_value(args.pop_front().ok_or("Invalid array length")?)
+                .map_err(|e| e.to_string())?;
+
+        Ok(Self {
+            subscription_id,
+            event,
+        })
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::Response;
+
+    #[test]
+    fn parse() {
+        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: Response = serde_json::from_str(json).expect("message");
+        assert!(message.as_event().is_some());
+    }
+
+    #[test]
+    fn serialize() {
+        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: Response = serde_json::from_str(json).expect("message");
+        let relayer = message.as_event().expect("relayer").to_owned();
+        let m: Response = relayer.into();
+
+        assert_eq!(json, serde_json::to_string(&m).expect("valid json"));
+    }
+}

+ 10 - 0
crates/types/src/relayer/mod.rs

@@ -0,0 +1,10 @@
+//! Relayer inner message
+//!
+//! This mod has all the messages that relayers may send to clients
+
+pub mod eose;
+pub mod event;
+pub mod notice;
+pub mod ok;
+
+pub use self::{eose::EndOfStoredEvents, event::Event, notice::Notice, ok::ROk};

+ 67 - 0
crates/types/src/relayer/notice.rs

@@ -0,0 +1,67 @@
+//! Notices
+//!
+//! Notices are the way relayers send errors with their description to clients
+//!
+//! This may happen when client makes mistakes at the protocol level
+use crate::common::SerializeDeserialize;
+use serde_json::Value;
+use std::{collections::VecDeque, ops::Deref};
+
+/// Notices are errors
+#[derive(Clone, Debug)]
+pub struct Notice(pub String);
+
+impl Deref for Notice {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl SerializeDeserialize for Notice {
+    fn get_tag() -> &'static str {
+        "NOTICE"
+    }
+
+    fn serialize(&self) -> Result<Vec<Value>, String> {
+        Ok(vec![
+            Value::String(Self::get_tag().to_owned()),
+            Value::String(self.0.to_owned()),
+        ])
+    }
+
+    fn deserialize(args: VecDeque<Value>) -> Result<Self, String> {
+        if args.is_empty() {
+            return Err("Invalid length".to_owned());
+        }
+        let message = args[0]
+            .as_str()
+            .ok_or_else(|| "Invalid message, expected string".to_owned())?
+            .to_owned();
+        Ok(Notice(message))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::Response;
+
+    #[test]
+    fn parse() {
+        let json = r#"["NOTICE", "test"]"#;
+        let message: Response = serde_json::from_str(json).expect("message");
+        assert!(message.as_notice().is_some());
+    }
+
+    #[test]
+    fn serialize() {
+        let notice = Notice("test".to_owned());
+        let m: Response = notice.into();
+        assert_eq!(
+            r#"["NOTICE","test"]"#,
+            serde_json::to_string(&m).expect("valid json")
+        );
+    }
+}

+ 86 - 0
crates/types/src/relayer/ok.rs

@@ -0,0 +1,86 @@
+//! OK
+//!
+//! When submitting events to relays, clients currently have no way to know if
+//! an event was successfully committed to the database. This NIP introduces the
+//! concept of command results which are like NOTICE's except provide more
+//! information about if an event was accepted or rejected.
+use crate::{
+    common::SerializeDeserialize,
+    types::{self, Addr},
+};
+use serde_json::Value;
+use std::collections::VecDeque;
+
+/// OK messages
+#[derive(Clone, Debug)]
+pub struct ROk {
+    /// Event Id
+    pub id: Addr,
+    /// Whether the event has been successful or not
+    pub status: bool,
+    /// Some message
+    pub message: String,
+}
+
+impl SerializeDeserialize for ROk {
+    fn get_tag() -> &'static str {
+        "OK"
+    }
+
+    fn serialize(&self) -> Result<Vec<Value>, String> {
+        Ok(vec![
+            Value::String(Self::get_tag().to_owned()),
+            Value::String(self.id.to_hex()),
+            Value::Bool(self.status),
+            Value::String(self.message.clone()),
+        ])
+    }
+
+    fn deserialize(args: VecDeque<Value>) -> Result<Self, String> {
+        if args.len() < 3 {
+            return Err("Invalid length".to_owned());
+        }
+        let id = args[0]
+            .as_str()
+            .ok_or_else(|| "Invalid type for element 1, expecting a string".to_owned())?
+            .try_into()
+            .map_err(|e: types::addr::Error| format!("Invalid id: {}", e))?;
+        let status = args[1].as_bool().ok_or("Invalid type, expected bool")?;
+        let message = args[2]
+            .as_str()
+            .ok_or_else(|| "Invalid message, expected string".to_owned())?
+            .to_owned();
+        Ok(Self {
+            id,
+            status,
+            message,
+        })
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::Response;
+
+    #[test]
+    fn parse() {
+        let json = r#"["OK", "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30", true, "pow: difficulty 25>=24"]"#;
+        let message: Response = serde_json::from_str(json).expect("message");
+        assert!(message.as_ok().is_some());
+    }
+
+    #[test]
+    fn serialize() {
+        let ok_ = ROk {
+            id: "a0b0".try_into().expect("valid_id"),
+            status: true,
+            message: "Some test".into(),
+        };
+        let m: Response = ok_.into();
+        assert_eq!(
+            r#"["OK","a0b0",true,"Some test"]"#,
+            serde_json::to_string(&m).expect("valid json")
+        );
+    }
+}

+ 129 - 0
crates/types/src/request.rs

@@ -0,0 +1,129 @@
+//! Request Message
+//!
+//! All messages that clients can send to relayers are abstracted in this mod
+use crate::{client, common::SerializeDeserialize, types};
+use serde::{
+    de::{self, Deserializer},
+    ser::{self, SerializeSeq, Serializer},
+    Deserialize,
+};
+use std::collections::VecDeque;
+
+custom_derive! {
+#[derive(Debug, Clone, EnumFromInner)]
+    /// Request Messages
+    ///
+    /// All requests from clients to relayer are abstracted in this struct
+    pub enum Request {
+        /// Close a subscription
+        Close(client::Close),
+        /// An Event from a client.  This is when a client wants to publish an
+        /// event. The event must be signed.
+        Event(client::Event),
+        /// Creates a subscription. Client tells the relayer which kind of events
+        /// they'd like to hear about
+        Request(client::Subscribe),
+    }
+}
+
+impl Request {
+    /// Returns the current message as a Request, if possible
+    pub fn as_request(&self) -> Option<&client::Subscribe> {
+        match self {
+            Self::Request(x) => Some(x),
+            _ => None,
+        }
+    }
+
+    /// Returns the current message as a close if possible
+    pub fn as_close(&self) -> Option<&client::Close> {
+        match self {
+            Self::Close(t) => Some(t),
+            _ => None,
+        }
+    }
+
+    /// Returns the current message as an event from the client, if possible
+    pub fn as_event(&self) -> Option<&types::Event> {
+        match self {
+            Self::Event(event) => Some(&**event),
+            _ => None,
+        }
+    }
+}
+
+impl ser::Serialize for Request {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let values = match self {
+            Self::Close(t) => t.serialize(),
+            Self::Event(t) => t.serialize(),
+            Self::Request(t) => t.serialize(),
+        }
+        .map_err(ser::Error::custom)?;
+
+        let mut seq = serializer.serialize_seq(Some(values.len()))?;
+        for value in values.iter() {
+            seq.serialize_element(value)?;
+        }
+        seq.end()
+    }
+}
+
+impl<'de> de::Deserialize<'de> for Request {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let mut array: VecDeque<serde_json::Value> = Deserialize::deserialize(deserializer)?;
+        if array.is_empty() {
+            return Err(de::Error::custom(
+                "Invalid array length, expecting at least one",
+            ));
+        }
+
+        let tag = array
+            .remove(0)
+            .ok_or_else(|| de::Error::custom("Invalid array length, expecting at least one"))?;
+
+        let tag = tag
+            .as_str()
+            .ok_or_else(|| de::Error::custom("Invalid type for element 0 of the array"))?;
+
+        match tag {
+            "EVENT" => Ok(client::Event::deserialize(array)
+                .map_err(de::Error::custom)?
+                .into()),
+            "CLOSE" => Ok(client::Close::deserialize(array)
+                .map_err(de::Error::custom)?
+                .into()),
+            "REQ" => Ok(client::Subscribe::deserialize(array)
+                .map_err(de::Error::custom)?
+                .into()),
+            tag => Err(de::Error::custom(format!("{} is not a support tag", tag))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn unsupported() {
+        let json = "[\"CLOSEX\", \"foo\"]";
+        let message: Result<Request, _> = serde_json::from_str(json);
+        assert!(message.is_err());
+    }
+
+    #[test]
+    fn close() {
+        let json = "[\"CLOSE\", \"foo\"]";
+        let message: Request = serde_json::from_str(json).expect("valid message");
+        let subscription_id = message.as_close().expect("valid subscription_id");
+
+        assert_eq!("foo".to_owned(), subscription_id.to_string());
+    }
+}

File diff suppressed because it is too large
+ 200 - 0
crates/types/src/response.rs


+ 16 - 4
crates/types/src/types/addr.rs

@@ -67,14 +67,18 @@ pub struct Addr {
 }
 
 impl Addr {
+    /// Instantiates a new Addr from a hex string with an optional human
+    /// readable part
     pub fn try_from_public_key_str(
-        hrp: Option<HumanReadablePart>,
         pk: &str,
+        hrp: Option<HumanReadablePart>,
     ) -> Result<Self, Error> {
-        Addr::try_from_bytes(hrp, hex::decode(pk)?)
+        Addr::try_from_bytes(hex::decode(pk)?, hrp)
     }
 
-    pub fn try_from_bytes(hrp: Option<HumanReadablePart>, bytes: Vec<u8>) -> Result<Self, Error> {
+    /// Instantiates a new Addr from a vector of bytes with an optional human
+    /// readable part
+    pub fn try_from_bytes(bytes: Vec<u8>, hrp: Option<HumanReadablePart>) -> Result<Self, Error> {
         if bytes.len() > 32 {
             return Err(Error::TooLong(bytes.len()));
         }
@@ -82,6 +86,7 @@ impl Addr {
         Ok(Addr { bytes, hrp })
     }
 
+    /// Creates a new Addr with another human readable part
     pub fn change_type(self, new_hrp: Option<HumanReadablePart>) -> Self {
         Self {
             bytes: self.bytes,
@@ -89,10 +94,12 @@ impl Addr {
         }
     }
 
+    /// Returns the Addr to a string of hex numbers
     pub fn to_hex(&self) -> String {
         hex::encode(&self.bytes)
     }
 
+    /// Attempts to create a human address, which is bench32 encoded
     pub fn try_to_human(&self) -> Result<String, Error> {
         Ok(bech32::encode(
             self.hrp.map(|t| t.to_string()).unwrap_or_default().as_str(),
@@ -102,6 +109,11 @@ impl Addr {
     }
 }
 
+/// Converts an Addr from an string
+///
+/// The first approach is try decoding the the Addr as a hex-encoded string.
+///
+/// The second approach is decoding with bech32
 impl TryFrom<String> for Addr {
     type Error = Error;
 
@@ -175,8 +187,8 @@ mod test {
     #[test]
     fn deserializes_from_public_key_and_json() {
         let addr1 = Addr::try_from_public_key_str(
-            Some(HumanReadablePart::NPub),
             "b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78",
+            Some(HumanReadablePart::NPub),
         )
         .expect("valid addr");
         assert_eq!(

+ 60 - 25
crates/types/src/types/content/mod.rs

@@ -1,70 +1,96 @@
+//! Content
+//!
+//! The content encoding logic is defined in this mod. The encoding is
+//! determined by the event kind.
+use super::Kind;
 use base64::{engine::general_purpose, Engine as _};
-use serde::{
-    ser::{self, Serializer},
-    Deserialize, Serialize,
-};
+use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 use thiserror::Error;
 
-use super::Kind;
-
 pub mod profile;
 
+/// Common errors
 #[derive(Error, Debug)]
 pub enum Error {
-    #[error("Issue at the serializer: {0}")]
+    /// A JSON encoding/decoding error
+    #[error("JSON: {0}")]
     Json(#[from] serde_json::Error),
 
+    /// A Base64 encoding/decoding error
     #[error("Base64: {0}")]
     Base64(#[from] base64::DecodeError),
 }
 
+/// Relay settings
 #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
 pub struct RelaySettings {
+    /// Can this relay be used to read/subscribe to updates?
     pub read: bool,
+    /// Can this relay be used to publish new events
     pub write: bool,
 }
 
+/// Encrypted message
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub struct EncryptedData {
+    /// Encrypted data, in raw bytes
     pub encrypted_message: Vec<u8>,
+    /// The Initialization vector for the encrypted message
     pub iv: Vec<u8>,
 }
 
+/// Event content
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub enum Content {
+    /// Metadata
+    ///
+    /// The metadata is the profile information
     Metadata(profile::Profile),
+    /// Short text notes
     ShortTextNote(String),
+    /// Recommend relayers where a given public key or event publishes their content
     RecommendRelayer(String),
+    /// List of contacts a public key has
+    ///
+    /// This can be interpreted as the list of accounts / public key a given
+    /// account follows
     Contacts(HashMap<String, RelaySettings>),
+    /// Encrypted messages
+    ///
+    /// The encrypted messages are encrypted with the user's public key. They
+    /// can only be decrypted with the counterpart private key
     EncryptedDirectMessage(EncryptedData),
+    //// An event that should be removed.
+    ///
+    /// A client can request relayers to remove their own contents. A relayer
+    /// may comply or not
+    EventDeletion,
+    /// Reposting content from another public key
+    Repost,
+    /// React to another event
+    Reaction(String),
+    /// Unknown event
     Unknown(Kind, String),
 }
 
-impl ser::Serialize for Content {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.serialize_str(
-            &self
-                .try_to_string()
-                .map_err(|e| ser::Error::custom(e.to_string()))?,
-        )
-    }
-}
-
 impl Content {
+    /// Returns the Kind for self
     pub fn kind(&self) -> Kind {
         self.into()
     }
 
+    /// Deserializes the content with a give kind
     pub fn deserialize(kind: Kind, content: &str) -> Result<Self, Error> {
         match kind {
-            Kind::Metadata => Ok(Self::Metadata(serde_json::from_str(content)?)),
+            Kind::Metadata => Ok(Self::Metadata(
+                serde_json::from_str(content).unwrap_or_default(),
+            )),
             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::Contacts => Ok(Self::Contacts(
+                serde_json::from_str(content).unwrap_or_default(),
+            )),
             Kind::EncryptedDirectMessages => {
                 let parts = content.split("?iv=").collect::<Vec<&str>>();
                 if parts.len() == 2 {
@@ -79,10 +105,17 @@ impl Content {
                     Ok(Self::Unknown(Kind::Unknown(4), content.to_owned()))
                 }
             }
+            Kind::EventDeletion => Ok(Self::EventDeletion),
+            Kind::Repost => Ok(Self::Repost),
+            Kind::Reaction => Ok(Self::Reaction(content.to_owned())),
             kind => Ok(Self::Unknown(kind, content.to_owned())),
         }
     }
 
+    /// Serializes the content to string
+    ///
+    /// This should be used only when raw_content is not available. This should be
+    /// used once, before signing to populate raw_content in the Event struct
     pub fn try_to_string(&self) -> Result<String, Error> {
         match self {
             Self::Metadata(p) => Ok(serde_json::to_string(&p)?),
@@ -94,9 +127,11 @@ impl Content {
                 let to_encode = format!("{}?iv={}", encrypted_message, iv);
                 Ok(serde_json::to_string(&to_encode)?)
             }
-            Self::ShortTextNote(t) | Self::RecommendRelayer(t) | Self::Unknown(_, t) => {
-                Ok(t.to_owned())
-            }
+            Self::ShortTextNote(t)
+            | Self::RecommendRelayer(t)
+            | Self::Unknown(_, t)
+            | Self::Reaction(t) => Ok(t.to_owned()),
+            Self::EventDeletion | Self::Repost => Ok("".to_owned()),
         }
     }
 }

+ 21 - 7
crates/types/src/types/content/profile.rs

@@ -1,19 +1,33 @@
+//! Profile
+//!
+//! The profile are defined in the NIP-01 and NIP-05. The content, as many
+//! others, is encoded as JSON. Because the events are encoded as JSON, the
+//! content is a string, not a nested object, but instead a double encoding JSON
+//! object
 use serde::{Deserialize, Serialize};
 
-#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
+/// Profile as defined as NIP-01. NIP-05. Most properties are optional
+#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Default)]
 pub struct Profile {
+    /// Displaying name
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub lud06: Option<String>,
+    pub display_name: Option<String>,
+    /// User biography
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub website: Option<String>,
+    pub about: Option<String>,
+    /// User name
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub nip05: Option<String>,
+    pub name: Option<String>,
+    /// Picture. It must be an external
     #[serde(skip_serializing_if = "Option::is_none")]
     pub picture: Option<String>,
+    /// Lightning payment address
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub display_name: Option<String>,
+    pub lud06: Option<String>,
+    /// Website definition
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub about: Option<String>,
+    pub website: Option<String>,
+    /// Where the NIP-05
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub name: Option<String>,
+    pub nip05: Option<String>,
 }

+ 57 - 9
crates/types/src/types/event.rs

@@ -1,3 +1,7 @@
+//! Event type
+//!
+//! This is the core type of the protocol. All events must be signed to be valid
+//! and broadcasted by relayers
 use super::{content, Content, Id, Kind, Signature};
 use chrono::{DateTime, Utc};
 use secp256k1::{schnorr, Message, XOnlyPublicKey};
@@ -9,39 +13,61 @@ use serde_json::json;
 use sha2::{Digest, Sha256};
 use thiserror::Error;
 
+/// Errors
 #[derive(Error, Debug)]
 pub enum Error {
+    /// JSON serialization errors
     #[error("Error serializing object: {0}")]
     Serialize(#[from] serde_json::Error),
 
+    /// Error serializing an ID
     #[error("Error serializing id: {0}")]
     Id(#[from] hex::FromHexError),
 
+    /// Error in the signature
     #[error("Signature error: {0}")]
     Signature(#[from] secp256k1::Error),
 
+    /// The provided ID is not valid, the ID is the hash of the content, as
+    /// described in NIP-01
     #[error("Provided ID is not correct")]
     InvalidProvidedId,
 
+    /// Internal Error
     #[error("Internal error: {0}")]
     Internal(#[from] content::Error),
 }
 
+/// This is the unsigned event.
+///
+/// The event must be signed, in the crate::types::Event, which wraps this
+/// struct, but all the content of the event is defined here, but the signature.
 #[derive(Debug, Clone, Serialize)]
-pub struct Data {
+pub struct UnsignedEvent {
+    /// The public key that signs this event
     #[serde(rename = "pubkey")]
     pub public_key: Id,
+    /// A timestamp when this event has been created
     #[serde(with = "super::ts_seconds")]
     pub created_at: DateTime<Utc>,
+    /// Content kind
     pub kind: Kind,
+    /// Tags
     pub tags: Vec<super::Tag>,
     #[serde(skip_serializing)]
+    /// Content
+    ///
+    /// This is the parsed version of the raw_content. This is meant to be used
+    /// at rust's level to read the content easily.
     pub content: Content,
+    /// Raw content
+    ///
+    /// The raw content that is serialized into the wire and signed.
     #[serde(rename = "content")]
     pub raw_content: String,
 }
 
-impl<'de> de::Deserialize<'de> for Data {
+impl<'de> de::Deserialize<'de> for UnsignedEvent {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
@@ -70,7 +96,8 @@ impl<'de> de::Deserialize<'de> for Data {
     }
 }
 
-impl Data {
+impl UnsignedEvent {
+    /// Creates a new unsigned event
     pub fn new(
         public_key: Id,
         tags: Vec<super::Tag>,
@@ -87,6 +114,9 @@ impl Data {
         })
     }
 
+    /// Calculates the ID for this unsigned event
+    ///
+    /// This ID is then signed by the public key's counter part private key.
     pub fn id(&self) -> Result<Id, Error> {
         let data_to_hash = serde_json::to_string(&json!(vec![
             json!(0),
@@ -96,42 +126,60 @@ impl Data {
             json!(self.tags),
             json!(self.raw_content),
         ]))?;
-        println!("{:?}", self.tags);
         let mut hasher = Sha256::new();
         hasher.update(&data_to_hash);
         Ok(Id(hasher.finalize().into()))
     }
 }
 
+/// Signed event
+///
+/// This is a wrap for UnsignedEvent but it will serializes the ID and the signature.
+///
+/// This is the struct that is published to relayers from clients
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct Event {
+    /// Event Id
+    ///
+    /// This is calculated from the UnsignedEvent and later is used to be
+    /// signed. Any minor alteration to the UnsignedEvent will generate a
+    /// totally different Id and Signature, making the whole event invalid
     pub id: Id,
+    /// Event content
     #[serde(flatten)]
-    pub data: Data,
+    pub inner: UnsignedEvent,
+    /// The signature that makes the inner unsigned event's ID
     #[serde(rename = "sig")]
     pub signature: Signature,
 }
 
 impl Event {
-    pub fn new(data: Data, signature: Signature) -> Result<Self, Error> {
+    /// Creates a new event
+    ///
+    /// At this stage user must provide the signature as well.
+    ///
+    /// TODO: Create a new function where the user provides the private key instead
+    pub fn new(data: UnsignedEvent, signature: Signature) -> Result<Self, Error> {
         let id = data.id()?;
         Self::verify_signature(&data.public_key, &signature, &id)?;
 
         Ok(Self {
             id,
-            data,
+            inner: data,
             signature,
         })
     }
 
+    /// Checks if the event is valid
     pub fn is_valid(&self) -> Result<(), Error> {
-        let calculated_id = self.data.id()?;
+        let calculated_id = self.inner.id()?;
         if calculated_id != self.id {
             return Err(Error::InvalidProvidedId);
         }
-        Self::verify_signature(&self.data.public_key, &self.signature, &self.id)
+        Self::verify_signature(&self.inner.public_key, &self.signature, &self.id)
     }
 
+    /// Verifies if the Id, Public Key and Signature are correct
     fn verify_signature(
         public_key: &Id,
         signature: &Signature,

+ 20 - 1
crates/types/src/types/filter.rs

@@ -1,30 +1,49 @@
+//! Filters
+//!
+//! This is used for clients to requests for events and subscribe to new events.
+//!
+//! Each property is treated as an AND, but a vector of filters can be provided,
+//! and they will be treated as OR.
 use chrono::{DateTime, Utc};
 use serde::{Deserialize, Serialize};
 
+use super::Kind;
+
+/// Filter
 #[derive(Serialize, Deserialize, Default, Debug, Clone)]
 pub struct Filter {
+    /// Fetch events by their Id, or subscribe
+    ///
+    /// A full Id can be provided, or a prefix.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub ids: Vec<super::Addr>,
+    /// Fetch events by a public key, or a prefix
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub authors: Vec<super::Addr>,
+    /// Fetch events by their kinds
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
-    pub kinds: Vec<u32>,
+    pub kinds: Vec<Kind>,
+    /// Fetch events that has tag references to a given event id
     #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "#e")]
     pub references_to_event: Vec<super::Addr>,
+    /// Fetches events that has a tag to a public key
     #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "#p")]
     pub references_to_public_key: Vec<super::Addr>,
+    /// Fetch events newer to this given timestamp
     #[serde(
         default,
         with = "super::option_ts_seconds",
         skip_serializing_if = "Option::is_none"
     )]
     pub since: Option<DateTime<Utc>>,
+    /// Fetch events older to this timestamp
     #[serde(
         default,
         with = "super::option_ts_seconds",
         skip_serializing_if = "Option::is_none"
     )]
     pub until: Option<DateTime<Utc>>,
+    /// Has a limit to fetch stored events
     #[serde(default, skip_serializing_if = "is_zero")]
     pub limit: u64,
 }

+ 4 - 0
crates/types/src/types/id.rs

@@ -1,3 +1,4 @@
+//! This mod wraps the event Ids
 use serde::{
     de::{self, Deserializer},
     ser::{self, Serializer},
@@ -5,6 +6,9 @@ use serde::{
 };
 use std::ops::Deref;
 
+/// Event Id
+///
+/// Event Id are raw 32 bytes and 64-character length hex encoded to JSON
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Id(pub [u8; 32]);
 

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

@@ -1,3 +1,6 @@
+//! Content Kind
+//!
+//! This is how the client should process the content.
 use super::Content;
 use serde::{
     de::{self, Deserializer},
@@ -5,16 +8,58 @@ use serde::{
     Deserialize,
 };
 
+/// Content Kind
+///
+/// Any unsupported Kind will be wrapped under the Unknown type
+///
+/// The Kind is represented as a u32 on the wire
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum Kind {
+    /// Metadata
+    ///
+    /// The metadata is the profile information
     Metadata,
+    /// Short text notes
     ShortTextNote,
+    /// Recommend relayers where a given public key or event publishes their content
     RecommendRelayer,
+    /// List of contacts a public key has
+    ///
+    /// This can be interpreted as the list of accounts / public key a given
+    /// account follows
     Contacts,
+    /// Encrypted messages
+    ///
+    /// The encrypted messages are encrypted with the user's public key. They
+    /// can only be decrypted with the counterpart private key
     EncryptedDirectMessages,
+    //// An event that should be removed.
+    ///
+    /// A client can request relayers to remove their own contents. A relayer
+    /// may comply or not
+    EventDeletion,
+    /// Reposting content from another public key
+    Repost,
+    /// React to another event
+    Reaction,
+    /// Unknown Kind
     Unknown(u32),
 }
 
+impl Kind {
+    /// Is the Kind replaceable according to NIP-16
+    pub fn is_replaceable(&self) -> bool {
+        let kind_id: u32 = (*self).into();
+        (10_000..=19_999).contains(&kind_id)
+    }
+
+    /// Is the Kind ephemeral according to NIP-16
+    pub fn is_ephemeral(&self) -> bool {
+        let kind_id: u32 = (*self).into();
+        (20_000..=29_999).contains(&kind_id)
+    }
+}
+
 impl From<Kind> for u32 {
     fn from(kind: Kind) -> Self {
         match kind {
@@ -23,6 +68,9 @@ impl From<Kind> for u32 {
             Kind::RecommendRelayer => 2,
             Kind::Contacts => 3,
             Kind::EncryptedDirectMessages => 4,
+            Kind::EventDeletion => 5,
+            Kind::Repost => 6,
+            Kind::Reaction => 7,
             Kind::Unknown(t) => t,
         }
     }
@@ -36,6 +84,9 @@ impl From<u32> for Kind {
             2 => Kind::RecommendRelayer,
             3 => Kind::Contacts,
             4 => Kind::EncryptedDirectMessages,
+            5 => Kind::EventDeletion,
+            6 => Kind::Repost,
+            7 => Kind::Reaction,
             any => Kind::Unknown(any),
         }
     }
@@ -68,7 +119,92 @@ impl From<&Content> for Kind {
             Content::RecommendRelayer(_) => Kind::RecommendRelayer,
             Content::Contacts(_) => Kind::Contacts,
             Content::EncryptedDirectMessage(_) => Kind::EncryptedDirectMessages,
+            Content::EventDeletion => Kind::EventDeletion,
+            Content::Repost => Kind::Repost,
+            Content::Reaction(_) => Kind::Reaction,
             Content::Unknown(kind, _) => *kind,
         }
     }
 }
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        types::{Content, Kind},
+        Response,
+    };
+
+    #[test]
+    fn deletion() {
+        let json = r#"["EVENT","6414a63d7ddf1",{"content":"","created_at":1679052792,"id":"2f9b354e8ea7b28e1e33c585ceb19e0b6838e3580f2741fe5af6cbe0b6146566","kind":5,"pubkey":"bd4bdec9641883c492ca0062bb4d6aa8c756385980ec3e7fe2217b03184985c6","sig":"faa1c74d050f3d941049cdfb6c4e6865b90c11ce591fc2150d53be2acfc92e085dfe451d1bbcff3c88079034def131394b37448baa0a46c848017f09a00305a3","tags":[["e","fe722046568a80af561b23c27a381514575f028c908b428dd8ac065db8fcb9eb"]]}]"#;
+
+        let msg: Response = serde_json::from_str(json).expect("message");
+        assert_eq!(
+            Kind::EventDeletion,
+            msg.as_event().expect("event").event.inner.kind
+        );
+    }
+
+    #[test]
+    fn deletion_content_to_kind() {
+        let content = Content::EventDeletion;
+        let kind: Kind = (&content).into();
+        assert_eq!(kind, Kind::EventDeletion);
+    }
+
+    #[test]
+    fn reaction() {
+        let json = r#"["EVENT","6414a69109d48",{"content":"🤙","created_at":1679074282,"id":"303eb43cea76695f11d3ceae9e1e89797034d04bbad4b922099c0c4087f30c58","kind":7,"pubkey":"e64b77a57752e98e2fa23d16773a182c46d92108a30f85e445604804d8543526","sig":"b6f2f26a2eecf13c077185f32cbfb73e3916484ef5745fdf37bd1025784935db18345b39048470bab0448cd52bcc8c7c2c140f895aa38ec1d17f1350e9d328d9","tags":[["e","5c24cac9fcdcb894d18c33c059dcc0eba7a5124387d1dbb6e03d2dd3411c4d40"],["p","bd8aed58fee47e64520ec250b70efd264b496bc3f8c2563c26c0b843cb1cdb53"]]}]"#;
+
+        let msg: Response = serde_json::from_str(json).expect("message");
+        assert_eq!(
+            Kind::Reaction,
+            msg.as_event().expect("event").event.inner.kind
+        );
+    }
+
+    #[test]
+    fn reaction_content_to_kind() {
+        let content = Content::Reaction("🤙".to_owned());
+        let kind: Kind = (&content).into();
+        assert_eq!(kind, Kind::Reaction);
+    }
+
+    #[test]
+    fn repost() {
+        let json = r#"["EVENT","6414a4343e86b",{"content":"","created_at":1679068882,"id":"a08ec930d95860f0ffe53d95887ed30334562e31d137db0377828c1c1827245d","kind":6,"pubkey":"13bf6b8de4d3b8b0103529b4753c6dece8cc2cddc34a682dc6a8dbe6ab317876","sig":"2d1e4b83e2c0a498f14555e9bfbd2516294062079403bdda4db656d0edd57b428e948becb50ce044f28006899eb2fc444ce2e4e04bef0247f863bbabd26f87fe","tags":[["e","2aa7af268b11942c24016333a590aee738cf5bf28e4602421178bfb08759b13a"],["p","13bf6b8de4d3b8b0103529b4753c6dece8cc2cddc34a682dc6a8dbe6ab317876"]]}]"#;
+
+        let msg: Response = serde_json::from_str(json).expect("message");
+        assert_eq!(
+            Kind::Repost,
+            msg.as_event().expect("event").event.inner.kind
+        );
+    }
+
+    #[test]
+    fn repost_content_to_kind() {
+        let content = Content::Repost;
+        let kind: Kind = (&content).into();
+        assert_eq!(kind, Kind::Repost);
+    }
+
+    #[test]
+    fn is_replaceable() {
+        let repleaceable: Kind = 15005.into();
+        let not_repleaceable: Kind = 1.into();
+        let not_repleaceable_2: Kind = 20101.into();
+        assert!(repleaceable.is_replaceable());
+        assert!(!not_repleaceable.is_replaceable());
+        assert!(!not_repleaceable_2.is_replaceable());
+    }
+
+    #[test]
+    fn is_ephemeral() {
+        let ephemeral: Kind = 25005.into();
+        let not_ephemeral: Kind = 1.into();
+        let not_ephemeral_2: Kind = 10101.into();
+        assert!(ephemeral.is_ephemeral());
+        assert!(!not_ephemeral.is_ephemeral());
+        assert!(!not_ephemeral_2.is_ephemeral());
+    }
+}

+ 1 - 1
crates/types/src/types/mod.rs

@@ -76,7 +76,7 @@ pub(crate) mod ts_seconds {
 pub use self::{
     addr::Addr,
     content::Content,
-    event::{Data, Event},
+    event::{Event, UnsignedEvent},
     filter::Filter,
     id::Id,
     kind::Kind,

+ 8 - 2
crates/types/src/types/signature.rs

@@ -1,20 +1,26 @@
-use std::ops::Deref;
-
+//! Event signature
 use serde::{
     de::{self, Deserialize, Deserializer},
     ser::{self, Serializer},
 };
+use std::ops::Deref;
 use thiserror::Error;
 
+/// Errors
 #[derive(Error, Debug)]
 pub enum Error {
+    /// Invalid JSON encoding
+    ///
+    /// It must be encoded as a hex string with length 128
     #[error("Error decoding: {0}")]
     DecodingError(#[from] hex::FromHexError),
 
+    /// Invalid size of the signature
     #[error("Expecting a 64 bytes signature")]
     InvalidSize,
 }
 
+/// Wraps the signature as a 64-bytes vector of bytes
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Signature(pub [u8; 64]);
 

+ 17 - 29
crates/types/src/types/subscription_id.rs

@@ -1,31 +1,32 @@
+//! Subscription ID
+//!
+//! This mod abstract the subscription and makes sure it is valid
 use hex::ToHex;
 use rand::RngCore;
-use serde::{
-    de::{self, Deserialize, Deserializer},
-    Serialize,
-};
-use std::convert::TryFrom;
+use std::{convert::TryFrom, ops::Deref};
 use thiserror::Error;
 
+/// Error type
 #[derive(Error, Debug)]
 pub enum Error {
+    /// The subscription ID is too long
     #[error("Subscription ID is limited to 64 characters")]
     TooLong,
 }
 
-#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
+/// Subscription ID
+///
+/// The rules are simple, any UTF-8 valid string with fewer than 32 characters
+///
+/// By default a random ID will be created if needed.
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct SubscriptionId(String);
 
-impl<'de> Deserialize<'de> for SubscriptionId {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let s = <String>::deserialize(deserializer)?;
-        let t = s
-            .try_into()
-            .map_err(|e: Error| de::Error::custom(e.to_string()))?;
-        Ok(t)
+impl Deref for SubscriptionId {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
     }
 }
 
@@ -75,17 +76,4 @@ mod test {
         let r: Result<SubscriptionId, _> = f.as_str().try_into();
         assert!(r.is_err());
     }
-
-    #[test]
-    fn deserialize() {
-        let r: Result<SubscriptionId, _> = serde_json::from_str("\"ok\"");
-        assert_eq!("ok".to_owned(), r.expect("valid").to_string());
-    }
-
-    #[test]
-    fn deserialize_err() {
-        let f = format!("\"too_long:{}\"", SubscriptionId::default().to_string());
-        let r: Result<SubscriptionId, _> = serde_json::from_str(&f);
-        assert!(r.is_err());
-    }
 }

+ 50 - 0
crates/types/src/types/tag/event.rs

@@ -0,0 +1,50 @@
+//! Event tag
+use super::Addr;
+
+/// A tag that references another event
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Event {
+    /// The other event to relate Id
+    pub id: Addr,
+    /// The relayer where the related content can be found
+    pub relayer_url: Option<String>,
+    /// <marker> is optional and if present is one of "reply", "root", or "mention".
+    pub marker: Option<Marker>,
+}
+
+/// Marker as defined NIP-10
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Marker {
+    /// For top level replies (those replying directly to the root event), only
+    /// the "root" marker should be used
+    Root,
+    /// Denotes the id of the reply event being responded to
+    Reply,
+    /// Denotes a quoted or reposted event id.
+    Mention,
+    /// Unknown marker
+    Unknown(String),
+}
+
+impl ToString for Marker {
+    fn to_string(&self) -> String {
+        (match self {
+            Self::Root => "root",
+            Self::Reply => "reply",
+            Self::Mention => "mention",
+            Self::Unknown(x) => x,
+        })
+        .to_owned()
+    }
+}
+
+impl From<&str> for Marker {
+    fn from(marker: &str) -> Self {
+        match marker.to_ascii_lowercase().as_str() {
+            "root" => Self::Root,
+            "reply" => Self::Reply,
+            "mention" => Self::Mention,
+            _ => Self::Unknown(marker.to_owned()),
+        }
+    }
+}

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

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

+ 15 - 15
crates/types/src/types/tag/mod.rs

@@ -1,20 +1,19 @@
+//! Tag mod
+//!
+//! An event can tag/reference another public key, or another event.
+//!
+//! It can also use the tag to add all sort of metadata, useful for their own
+//! type
 use super::Addr;
 use serde::{
     de::{self, Deserialize, Deserializer},
     ser::{self, SerializeSeq, Serializer},
 };
-use thiserror::Error;
 
-mod key;
+mod event;
 mod public_key;
 
-pub use self::{key::*, public_key::*};
-
-#[derive(Error, Debug)]
-pub enum Error {
-    #[error("Subscription ID is limited to 64 characters")]
-    TooLong,
-}
+pub use self::{event::*, public_key::*};
 
 /// Tags are how events relates to each other.
 ///
@@ -26,6 +25,7 @@ pub enum Error {
 /// their parameters
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum Tag {
+    /// Tag another event
     Event(Event),
     /// Tag another public key
     PubKey(PubKey),
@@ -65,7 +65,7 @@ impl ser::Serialize for Tag {
                 if let Some(relayer) = &event.relayer_url {
                     seq.serialize_element(&relayer)?;
                     if let Some(marker) = &event.marker {
-                        seq.serialize_element(&marker)?;
+                        seq.serialize_element(&marker.to_string())?;
                     }
                 }
             }
@@ -112,7 +112,7 @@ impl<'de> Deserialize<'de> for Tag {
                     Tag::Event(Event {
                         id,
                         relayer_url,
-                        marker: extra,
+                        marker: extra.map(|x| x.as_str().into()),
                     })
                 } else {
                     Tag::PubKey(PubKey {
@@ -130,19 +130,19 @@ impl<'de> Deserialize<'de> for Tag {
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::Message;
+    use crate::Response;
 
     #[test]
     fn bug_01() {
         let json = r#"["EVENT","d45a98f898820258a3313f5cb14f5fe8a9263437931ac6309f23ae0324833f39",{"content":"Will fight under the light of lightnings! No darkness on nostr! 🐶🐾😂","created_at":1678685477,"id":"5462822032c16d32267ba40536409fd51ea188b20e7dd5f9e7a0aa5561346f79","kind":1,"pubkey":"8fb140b4e8ddef97ce4b821d247278a1a4353362623f64021484b372f948000c","sig":"62c8e09a0fedd8096a313ef12436b0f0bcad56e9058d4bc12f61ae6094c099bb966dce47efa2d721c5bfdf923614db46d1d1c248105cb99c4ec495292cc875b1","tags":[["e","5f63f9e7d37673e76ddf7448cd67c3d74f9be96c240a40199b59a30db32d7f43"],["e","c70894331986c66a2baf6fc12dd5c86280e4616cee0e57bbee90972ebbb4b735"],["p","ac3fb436a663b25893657f4b6a3d9d2f02d1974bb5ced603f4d0c8ee32d7e0a2"]]}]"#;
-        let message: Result<Message, _> = serde_json::from_str(json);
+        let message: Result<Response, _> = serde_json::from_str(json);
         assert!(message.is_ok());
     }
 
     #[test]
     fn parse_unexpected_tag() {
         let json = r#"["EVENT","f4e62282fda9c7d93a6e3b03fd1f1a3a34936f74584b0e021edf9659fd7da9d6",{"content":"1678682368","created_at":1678682368,"id":"7506ccd8ce4de835c61c04fd16f8489b2acb8cc052ed6730cba203188dfedf57","kind":30000,"pubkey":"228cc1e37a8fec2eee3dda3a3dbd04a60968086d8f42751a7632499d938eda8f","sig":"73ec91ee5d23a93287700f31274a57d84f46c0aa06523138c8f5b0cf2f20bd8a7db72346ecae9c717d9642cea81d72c35ea95ec615146b1640f62de5e3fbbb69","tags":[["d","chats/null/lastOpened"]]}]"#;
-        let message: Result<Message, _> = serde_json::from_str(json);
+        let message: Result<Response, _> = serde_json::from_str(json);
         assert!(message.is_ok());
     }
 
@@ -152,7 +152,7 @@ mod test {
         let tag: Tag = serde_json::from_str(&json).expect("valid json");
         assert_eq!(
             Tag::PubKey(PubKey {
-                id: Addr::try_from_public_key_str(None, "a0b0c0d0").expect("valid addr"),
+                id: Addr::try_from_public_key_str("a0b0c0d0", None).expect("valid addr"),
                 relayer_url: None,
                 pet_name: None,
             }),

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

@@ -1,8 +1,14 @@
+//! Public key tag
 use super::Addr;
 
+/// Public key tag
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub struct PubKey {
+    /// The public key or a prefix for more privacy
     pub id: Addr,
+    /// Optionally a relayer to connect to
     pub relayer_url: Option<String>,
+    /// A pet name, an internal name, that this public key gives to the
+    /// related/tagged public key
     pub pet_name: Option<String>,
 }

Some files were not shown because too many files changed in this diff