commit 9e603005a3f41583cb26def3b3406e2fcc5f3bed
parent e791ed375864725dc5ba915eb43230a1d1e49cc7
Author: triesap <tyson@radroots.org>
Date: Tue, 14 Jul 2026 07:04:39 +0000
event: add canonical NIP-01 wire
- add RadrootsNip01EventWire parsing, limits, extra preservation, and ID verification
- move event ID preimage generation onto a dedicated canonical serializer
- add event wire conformance vectors for valid, escaped, and mismatched events
- keep no-default event tests green with test-only serde and std harness support
Diffstat:
49 files changed, 1732 insertions(+), 284 deletions(-)
diff --git a/contracts/conformance/vectors/event/nip01_wire.v1.json b/contracts/conformance/vectors/event/nip01_wire.v1.json
@@ -0,0 +1,41 @@
+{
+ "suite": "event",
+ "contract_version": "0.1.0",
+ "vectors": [
+ {
+ "id": "nip01_wire_profile_minimal_001",
+ "kind": "event.nip01_wire.valid",
+ "input": {
+ "raw_json": "{\"id\":\"2a15e33622a155ae231b28bebe390869e67a0e228f77ecfcd652b1ce180a9dde\",\"pubkey\":\"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"created_at\":1700000001,\"kind\":0,\"tags\":[],\"content\":\"{}\",\"sig\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"client\":\"radroots-test\"}"
+ },
+ "expected": {
+ "canonical_id_preimage": "[0,\"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",1700000001,0,[],\"{}\"]",
+ "computed_event_id": "2a15e33622a155ae231b28bebe390869e67a0e228f77ecfcd652b1ce180a9dde",
+ "extra_fields": [
+ "client"
+ ]
+ }
+ },
+ {
+ "id": "nip01_wire_escape_002",
+ "kind": "event.nip01_wire.valid",
+ "input": {
+ "raw_json": "{\"id\":\"bb2ca7d7e3718fb17d70263ff0dc4e8bf4e63b9f425b2e32914a20ca9adc7199\",\"pubkey\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"created_at\":10,\"kind\":1,\"tags\":[[\"t\",\"line\\nsoil\"]],\"content\":\"\\\"\\\\\\n\\r\\t\\b\\f\\u0001\",\"sig\":\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"}"
+ },
+ "expected": {
+ "canonical_id_preimage": "[0,\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",10,1,[[\"t\",\"line\\nsoil\"]],\"\\\"\\\\\\n\\r\\t\\b\\f\\u0001\"]",
+ "computed_event_id": "bb2ca7d7e3718fb17d70263ff0dc4e8bf4e63b9f425b2e32914a20ca9adc7199"
+ }
+ },
+ {
+ "id": "nip01_wire_id_mismatch_003",
+ "kind": "event.nip01_wire.invalid",
+ "input": {
+ "raw_json": "{\"id\":\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"pubkey\":\"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"created_at\":1700000001,\"kind\":0,\"tags\":[],\"content\":\"{}\",\"sig\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\"}"
+ },
+ "expected": {
+ "error": "event_id_mismatch"
+ }
+ }
+ ]
+}
diff --git a/crates/event/Cargo.toml b/crates/event/Cargo.toml
@@ -40,3 +40,11 @@ serde_json = { workspace = true, default-features = false, features = [
sha2 = { workspace = true, default-features = false }
[dev-dependencies]
+radroots_core = { workspace = true, default-features = false, features = [
+ "serde",
+ "std",
+] }
+serde = { workspace = true, default-features = false, features = [
+ "alloc",
+ "derive",
+] }
diff --git a/crates/event/src/account.rs b/crates/event/src/account.rs
@@ -7,7 +7,10 @@ pub const KIND_ACCOUNT_CLAIM: u32 = KIND_ACCOUNT_CLAIM_EVENT;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsAccountClaim {
pub username: String,
diff --git a/crates/event/src/app_data.rs b/crates/event/src/app_data.rs
@@ -9,7 +9,10 @@ pub const KIND_APP_DATA: u32 = KIND_APP_DATA_EVENT;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsAppData {
pub d_tag: String,
diff --git a/crates/event/src/article.rs b/crates/event/src/article.rs
@@ -3,7 +3,10 @@ use alloc::{string::String, vec::Vec};
use crate::social::{RadrootsSocialFarmAnchor, RadrootsSocialLocation};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsArticle {
pub d_tag: String,
diff --git a/crates/event/src/calendar.rs b/crates/event/src/calendar.rs
@@ -6,7 +6,10 @@ use crate::social::{
RadrootsCalendarParticipant, RadrootsSocialLocation, RadrootsSocialTarget,
};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCalendar {
pub d_tag: String,
@@ -29,7 +32,10 @@ pub struct RadrootsCalendar {
pub image: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCalendarDateEvent {
pub d_tag: String,
@@ -72,7 +78,10 @@ pub struct RadrootsCalendarDateEvent {
pub participants: Option<Vec<RadrootsCalendarParticipant>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCalendarTimeEvent {
pub d_tag: String,
@@ -125,7 +134,10 @@ pub struct RadrootsCalendarTimeEvent {
pub participants: Option<Vec<RadrootsCalendarParticipant>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCalendarEventRsvp {
pub d_tag: String,
diff --git a/crates/event/src/comment.rs b/crates/event/src/comment.rs
@@ -3,7 +3,10 @@ use crate::social::RadrootsSocialTarget;
#[cfg(not(feature = "std"))]
use alloc::string::String;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
diff --git a/crates/event/src/coop.rs b/crates/event/src/coop.rs
@@ -7,7 +7,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCoop {
pub d_tag: String,
@@ -22,7 +25,10 @@ pub struct RadrootsCoop {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCoopRef {
pub pubkey: String,
@@ -31,7 +37,10 @@ pub struct RadrootsCoopRef {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsCoopLocation {
pub primary: Option<String>,
diff --git a/crates/event/src/document.rs b/crates/event/src/document.rs
@@ -5,7 +5,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsDocumentSubject {
pub pubkey: String,
@@ -14,7 +17,10 @@ pub struct RadrootsDocumentSubject {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsDocument {
pub d_tag: String,
diff --git a/crates/event/src/draft.rs b/crates/event/src/draft.rs
@@ -1,18 +1,10 @@
#![forbid(unsafe_code)]
-#[cfg(not(feature = "std"))]
-use alloc::{
- borrow::ToOwned,
- string::{String, ToString},
- vec::Vec,
-};
+#[cfg(all(not(feature = "std"), not(test)))]
+use alloc::{borrow::ToOwned, string::String, vec::Vec};
-#[cfg(feature = "std")]
-use std::{
- borrow::ToOwned,
- string::{String, ToString},
- vec::Vec,
-};
+#[cfg(any(feature = "std", test))]
+use std::{borrow::ToOwned, string::String, vec::Vec};
use crate::RadrootsEventEnvelope;
use crate::contract::{
@@ -22,8 +14,11 @@ use crate::contract::{
use crate::ids::{
RadrootsEventId, RadrootsEventSignature, RadrootsIdParseError, RadrootsPublicKey,
};
+use crate::wire::{
+ RadrootsCanonicalEventIdError, canonical_nip01_event_id_preimage,
+ compute_canonical_nip01_event_id,
+};
use core::fmt;
-use sha2::{Digest, Sha256};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsDraftError {
@@ -66,7 +61,7 @@ pub enum RadrootsDraftError {
computed_event_id: String,
},
IdParse(RadrootsIdParseError),
- JsonString(String),
+ CanonicalEventId(RadrootsCanonicalEventIdError),
}
impl fmt::Display for RadrootsDraftError {
@@ -138,7 +133,7 @@ impl fmt::Display for RadrootsDraftError {
"signed event computed id mismatch: expected {expected_event_id}, computed {computed_event_id}"
),
Self::IdParse(error) => write!(f, "{error}"),
- Self::JsonString(error) => write!(f, "json string serialization failed: {error}"),
+ Self::CanonicalEventId(error) => write!(f, "{error}"),
}
}
}
@@ -152,13 +147,19 @@ impl From<RadrootsIdParseError> for RadrootsDraftError {
}
}
-impl From<serde_json::Error> for RadrootsDraftError {
- fn from(value: serde_json::Error) -> Self {
- Self::JsonString(value.to_string())
+impl From<RadrootsCanonicalEventIdError> for RadrootsDraftError {
+ fn from(value: RadrootsCanonicalEventIdError) -> Self {
+ match value {
+ RadrootsCanonicalEventIdError::InvalidPubkey(error) => Self::IdParse(error),
+ error => Self::CanonicalEventId(error),
+ }
}
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsEventDraft {
pub contract_id: String,
@@ -226,7 +227,10 @@ impl RadrootsEventDraft {
}
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsSignedEventParts {
pub id: String,
@@ -239,7 +243,10 @@ pub struct RadrootsSignedEventParts {
pub raw_json: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsSignedEvent {
pub id: String,
@@ -350,11 +357,14 @@ pub fn compute_nip01_event_id(
tags: &[Vec<String>],
content: &str,
) -> Result<RadrootsEventId, RadrootsDraftError> {
- let pubkey = RadrootsPublicKey::parse(pubkey)?;
- let preimage = nip01_event_id_preimage(pubkey.as_str(), created_at, kind, tags, content)?;
- let digest = Sha256::digest(preimage.as_bytes());
- let event_id = hex::encode(digest);
- Ok(RadrootsEventId::parse(event_id)?)
+ RadrootsPublicKey::parse(pubkey)?;
+ Ok(compute_canonical_nip01_event_id(
+ pubkey,
+ u64::from(created_at),
+ kind,
+ tags,
+ content,
+ )?)
}
pub fn nip01_event_id_preimage(
@@ -364,36 +374,13 @@ pub fn nip01_event_id_preimage(
tags: &[Vec<String>],
content: &str,
) -> Result<String, RadrootsDraftError> {
- let mut preimage = String::new();
- preimage.push_str("[0,");
- push_json_string(&mut preimage, pubkey)?;
- preimage.push(',');
- preimage.push_str(created_at.to_string().as_str());
- preimage.push(',');
- preimage.push_str(kind.to_string().as_str());
- preimage.push_str(",[");
- for (tag_index, tag) in tags.iter().enumerate() {
- if tag_index > 0 {
- preimage.push(',');
- }
- preimage.push('[');
- for (value_index, value) in tag.iter().enumerate() {
- if value_index > 0 {
- preimage.push(',');
- }
- push_json_string(&mut preimage, value)?;
- }
- preimage.push(']');
- }
- preimage.push_str("],");
- push_json_string(&mut preimage, content)?;
- preimage.push(']');
- Ok(preimage)
-}
-
-fn push_json_string(target: &mut String, value: &str) -> Result<(), RadrootsDraftError> {
- target.push_str(serde_json::to_string(value)?.as_str());
- Ok(())
+ Ok(canonical_nip01_event_id_preimage(
+ pubkey,
+ u64::from(created_at),
+ kind,
+ tags,
+ content,
+ )?)
}
#[cfg(test)]
@@ -850,18 +837,25 @@ mod tests {
computed_event_id: hex_64('f'),
},
RadrootsDraftError::from(RadrootsIdParseError::Empty),
+ RadrootsDraftError::CanonicalEventId(
+ RadrootsCanonicalEventIdError::InvalidComputedEventId(
+ RadrootsIdParseError::InvalidFormat,
+ ),
+ ),
];
for error in errors {
assert!(!error.to_string().is_empty());
}
- let json_error = serde_json::from_str::<String>("{").expect_err("json error");
- let error = RadrootsDraftError::from(json_error);
assert!(
- error
- .to_string()
- .contains("json string serialization failed")
+ RadrootsDraftError::CanonicalEventId(
+ RadrootsCanonicalEventIdError::InvalidComputedEventId(
+ RadrootsIdParseError::InvalidFormat,
+ ),
+ )
+ .to_string()
+ .contains("canonical event id digest")
);
}
diff --git a/crates/event/src/farm.rs b/crates/event/src/farm.rs
@@ -3,7 +3,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsFarm {
pub d_tag: String,
@@ -18,7 +21,10 @@ pub struct RadrootsFarm {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, Default)]
pub struct RadrootsFarmRef {
pub pubkey: String,
@@ -27,7 +33,10 @@ pub struct RadrootsFarmRef {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsFarmPublicLocation {
pub primary: String,
diff --git a/crates/event/src/farm_crdt.rs b/crates/event/src/farm_crdt.rs
@@ -13,7 +13,10 @@ pub const KIND_FARM_CRDT_CHANGE: u32 = KIND_FARM_CRDT_CHANGE_EVENT;
pub const RADROOTS_FARM_CRDT_CHANGE_SCHEMA: &str = "radroots.farm.crdt.change.v1";
pub const RADROOTS_FARM_CRDT_TAG: &str = "radroots:farm:crdt";
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsFarmCrdtChange {
pub schema: String,
@@ -33,8 +36,11 @@ pub struct RadrootsFarmCrdtChange {
pub app_version: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(from = "String", into = "String"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(from = "String", into = "String"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsFarmCrdtDocumentKind {
FarmMembership,
@@ -55,7 +61,10 @@ pub enum RadrootsFarmCrdtDocumentKind {
Other { value: String },
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsCrdtBackend {
Automerge,
@@ -63,8 +72,11 @@ pub enum RadrootsCrdtBackend {
Loro,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(from = "String", into = "String"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(from = "String", into = "String"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsFarmSemanticKind {
FarmTaskCreate,
diff --git a/crates/event/src/farm_file.rs b/crates/event/src/farm_file.rs
@@ -9,7 +9,10 @@ use alloc::{string::String, vec::Vec};
pub const KIND_FARM_FILE_METADATA: u32 = KIND_FARM_FILE_METADATA_EVENT;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsFarmFileMetadata {
pub d_tag: String,
@@ -31,14 +34,20 @@ pub struct RadrootsFarmFileMetadata {
pub fallbacks: Vec<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RadrootsFarmFileDimensions {
pub w: u32,
pub h: u32,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsFarmFileSource {
pub url: String,
diff --git a/crates/event/src/farm_workspace.rs b/crates/event/src/farm_workspace.rs
@@ -11,7 +11,10 @@ pub const RADROOTS_FARM_WORKSPACE_SCHEMA: &str = "radroots.farm.workspace.v1";
pub const RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION: &str = "field-nostr-v1";
pub const RADROOTS_FARM_WORKSPACE_TAG: &str = "radroots:farm:workspace";
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsFarmWorkspaceManifest {
pub d_tag: String,
@@ -28,21 +31,30 @@ pub struct RadrootsFarmWorkspaceManifest {
pub updated_at_ms: Option<u64>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsFarmWorkspaceRef {
pub pubkey: String,
pub d_tag: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsFarmWorkspaceRelay {
pub url: String,
pub mode: RadrootsFarmWorkspaceRelayMode,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsFarmWorkspaceRelayMode {
Read,
@@ -50,7 +62,10 @@ pub enum RadrootsFarmWorkspaceRelayMode {
ReadWrite,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsFarmWorkspaceMediaServer {
pub url: String,
diff --git a/crates/event/src/file_metadata.rs b/crates/event/src/file_metadata.rs
@@ -3,7 +3,10 @@ use alloc::{string::String, vec::Vec};
use crate::social::{RadrootsSocialMediaDimensions, RadrootsSocialMediaThumbnail};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsFileMetadata {
pub url: String,
diff --git a/crates/event/src/follow.rs b/crates/event/src/follow.rs
@@ -3,7 +3,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsFollow {
pub list: Vec<RadrootsFollowProfile>,
@@ -11,7 +14,10 @@ pub struct RadrootsFollow {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsFollowProfile {
pub published_at: u32,
diff --git a/crates/event/src/gcs.rs b/crates/event/src/gcs.rs
@@ -3,27 +3,36 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGeoJsonPoint {
- #[cfg_attr(feature = "serde", serde(rename = "type"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "type"))]
pub r#type: String,
pub coordinates: [f64; 2],
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGeoJsonPolygon {
- #[cfg_attr(feature = "serde", serde(rename = "type"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "type"))]
pub r#type: String,
pub coordinates: Vec<Vec<[f64; 2]>>,
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGcsLocation {
pub lat: f64,
diff --git a/crates/event/src/geochat.rs b/crates/event/src/geochat.rs
@@ -5,7 +5,10 @@ use alloc::string::String;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGeoChat {
pub geohash: String,
diff --git a/crates/event/src/gift_wrap.rs b/crates/event/src/gift_wrap.rs
@@ -5,7 +5,10 @@ use alloc::string::String;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGiftWrap {
pub recipient: RadrootsGiftWrapRecipient,
@@ -15,7 +18,10 @@ pub struct RadrootsGiftWrap {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGiftWrapRecipient {
pub public_key: String,
diff --git a/crates/event/src/group.rs b/crates/event/src/group.rs
@@ -33,7 +33,10 @@ pub const KIND_GROUP_ADMINS: u32 = KIND_GROUP_ADMINS_EVENT;
pub const KIND_GROUP_MEMBERS: u32 = KIND_GROUP_MEMBERS_EVENT;
pub const KIND_GROUP_ROLES: u32 = KIND_GROUP_ROLES_EVENT;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupPutUser {
pub group_id: String,
@@ -42,7 +45,10 @@ pub struct RadrootsGroupPutUser {
pub roles: Vec<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupRemoveUser {
pub group_id: String,
@@ -50,7 +56,10 @@ pub struct RadrootsGroupRemoveUser {
pub pubkey: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupCreateGroup {
pub group_id: String,
@@ -58,7 +67,10 @@ pub struct RadrootsGroupCreateGroup {
pub metadata: RadrootsGroupEditableMetadata,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupEditMetadata {
pub group_id: String,
@@ -66,14 +78,20 @@ pub struct RadrootsGroupEditMetadata {
pub metadata: RadrootsGroupEditableMetadata,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupDeleteGroup {
pub group_id: String,
pub message: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupDeleteEvent {
pub group_id: String,
@@ -81,7 +99,10 @@ pub struct RadrootsGroupDeleteEvent {
pub event_id: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupCreateInvite {
pub group_id: String,
@@ -89,7 +110,10 @@ pub struct RadrootsGroupCreateInvite {
pub code: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupJoinRequest {
pub group_id: String,
@@ -97,21 +121,30 @@ pub struct RadrootsGroupJoinRequest {
pub code: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupLeaveRequest {
pub group_id: String,
pub message: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupMetadata {
pub d_tag: String,
pub metadata: RadrootsGroupEditableMetadata,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupAdmins {
pub d_tag: String,
@@ -119,7 +152,10 @@ pub struct RadrootsGroupAdmins {
pub admins: Vec<RadrootsGroupUserRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupMembers {
pub d_tag: String,
@@ -127,7 +163,10 @@ pub struct RadrootsGroupMembers {
pub members: Vec<RadrootsGroupUserRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupRoles {
pub d_tag: String,
@@ -135,7 +174,10 @@ pub struct RadrootsGroupRoles {
pub roles: Vec<RadrootsGroupRole>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsGroupEditableMetadata {
pub name: Option<String>,
@@ -148,14 +190,20 @@ pub struct RadrootsGroupEditableMetadata {
pub supported_kinds: Option<Vec<u32>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupUserRef {
pub pubkey: String,
pub roles: Vec<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsGroupRole {
pub name: String,
diff --git a/crates/event/src/http_auth.rs b/crates/event/src/http_auth.rs
@@ -7,7 +7,10 @@ use alloc::string::String;
pub const KIND_HTTP_AUTH: u32 = KIND_HTTP_AUTH_EVENT;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsHttpAuth {
pub url: String,
diff --git a/crates/event/src/ids.rs b/crates/event/src/ids.rs
@@ -150,7 +150,7 @@ macro_rules! validated_string_id {
}
}
- #[cfg(feature = "serde")]
+ #[cfg(any(feature = "serde", test))]
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -160,7 +160,7 @@ macro_rules! validated_string_id {
}
}
- #[cfg(feature = "serde")]
+ #[cfg(any(feature = "serde", test))]
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -378,7 +378,7 @@ mod tests {
assert_eq!(converted, value);
assert_eq!(id.into_string(), value.to_owned());
- #[cfg(feature = "serde")]
+ #[cfg(any(feature = "serde", test))]
{
let id = <$ty>::parse(value).expect("parse");
let encoded = serde_json::to_string(&id).expect("serialize");
diff --git a/crates/event/src/job.rs b/crates/event/src/job.rs
@@ -3,41 +3,50 @@ use alloc::string::String;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub enum JobInputType {
- #[cfg_attr(feature = "serde", serde(rename = "url"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "url"))]
Url,
- #[cfg_attr(feature = "serde", serde(rename = "event"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "event"))]
Event,
- #[cfg_attr(feature = "serde", serde(rename = "job"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "job"))]
Job,
- #[cfg_attr(feature = "serde", serde(rename = "text"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "text"))]
Text,
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub enum JobFeedbackStatus {
- #[cfg_attr(feature = "serde", serde(rename = "payment_required"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "payment_required"))]
PaymentRequired,
- #[cfg_attr(feature = "serde", serde(rename = "processing"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "processing"))]
Processing,
- #[cfg_attr(feature = "serde", serde(rename = "error"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "error"))]
Error,
- #[cfg_attr(feature = "serde", serde(rename = "success"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "success"))]
Success,
- #[cfg_attr(feature = "serde", serde(rename = "partial"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "partial"))]
Partial,
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct JobPaymentRequest {
pub amount_sat: u32,
diff --git a/crates/event/src/job_feedback.rs b/crates/event/src/job_feedback.rs
@@ -8,7 +8,10 @@ use alloc::string::String;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsJobFeedback {
pub kind: u16,
diff --git a/crates/event/src/job_request.rs b/crates/event/src/job_request.rs
@@ -5,7 +5,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsJobInput {
pub data: String,
@@ -16,7 +19,10 @@ pub struct RadrootsJobInput {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsJobParam {
pub key: String,
@@ -25,7 +31,10 @@ pub struct RadrootsJobParam {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsJobRequest {
pub kind: u16,
diff --git a/crates/event/src/job_result.rs b/crates/event/src/job_result.rs
@@ -5,7 +5,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsJobResult {
pub kind: u16,
diff --git a/crates/event/src/knowledge.rs b/crates/event/src/knowledge.rs
@@ -524,7 +524,10 @@ pub fn validate_contribution_attestation(
validate_event_refs(&attestation.evidence_refs, "evidence_refs")
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -535,7 +538,10 @@ pub struct RadrootsAddressableRef {
pub relays: Vec<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -546,7 +552,10 @@ pub struct RadrootsRightsAssertion {
pub url: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -561,7 +570,10 @@ pub struct RadrootsWikiArticle {
pub deferred_to: Option<RadrootsWikiArticleVersionRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -570,7 +582,10 @@ pub struct RadrootsWikiArticleVersionRef {
pub address_ref: RadrootsAddressableRef,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -579,7 +594,10 @@ pub struct RadrootsWikiRedirect {
pub target: RadrootsAddressableRef,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -591,7 +609,10 @@ pub struct RadrootsWikiMergeRequest {
pub explanation: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -612,7 +633,10 @@ pub struct RadrootsKnowledgeSource {
pub summary: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -628,7 +652,10 @@ pub struct RadrootsKnowledgeClaim {
pub supersedes: Vec<RadrootsEventRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -642,7 +669,10 @@ pub struct RadrootsKnowledgeCitationSpan {
pub chunk_id: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -654,7 +684,10 @@ pub struct RadrootsKnowledgeNodeRef {
pub label: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -669,7 +702,10 @@ pub struct RadrootsKnowledgeRelation {
pub supersedes: Vec<RadrootsEventRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -684,7 +720,10 @@ pub struct RadrootsKnowledgeReview {
pub evidence_refs: Vec<RadrootsEventRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -697,10 +736,13 @@ pub struct RadrootsKnowledgeReviewTarget {
pub review_scope: RadrootsKnowledgeReviewScope,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsKnowledgeReviewScope {
SpecificVersion,
@@ -708,7 +750,10 @@ pub enum RadrootsKnowledgeReviewScope {
PolicyLatest,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -718,7 +763,10 @@ pub struct RadrootsKnowledgeReviewScore {
pub note: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -735,7 +783,10 @@ pub struct RadrootsKnowledgeFieldReport {
pub limitations: Vec<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -747,10 +798,13 @@ pub struct RadrootsKnowledgeFieldContext {
pub context_tags: Vec<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsKnowledgeLocationPrecision {
None,
@@ -761,7 +815,10 @@ pub enum RadrootsKnowledgeLocationPrecision {
ExactPrivateReference,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -772,7 +829,10 @@ pub struct RadrootsKnowledgeLocation {
pub geohash: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -783,7 +843,10 @@ pub struct RadrootsKnowledgeObservation {
pub values: Vec<RadrootsKnowledgeObservationValue>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -793,7 +856,10 @@ pub struct RadrootsKnowledgeObservationValue {
pub unit: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -809,7 +875,10 @@ pub struct RadrootsEvidenceBounty {
pub closes_at: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -824,7 +893,10 @@ pub struct RadrootsKnowledgeChangeProposal {
pub supersedes: Vec<RadrootsEventRef>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/event/src/lib.rs b/crates/event/src/lib.rs
@@ -1,4 +1,4 @@
-#![cfg_attr(not(feature = "std"), no_std)]
+#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![forbid(unsafe_code)]
#[cfg(not(feature = "std"))]
extern crate alloc;
@@ -61,8 +61,17 @@ pub mod seal;
pub mod social;
pub mod tags;
pub mod trade_validation;
+pub mod wire;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub use wire::{
+ RadrootsCanonicalEventIdError, RadrootsEventWireError, RadrootsEventWireLimits,
+ RadrootsNip01EventWire, canonical_nip01_event_id_preimage, compute_canonical_nip01_event_id,
+};
+
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -76,7 +85,10 @@ pub struct RadrootsEventEnvelope {
pub sig: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -88,7 +100,10 @@ pub struct RadrootsEventRef {
pub relays: Option<Vec<String>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/event/src/list.rs b/crates/event/src/list.rs
@@ -3,7 +3,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsList {
pub content: String,
@@ -12,7 +15,10 @@ pub struct RadrootsList {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsListEntry {
pub tag: String,
diff --git a/crates/event/src/list_set.rs b/crates/event/src/list_set.rs
@@ -5,7 +5,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsListSet {
pub d_tag: String,
diff --git a/crates/event/src/listing.rs b/crates/event/src/listing.rs
@@ -11,7 +11,10 @@ use crate::resource_area::RadrootsResourceAreaRef;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(
@@ -31,7 +34,10 @@ pub enum RadrootsListingAvailability {
},
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(
@@ -45,7 +51,10 @@ pub enum RadrootsListingStatus {
Other { value: String },
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(
@@ -60,7 +69,10 @@ pub enum RadrootsListingDeliveryMethod {
Other { method: String },
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
@@ -72,7 +84,7 @@ pub struct RadrootsListing {
serde(default, skip_serializing_if = "Option::is_none")
)]
pub published_at: Option<u64>,
- #[cfg_attr(feature = "serde", serde(default))]
+ #[cfg_attr(any(feature = "serde", test), serde(default))]
pub farm: RadrootsFarmRef,
pub product: RadrootsListingProduct,
pub primary_bin_id: RadrootsInventoryBinId,
@@ -87,7 +99,10 @@ pub struct RadrootsListing {
pub images: Option<Vec<RadrootsListingImage>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
@@ -109,7 +124,10 @@ pub const RADROOTS_LISTING_PRODUCT_TAG_KEYS: [&str; 9] = [
pub struct RadrootsListingProductTagKeys;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
@@ -126,7 +144,10 @@ pub struct RadrootsListingBin {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsListingPublicLocation {
pub primary: String,
@@ -136,7 +157,10 @@ pub struct RadrootsListingPublicLocation {
pub geohash: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
@@ -145,7 +169,10 @@ pub struct RadrootsListingImage {
pub size: Option<RadrootsListingImageSize>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[derive(Clone, Debug)]
pub struct RadrootsListingImageSize {
diff --git a/crates/event/src/message.rs b/crates/event/src/message.rs
@@ -7,7 +7,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsMessage {
pub recipients: Vec<RadrootsMessageRecipient>,
@@ -18,7 +21,10 @@ pub struct RadrootsMessage {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsMessageRecipient {
pub public_key: String,
diff --git a/crates/event/src/message_file.rs b/crates/event/src/message_file.rs
@@ -8,7 +8,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsMessageFile {
pub recipients: Vec<RadrootsMessageRecipient>,
@@ -31,7 +34,10 @@ pub struct RadrootsMessageFile {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RadrootsMessageFileDimensions {
pub w: u32,
diff --git a/crates/event/src/order.rs b/crates/event/src/order.rs
@@ -21,7 +21,10 @@ use radroots_core::{RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney
pub const RADROOTS_COMMERCIAL_LISTING_DOMAIN: &str = "trade:listing";
pub const RADROOTS_ORDER_ENVELOPE_VERSION: u16 = 1;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -166,7 +169,10 @@ impl RadrootsOrderEconomics {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderRequest")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderRequest {
pub order_id: RadrootsOrderId,
@@ -195,7 +201,10 @@ impl RadrootsOrderRequest {
feature = "dto-bindgen",
dto(ts(name = "RadrootsOrderRevisionProposal"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderRevisionProposal {
pub revision_id: RadrootsOrderRevisionId,
@@ -226,10 +235,16 @@ impl RadrootsOrderRevisionProposal {
}
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case", tag = "decision"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ serde(rename_all = "snake_case", tag = "decision")
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsOrderRevisionOutcome {
Accepted,
@@ -251,7 +266,10 @@ impl RadrootsOrderRevisionOutcome {
feature = "dto-bindgen",
dto(ts(name = "RadrootsOrderRevisionDecision"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderRevisionDecision {
pub revision_id: RadrootsOrderRevisionId,
@@ -283,7 +301,10 @@ impl RadrootsOrderRevisionDecision {
feature = "dto-bindgen",
dto(ts(name = "RadrootsOrderInventoryCommitment"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderInventoryCommitment {
pub bin_id: RadrootsInventoryBinId,
@@ -296,15 +317,21 @@ pub struct RadrootsOrderInventoryCommitment {
feature = "dto-bindgen",
dto(ts(name = "RadrootsOrderDecisionOutcome"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case", tag = "decision"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(
+ any(feature = "serde", test),
+ serde(rename_all = "snake_case", tag = "decision")
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsOrderDecisionOutcome {
- #[cfg_attr(feature = "serde", serde(rename = "accepted"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "accepted"))]
Accepted {
inventory_commitments: Vec<RadrootsOrderInventoryCommitment>,
},
- #[cfg_attr(feature = "serde", serde(rename = "declined"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "declined"))]
Declined { reason: String },
}
@@ -322,7 +349,10 @@ impl RadrootsOrderDecisionOutcome {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderDecision")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderDecision {
pub order_id: RadrootsOrderId,
@@ -345,7 +375,10 @@ impl RadrootsOrderDecision {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderCancellation")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderCancellation {
pub order_id: RadrootsOrderId,
@@ -368,29 +401,41 @@ impl RadrootsOrderCancellation {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsCommercialDomain")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsCommercialDomain {
- #[cfg_attr(feature = "serde", serde(rename = "trade:listing"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "trade:listing"))]
Listing,
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderEventType")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsOrderEventType {
- #[cfg_attr(feature = "serde", serde(rename = "TradeOrderRequested"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "TradeOrderRequested"))]
OrderRequested,
- #[cfg_attr(feature = "serde", serde(rename = "TradeOrderDecision"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "TradeOrderDecision"))]
OrderDecision,
- #[cfg_attr(feature = "serde", serde(rename = "TradeOrderRevisionProposed"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(rename = "TradeOrderRevisionProposed")
+ )]
OrderRevisionProposed,
- #[cfg_attr(feature = "serde", serde(rename = "TradeOrderRevisionDecision"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(rename = "TradeOrderRevisionDecision")
+ )]
OrderRevisionDecision,
- #[cfg_attr(feature = "serde", serde(rename = "TradeOrderCancelled"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "TradeOrderCancelled"))]
OrderCancelled,
}
@@ -446,12 +491,15 @@ impl RadrootsOrderEventType {
}
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderEnvelope<T> {
pub version: u16,
pub domain: RadrootsCommercialDomain,
- #[cfg_attr(feature = "serde", serde(rename = "type"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "type"))]
pub message_type: RadrootsOrderEventType,
pub order_id: String,
pub listing_addr: String,
diff --git a/crates/event/src/order_economics.rs b/crates/event/src/order_economics.rs
@@ -12,7 +12,10 @@ use crate::ids::{RadrootsInventoryBinId, RadrootsOrderQuoteId};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderItem")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderItem {
pub bin_id: RadrootsInventoryBinId,
@@ -22,11 +25,14 @@ pub struct RadrootsOrderItem {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderPricingBasis")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsOrderPricingBasis {
- #[cfg_attr(feature = "serde", serde(rename = "listing_event"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "listing_event"))]
ListingEvent,
}
@@ -36,45 +42,57 @@ pub enum RadrootsOrderPricingBasis {
feature = "dto-bindgen",
dto(ts(name = "RadrootsOrderEconomicLineKind"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsOrderEconomicLineKind {
- #[cfg_attr(feature = "serde", serde(rename = "listing_discount"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "listing_discount"))]
ListingDiscount,
- #[cfg_attr(feature = "serde", serde(rename = "basket_adjustment"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "basket_adjustment"))]
BasketAdjustment,
- #[cfg_attr(feature = "serde", serde(rename = "revision_adjustment"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "revision_adjustment"))]
RevisionAdjustment,
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderEconomicActor")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsOrderEconomicActor {
- #[cfg_attr(feature = "serde", serde(rename = "buyer"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "buyer"))]
Buyer,
- #[cfg_attr(feature = "serde", serde(rename = "seller"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "seller"))]
Seller,
}
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderEconomicEffect")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RadrootsOrderEconomicEffect {
- #[cfg_attr(feature = "serde", serde(rename = "increase"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "increase"))]
Increase,
- #[cfg_attr(feature = "serde", serde(rename = "decrease"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "decrease"))]
Decrease,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -91,7 +109,10 @@ pub struct RadrootsOrderEconomicItem {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderEconomicLine")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderEconomicLine {
pub id: String,
@@ -105,7 +126,10 @@ pub struct RadrootsOrderEconomicLine {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(feature = "dto-bindgen", dto(ts(name = "RadrootsOrderEconomicTotals")))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsOrderEconomicTotals {
pub subtotal: RadrootsCoreMoney,
@@ -114,7 +138,10 @@ pub struct RadrootsOrderEconomicTotals {
pub total: RadrootsCoreMoney,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/event/src/plot.rs b/crates/event/src/plot.rs
@@ -5,7 +5,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsPlotRef {
pub pubkey: String,
@@ -14,7 +17,10 @@ pub struct RadrootsPlotRef {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsPlot {
pub d_tag: String,
@@ -27,7 +33,10 @@ pub struct RadrootsPlot {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsPlotLocation {
pub primary: Option<String>,
diff --git a/crates/event/src/post.rs b/crates/event/src/post.rs
@@ -6,7 +6,10 @@ use crate::social::{
RadrootsSocialTarget,
};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
diff --git a/crates/event/src/profile.rs b/crates/event/src/profile.rs
@@ -10,19 +10,22 @@ pub const RADROOTS_PROFILE_TYPE_TAG_RADROOTSD: &str = "radroots:type:radrootsd";
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub enum RadrootsProfileType {
- #[cfg_attr(feature = "serde", serde(rename = "individual"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "individual"))]
Individual,
- #[cfg_attr(feature = "serde", serde(rename = "farm"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "farm"))]
Farm,
- #[cfg_attr(feature = "serde", serde(rename = "coop"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "coop"))]
Coop,
- #[cfg_attr(feature = "serde", serde(rename = "any"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "any"))]
Any,
- #[cfg_attr(feature = "serde", serde(rename = "radrootsd"))]
+ #[cfg_attr(any(feature = "serde", test), serde(rename = "radrootsd"))]
Radrootsd,
}
@@ -49,7 +52,10 @@ pub fn radroots_profile_type_from_tag_value(value: &str) -> Option<RadrootsProfi
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsProfile {
pub name: String,
diff --git a/crates/event/src/reaction.rs b/crates/event/src/reaction.rs
@@ -3,7 +3,10 @@ use crate::social::RadrootsSocialTarget;
#[cfg(not(feature = "std"))]
use alloc::string::String;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
diff --git a/crates/event/src/relay_auth.rs b/crates/event/src/relay_auth.rs
@@ -7,7 +7,10 @@ use alloc::string::String;
pub const KIND_RELAY_AUTH: u32 = KIND_RELAY_AUTH_EVENT;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsRelayAuth {
pub relay: String,
diff --git a/crates/event/src/relay_document.rs b/crates/event/src/relay_document.rs
@@ -3,21 +3,45 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsRelayDocument {
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub name: Option<String>,
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub description: Option<String>,
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub pubkey: Option<String>,
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub contact: Option<String>,
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub supported_nips: Option<Vec<u16>>,
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub software: Option<String>,
- #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
+ #[cfg_attr(
+ any(feature = "serde", test),
+ serde(skip_serializing_if = "Option::is_none")
+ )]
pub version: Option<String>,
}
diff --git a/crates/event/src/report.rs b/crates/event/src/report.rs
@@ -3,7 +3,10 @@ use alloc::string::String;
use crate::social::{RadrootsReportFileTarget, RadrootsReportType, RadrootsSocialTarget};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsReport {
pub reported_pubkey: String,
diff --git a/crates/event/src/repost.rs b/crates/event/src/repost.rs
@@ -3,7 +3,10 @@ use alloc::string::String;
use crate::social::RadrootsSocialTarget;
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsRepost {
pub target: RadrootsSocialTarget,
@@ -14,7 +17,10 @@ pub struct RadrootsRepost {
pub content: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsGenericRepost {
pub target: RadrootsSocialTarget,
diff --git a/crates/event/src/resource_area.rs b/crates/event/src/resource_area.rs
@@ -7,7 +7,10 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsResourceArea {
pub d_tag: String,
@@ -19,7 +22,10 @@ pub struct RadrootsResourceArea {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsResourceAreaRef {
pub pubkey: String,
@@ -28,7 +34,10 @@ pub struct RadrootsResourceAreaRef {
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsResourceAreaLocation {
pub primary: Option<String>,
diff --git a/crates/event/src/resource_cap.rs b/crates/event/src/resource_cap.rs
@@ -9,14 +9,20 @@ use alloc::{string::String, vec::Vec};
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsResourceHarvestProduct {
pub key: String,
pub category: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[derive(Clone, Debug)]
diff --git a/crates/event/src/seal.rs b/crates/event/src/seal.rs
@@ -5,7 +5,10 @@ use alloc::string::String;
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug)]
pub struct RadrootsSeal {
pub content: String,
diff --git a/crates/event/src/social.rs b/crates/event/src/social.rs
@@ -3,9 +3,15 @@ use crate::farm::RadrootsFarmRef;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case", tag = "kind"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ serde(rename_all = "snake_case", tag = "kind")
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsSocialTarget {
Event {
@@ -27,7 +33,10 @@ pub enum RadrootsSocialTarget {
},
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[derive(Clone, Debug, Default)]
pub struct RadrootsSocialFarmAnchor {
@@ -35,7 +44,10 @@ pub struct RadrootsSocialFarmAnchor {
pub relays: Option<Vec<String>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsSocialLocation {
@@ -43,7 +55,10 @@ pub struct RadrootsSocialLocation {
pub geohash: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsSocialMediaDimensions {
@@ -51,7 +66,10 @@ pub struct RadrootsSocialMediaDimensions {
pub height: u32,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsSocialMediaThumbnail {
@@ -59,7 +77,10 @@ pub struct RadrootsSocialMediaThumbnail {
pub dimensions: Option<RadrootsSocialMediaDimensions>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsSocialMediaMetadata {
@@ -82,7 +103,10 @@ pub struct RadrootsSocialMediaMetadata {
pub imeta: Option<Vec<Vec<String>>>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsCalendarParticipant {
pub pubkey: String,
@@ -90,14 +114,20 @@ pub struct RadrootsCalendarParticipant {
pub role: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsCalendarDateValue {
pub value: String,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsCalendarEventRsvpStatus {
Accepted,
@@ -105,16 +135,22 @@ pub enum RadrootsCalendarEventRsvpStatus {
Tentative,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsCalendarEventFreeBusy {
Free,
Busy,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
+#[cfg_attr(any(feature = "serde", test), serde(rename_all = "snake_case"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsReportType {
Nudity,
@@ -126,7 +162,10 @@ pub enum RadrootsReportType {
Other,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RadrootsReportFileTarget {
pub sha256: Option<String>,
@@ -134,7 +173,10 @@ pub struct RadrootsReportFileTarget {
pub magnet: Option<String>,
}
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsReportTarget {
pub reported_pubkey: String,
diff --git a/crates/event/src/trade_validation.rs b/crates/event/src/trade_validation.rs
@@ -5,7 +5,10 @@ use alloc::{string::String, vec::Vec};
use crate::{RadrootsEventPtr, order::RadrootsListingParseError};
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
#[cfg_attr(
@@ -84,7 +87,10 @@ impl std::error::Error for RadrootsTradeValidationListingError {}
feature = "dto-bindgen",
dto(ts(name = "RadrootsTradeListingValidateRequest"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsTradeValidationListingRequest {
pub listing_event: Option<RadrootsEventPtr>,
@@ -96,7 +102,10 @@ pub struct RadrootsTradeValidationListingRequest {
feature = "dto-bindgen",
dto(ts(name = "RadrootsTradeListingValidateResult"))
)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(
+ any(feature = "serde", test),
+ derive(serde::Serialize, serde::Deserialize)
+)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsTradeValidationListingResult {
pub valid: bool,
diff --git a/crates/event/src/wire.rs b/crates/event/src/wire.rs
@@ -0,0 +1,883 @@
+#![forbid(unsafe_code)]
+
+#[cfg(all(not(feature = "std"), not(test)))]
+use alloc::{
+ collections::BTreeMap,
+ string::{String, ToString},
+ vec::Vec,
+};
+
+#[cfg(any(feature = "std", test))]
+use std::{collections::BTreeMap, string::String, vec::Vec};
+
+use crate::ids::{
+ RadrootsEventId, RadrootsEventSignature, RadrootsIdParseError, RadrootsPublicKey,
+};
+use core::fmt;
+use serde_json::{Map, Value};
+use sha2::{Digest, Sha256};
+
+pub const DEFAULT_RAW_JSON_MAX_BYTES: usize = 256 * 1024;
+pub const DEFAULT_CONTENT_MAX_BYTES: usize = 128 * 1024;
+pub const DEFAULT_TAG_MAX_COUNT: usize = 1024;
+pub const DEFAULT_TAG_ELEMENT_MAX_BYTES: usize = 4 * 1024;
+pub const DEFAULT_TAG_TOTAL_MAX_BYTES: usize = 128 * 1024;
+pub const DEFAULT_EXTRA_MAX_FIELDS: usize = 64;
+pub const DEFAULT_EXTRA_TOTAL_JSON_MAX_BYTES: usize = 64 * 1024;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct RadrootsEventWireLimits {
+ pub max_raw_json_bytes: usize,
+ pub max_content_bytes: usize,
+ pub max_tag_count: usize,
+ pub max_tag_element_bytes: usize,
+ pub max_total_tag_bytes: usize,
+ pub max_extra_fields: usize,
+ pub max_total_extra_json_bytes: usize,
+}
+
+impl Default for RadrootsEventWireLimits {
+ fn default() -> Self {
+ Self {
+ max_raw_json_bytes: DEFAULT_RAW_JSON_MAX_BYTES,
+ max_content_bytes: DEFAULT_CONTENT_MAX_BYTES,
+ max_tag_count: DEFAULT_TAG_MAX_COUNT,
+ max_tag_element_bytes: DEFAULT_TAG_ELEMENT_MAX_BYTES,
+ max_total_tag_bytes: DEFAULT_TAG_TOTAL_MAX_BYTES,
+ max_extra_fields: DEFAULT_EXTRA_MAX_FIELDS,
+ max_total_extra_json_bytes: DEFAULT_EXTRA_TOTAL_JSON_MAX_BYTES,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum RadrootsCanonicalEventIdError {
+ InvalidPubkey(RadrootsIdParseError),
+ InvalidComputedEventId(RadrootsIdParseError),
+}
+
+impl fmt::Display for RadrootsCanonicalEventIdError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::InvalidPubkey(error) => {
+ write!(f, "canonical event id pubkey is invalid: {error}")
+ }
+ Self::InvalidComputedEventId(error) => {
+ write!(f, "canonical event id digest is invalid: {error}")
+ }
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for RadrootsCanonicalEventIdError {}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum RadrootsEventWireError {
+ Json(String),
+ RootNotObject,
+ MissingField(&'static str),
+ InvalidField(&'static str),
+ InvalidIdentifier {
+ field: &'static str,
+ error: RadrootsIdParseError,
+ },
+ NonCanonicalIdentifier {
+ field: &'static str,
+ },
+ RawJsonTooLarge {
+ max: usize,
+ actual: usize,
+ },
+ ContentTooLarge {
+ max: usize,
+ actual: usize,
+ },
+ TooManyTags {
+ max: usize,
+ actual: usize,
+ },
+ EmptyTag {
+ index: usize,
+ },
+ EmptyTagKey {
+ index: usize,
+ },
+ ControlCharacterTagKey {
+ index: usize,
+ },
+ TagElementTooLarge {
+ tag_index: usize,
+ element_index: usize,
+ max: usize,
+ actual: usize,
+ },
+ TagsTooLarge {
+ max: usize,
+ actual: usize,
+ },
+ TooManyExtraFields {
+ max: usize,
+ actual: usize,
+ },
+ ExtraJsonTooLarge {
+ max: usize,
+ actual: usize,
+ },
+ CanonicalEventId(RadrootsCanonicalEventIdError),
+ EventIdMismatch {
+ declared: String,
+ computed: String,
+ },
+}
+
+impl fmt::Display for RadrootsEventWireError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Json(error) => write!(f, "event wire json is invalid: {error}"),
+ Self::RootNotObject => write!(f, "event wire root must be a JSON object"),
+ Self::MissingField(field) => write!(f, "event wire missing required field {field}"),
+ Self::InvalidField(field) => write!(f, "event wire field {field} is invalid"),
+ Self::InvalidIdentifier { field, error } => {
+ write!(f, "event wire field {field} is invalid: {error}")
+ }
+ Self::NonCanonicalIdentifier { field } => {
+ write!(
+ f,
+ "event wire field {field} must be canonical lowercase hex"
+ )
+ }
+ Self::RawJsonTooLarge { max, actual } => {
+ write!(f, "event wire raw JSON size {actual} exceeds {max} bytes")
+ }
+ Self::ContentTooLarge { max, actual } => {
+ write!(f, "event wire content size {actual} exceeds {max} bytes")
+ }
+ Self::TooManyTags { max, actual } => {
+ write!(f, "event wire tag count {actual} exceeds {max}")
+ }
+ Self::EmptyTag { index } => write!(f, "event wire tag {index} is empty"),
+ Self::EmptyTagKey { index } => write!(f, "event wire tag {index} key is empty"),
+ Self::ControlCharacterTagKey { index } => {
+ write!(f, "event wire tag {index} key contains a control character")
+ }
+ Self::TagElementTooLarge {
+ tag_index,
+ element_index,
+ max,
+ actual,
+ } => write!(
+ f,
+ "event wire tag {tag_index} element {element_index} size {actual} exceeds {max} bytes"
+ ),
+ Self::TagsTooLarge { max, actual } => {
+ write!(f, "event wire tag bytes {actual} exceed {max}")
+ }
+ Self::TooManyExtraFields { max, actual } => {
+ write!(f, "event wire extra field count {actual} exceeds {max}")
+ }
+ Self::ExtraJsonTooLarge { max, actual } => {
+ write!(f, "event wire extra JSON bytes {actual} exceed {max}")
+ }
+ Self::CanonicalEventId(error) => write!(f, "{error}"),
+ Self::EventIdMismatch { declared, computed } => write!(
+ f,
+ "event wire id mismatch: declared {declared}, computed {computed}"
+ ),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for RadrootsEventWireError {}
+
+impl From<RadrootsCanonicalEventIdError> for RadrootsEventWireError {
+ fn from(value: RadrootsCanonicalEventIdError) -> Self {
+ Self::CanonicalEventId(value)
+ }
+}
+
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct RadrootsNip01EventWire {
+ pub id: String,
+ pub pubkey: String,
+ pub created_at: u64,
+ pub kind: u32,
+ pub tags: Vec<Vec<String>>,
+ pub content: String,
+ pub sig: String,
+ #[cfg_attr(feature = "serde", serde(flatten))]
+ pub extra: BTreeMap<String, Value>,
+}
+
+impl RadrootsNip01EventWire {
+ pub fn parse_json(raw_json: &str) -> Result<Self, RadrootsEventWireError> {
+ Self::parse_json_with_limits(raw_json, RadrootsEventWireLimits::default())
+ }
+
+ pub fn parse_json_with_limits(
+ raw_json: &str,
+ limits: RadrootsEventWireLimits,
+ ) -> Result<Self, RadrootsEventWireError> {
+ let raw_len = raw_json.len();
+ if raw_len > limits.max_raw_json_bytes {
+ return Err(RadrootsEventWireError::RawJsonTooLarge {
+ max: limits.max_raw_json_bytes,
+ actual: raw_len,
+ });
+ }
+ let value = serde_json::from_str::<Value>(raw_json)
+ .map_err(|error| RadrootsEventWireError::Json(error.to_string()))?;
+ Self::from_json_value(value, limits)
+ }
+
+ pub fn canonical_id_preimage(&self) -> Result<String, RadrootsCanonicalEventIdError> {
+ canonical_nip01_event_id_preimage(
+ self.pubkey.as_str(),
+ self.created_at,
+ self.kind,
+ &self.tags,
+ self.content.as_str(),
+ )
+ }
+
+ pub fn computed_event_id(&self) -> Result<RadrootsEventId, RadrootsCanonicalEventIdError> {
+ compute_canonical_nip01_event_id(
+ self.pubkey.as_str(),
+ self.created_at,
+ self.kind,
+ &self.tags,
+ self.content.as_str(),
+ )
+ }
+
+ pub fn verify_id(&self) -> Result<(), RadrootsEventWireError> {
+ let computed = self.computed_event_id()?.into_string();
+ if computed.as_str() != self.id.as_str() {
+ return Err(RadrootsEventWireError::EventIdMismatch {
+ declared: self.id.clone(),
+ computed,
+ });
+ }
+ Ok(())
+ }
+
+ fn from_json_value(
+ value: Value,
+ limits: RadrootsEventWireLimits,
+ ) -> Result<Self, RadrootsEventWireError> {
+ let mut object = match value {
+ Value::Object(object) => object,
+ _ => return Err(RadrootsEventWireError::RootNotObject),
+ };
+ let id = take_canonical_event_id(&mut object)?;
+ let pubkey = take_canonical_pubkey(&mut object)?;
+ let created_at = take_u64(&mut object, "created_at")?;
+ let kind = take_u32(&mut object, "kind")?;
+ let tags = take_tags(&mut object, limits)?;
+ let content = take_string(&mut object, "content")?;
+ let content_len = content.len();
+ if content_len > limits.max_content_bytes {
+ return Err(RadrootsEventWireError::ContentTooLarge {
+ max: limits.max_content_bytes,
+ actual: content_len,
+ });
+ }
+ let sig = take_canonical_signature(&mut object)?;
+ let extra = validate_extra(object, limits)?;
+ let wire = Self {
+ id,
+ pubkey,
+ created_at,
+ kind,
+ tags,
+ content,
+ sig,
+ extra,
+ };
+ wire.verify_id()?;
+ Ok(wire)
+ }
+}
+
+pub fn canonical_nip01_event_id_preimage(
+ pubkey: &str,
+ created_at: u64,
+ kind: u32,
+ tags: &[Vec<String>],
+ content: &str,
+) -> Result<String, RadrootsCanonicalEventIdError> {
+ let pubkey =
+ RadrootsPublicKey::parse(pubkey).map_err(RadrootsCanonicalEventIdError::InvalidPubkey)?;
+ let mut preimage = String::new();
+ preimage.push_str("[0,");
+ push_canonical_json_string(&mut preimage, pubkey.as_str());
+ preimage.push(',');
+ preimage.push_str(created_at.to_string().as_str());
+ preimage.push(',');
+ preimage.push_str(kind.to_string().as_str());
+ preimage.push_str(",[");
+ for (tag_index, tag) in tags.iter().enumerate() {
+ if tag_index > 0 {
+ preimage.push(',');
+ }
+ preimage.push('[');
+ for (value_index, value) in tag.iter().enumerate() {
+ if value_index > 0 {
+ preimage.push(',');
+ }
+ push_canonical_json_string(&mut preimage, value);
+ }
+ preimage.push(']');
+ }
+ preimage.push_str("],");
+ push_canonical_json_string(&mut preimage, content);
+ preimage.push(']');
+ Ok(preimage)
+}
+
+pub fn compute_canonical_nip01_event_id(
+ pubkey: &str,
+ created_at: u64,
+ kind: u32,
+ tags: &[Vec<String>],
+ content: &str,
+) -> Result<RadrootsEventId, RadrootsCanonicalEventIdError> {
+ let preimage = canonical_nip01_event_id_preimage(pubkey, created_at, kind, tags, content)?;
+ let digest = Sha256::digest(preimage.as_bytes());
+ let event_id = hex::encode(digest);
+ RadrootsEventId::parse(event_id).map_err(RadrootsCanonicalEventIdError::InvalidComputedEventId)
+}
+
+fn take_string(
+ object: &mut Map<String, Value>,
+ field: &'static str,
+) -> Result<String, RadrootsEventWireError> {
+ match object.remove(field) {
+ Some(Value::String(value)) => Ok(value),
+ Some(_) => Err(RadrootsEventWireError::InvalidField(field)),
+ None => Err(RadrootsEventWireError::MissingField(field)),
+ }
+}
+
+fn take_canonical_event_id(
+ object: &mut Map<String, Value>,
+) -> Result<String, RadrootsEventWireError> {
+ let raw = take_string(object, "id")?;
+ let parsed = RadrootsEventId::parse(raw.as_str())
+ .map_err(|error| RadrootsEventWireError::InvalidIdentifier { field: "id", error })?;
+ canonical_identifier_string("id", raw, parsed.into_string())
+}
+
+fn take_canonical_pubkey(
+ object: &mut Map<String, Value>,
+) -> Result<String, RadrootsEventWireError> {
+ let raw = take_string(object, "pubkey")?;
+ let parsed = RadrootsPublicKey::parse(raw.as_str()).map_err(|error| {
+ RadrootsEventWireError::InvalidIdentifier {
+ field: "pubkey",
+ error,
+ }
+ })?;
+ canonical_identifier_string("pubkey", raw, parsed.into_string())
+}
+
+fn take_canonical_signature(
+ object: &mut Map<String, Value>,
+) -> Result<String, RadrootsEventWireError> {
+ let raw = take_string(object, "sig")?;
+ let parsed = RadrootsEventSignature::parse(raw.as_str()).map_err(|error| {
+ RadrootsEventWireError::InvalidIdentifier {
+ field: "sig",
+ error,
+ }
+ })?;
+ canonical_identifier_string("sig", raw, parsed.into_string())
+}
+
+fn canonical_identifier_string(
+ field: &'static str,
+ raw: String,
+ canonical: String,
+) -> Result<String, RadrootsEventWireError> {
+ if canonical.as_str() != raw.as_str() {
+ return Err(RadrootsEventWireError::NonCanonicalIdentifier { field });
+ }
+ Ok(canonical)
+}
+
+fn take_u64(
+ object: &mut Map<String, Value>,
+ field: &'static str,
+) -> Result<u64, RadrootsEventWireError> {
+ match object.remove(field) {
+ Some(Value::Number(value)) => value
+ .as_u64()
+ .ok_or(RadrootsEventWireError::InvalidField(field)),
+ Some(_) => Err(RadrootsEventWireError::InvalidField(field)),
+ None => Err(RadrootsEventWireError::MissingField(field)),
+ }
+}
+
+fn take_u32(
+ object: &mut Map<String, Value>,
+ field: &'static str,
+) -> Result<u32, RadrootsEventWireError> {
+ let value = take_u64(object, field)?;
+ u32::try_from(value).map_err(|_| RadrootsEventWireError::InvalidField(field))
+}
+
+fn take_tags(
+ object: &mut Map<String, Value>,
+ limits: RadrootsEventWireLimits,
+) -> Result<Vec<Vec<String>>, RadrootsEventWireError> {
+ let raw_tags = match object.remove("tags") {
+ Some(Value::Array(raw_tags)) => raw_tags,
+ Some(_) => return Err(RadrootsEventWireError::InvalidField("tags")),
+ None => return Err(RadrootsEventWireError::MissingField("tags")),
+ };
+ let tag_count = raw_tags.len();
+ if tag_count > limits.max_tag_count {
+ return Err(RadrootsEventWireError::TooManyTags {
+ max: limits.max_tag_count,
+ actual: tag_count,
+ });
+ }
+ let mut total_tag_bytes = 0usize;
+ let mut tags = Vec::with_capacity(tag_count);
+ for (tag_index, raw_tag) in raw_tags.into_iter().enumerate() {
+ let raw_values = match raw_tag {
+ Value::Array(values) => values,
+ _ => return Err(RadrootsEventWireError::InvalidField("tags")),
+ };
+ if raw_values.is_empty() {
+ return Err(RadrootsEventWireError::EmptyTag { index: tag_index });
+ }
+ let mut tag = Vec::with_capacity(raw_values.len());
+ for (element_index, raw_value) in raw_values.into_iter().enumerate() {
+ let value = match raw_value {
+ Value::String(value) => value,
+ _ => return Err(RadrootsEventWireError::InvalidField("tags")),
+ };
+ let value_len = value.len();
+ if value_len > limits.max_tag_element_bytes {
+ return Err(RadrootsEventWireError::TagElementTooLarge {
+ tag_index,
+ element_index,
+ max: limits.max_tag_element_bytes,
+ actual: value_len,
+ });
+ }
+ if element_index == 0 {
+ validate_tag_key(tag_index, value.as_str())?;
+ }
+ total_tag_bytes = total_tag_bytes.saturating_add(value_len);
+ if total_tag_bytes > limits.max_total_tag_bytes {
+ return Err(RadrootsEventWireError::TagsTooLarge {
+ max: limits.max_total_tag_bytes,
+ actual: total_tag_bytes,
+ });
+ }
+ tag.push(value);
+ }
+ tags.push(tag);
+ }
+ Ok(tags)
+}
+
+fn validate_tag_key(index: usize, value: &str) -> Result<(), RadrootsEventWireError> {
+ if value.is_empty() {
+ return Err(RadrootsEventWireError::EmptyTagKey { index });
+ }
+ if value.chars().any(char::is_control) {
+ return Err(RadrootsEventWireError::ControlCharacterTagKey { index });
+ }
+ Ok(())
+}
+
+fn validate_extra(
+ object: Map<String, Value>,
+ limits: RadrootsEventWireLimits,
+) -> Result<BTreeMap<String, Value>, RadrootsEventWireError> {
+ let extra_count = object.len();
+ if extra_count > limits.max_extra_fields {
+ return Err(RadrootsEventWireError::TooManyExtraFields {
+ max: limits.max_extra_fields,
+ actual: extra_count,
+ });
+ }
+ let mut total_json_bytes = 0usize;
+ let mut extra = BTreeMap::new();
+ for (key, value) in object {
+ let key_json_len = serde_json::to_vec(&key)
+ .map_err(|error| RadrootsEventWireError::Json(error.to_string()))?
+ .len();
+ let value_json_len = serde_json::to_vec(&value)
+ .map_err(|error| RadrootsEventWireError::Json(error.to_string()))?
+ .len();
+ total_json_bytes = total_json_bytes
+ .saturating_add(key_json_len)
+ .saturating_add(1)
+ .saturating_add(value_json_len);
+ if total_json_bytes > limits.max_total_extra_json_bytes {
+ return Err(RadrootsEventWireError::ExtraJsonTooLarge {
+ max: limits.max_total_extra_json_bytes,
+ actual: total_json_bytes,
+ });
+ }
+ extra.insert(key, value);
+ }
+ Ok(extra)
+}
+
+fn push_canonical_json_string(target: &mut String, value: &str) {
+ target.push('"');
+ for character in value.chars() {
+ match character {
+ '"' => target.push_str("\\\""),
+ '\\' => target.push_str("\\\\"),
+ '\n' => target.push_str("\\n"),
+ '\r' => target.push_str("\\r"),
+ '\t' => target.push_str("\\t"),
+ '\u{08}' => target.push_str("\\b"),
+ '\u{0c}' => target.push_str("\\f"),
+ '\u{00}'..='\u{1f}' => push_unicode_escape(target, character),
+ _ => target.push(character),
+ }
+ }
+ target.push('"');
+}
+
+fn push_unicode_escape(target: &mut String, character: char) {
+ const HEX: &[u8; 16] = b"0123456789abcdef";
+ let value = character as u32;
+ target.push_str("\\u00");
+ target.push(HEX[((value >> 4) & 0x0f) as usize] as char);
+ target.push(HEX[(value & 0x0f) as usize] as char);
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde_json::json;
+
+ fn hex_64(character: char) -> String {
+ core::iter::repeat_n(character, 64).collect()
+ }
+
+ fn hex_128(character: char) -> String {
+ core::iter::repeat_n(character, 128).collect()
+ }
+
+ fn valid_event_value(content: &str, tags: Vec<Vec<String>>) -> Value {
+ let pubkey = hex_64('a');
+ let id =
+ compute_canonical_nip01_event_id(pubkey.as_str(), 1_700_000_000, 1, &tags, content)
+ .expect("event id")
+ .into_string();
+ json!({
+ "id": id,
+ "pubkey": pubkey,
+ "created_at": 1_700_000_000u64,
+ "kind": 1u32,
+ "tags": tags,
+ "content": content,
+ "sig": hex_128('b')
+ })
+ }
+
+ fn raw_json(value: &Value) -> String {
+ serde_json::to_string(value).expect("event json")
+ }
+
+ fn valid_event_json(content: &str, tags: Vec<Vec<String>>) -> String {
+ raw_json(&valid_event_value(content, tags))
+ }
+
+ fn default_tags() -> Vec<Vec<String>> {
+ vec![vec!["t".to_owned(), "soil".to_owned()]]
+ }
+
+ #[test]
+ fn canonical_preimage_escapes_required_json_characters() {
+ let preimage = canonical_nip01_event_id_preimage(
+ hex_64('a').as_str(),
+ 10,
+ 1,
+ &[vec!["t".to_owned(), "line\nsoil".to_owned()]],
+ "\"\\\n\r\t\u{08}\u{0c}\u{01}",
+ )
+ .expect("preimage");
+
+ assert_eq!(
+ preimage,
+ r#"[0,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",10,1,[["t","line\nsoil"]],"\"\\\n\r\t\b\f\u0001"]"#
+ );
+ }
+
+ #[test]
+ fn parses_wire_json_preserves_extra_and_verifies_id() {
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("client".to_owned(), json!({"name":"radroots-test"}));
+ let wire = RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()).expect("wire");
+
+ assert_eq!(wire.pubkey, hex_64('a'));
+ assert_eq!(wire.created_at, 1_700_000_000);
+ assert_eq!(wire.kind, 1);
+ assert_eq!(wire.tags, default_tags());
+ assert_eq!(wire.content, "hello");
+ assert_eq!(
+ wire.extra.get("client").expect("client extra"),
+ &json!({"name":"radroots-test"})
+ );
+ wire.verify_id().expect("verified id");
+ assert_eq!(
+ wire.canonical_id_preimage().expect("preimage"),
+ r#"[0,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1700000000,1,[["t","soil"]],"hello"]"#
+ );
+ }
+
+ #[cfg(feature = "serde")]
+ #[test]
+ fn serde_flatten_preserves_extra_fields() {
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("client".to_owned(), json!("radroots-test"));
+ let wire = RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()).expect("wire");
+ let encoded = serde_json::to_value(&wire).expect("encoded");
+
+ assert_eq!(encoded.get("client"), Some(&json!("radroots-test")));
+ assert_eq!(encoded.get("id"), Some(&Value::String(wire.id)));
+ }
+
+ #[test]
+ fn parse_json_rejects_required_field_errors_and_id_mismatch() {
+ let mut value = valid_event_value("hello", default_tags());
+ value.as_object_mut().expect("object").remove("id");
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::MissingField("id"))
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("pubkey".to_owned(), json!("not-hex"));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::InvalidIdentifier {
+ field: "pubkey",
+ ..
+ })
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("sig".to_owned(), json!(hex_64('b')));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::InvalidIdentifier { field: "sig", .. })
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("id".to_owned(), json!(hex_64('f')));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::EventIdMismatch { .. })
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("id".to_owned(), json!(hex_64('A')));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::NonCanonicalIdentifier { field: "id" })
+ ));
+ }
+
+ #[test]
+ fn parse_json_rejects_tag_shape_errors() {
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("tags".to_owned(), json!([[]]));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::EmptyTag { index: 0 })
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("tags".to_owned(), json!([["", "soil"]]));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::EmptyTagKey { index: 0 })
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("tags".to_owned(), json!([["t\n", "soil"]]));
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json(raw_json(&value).as_str()),
+ Err(RadrootsEventWireError::ControlCharacterTagKey { index: 0 })
+ ));
+ }
+
+ #[test]
+ fn parse_json_rejects_resource_budget_violations() {
+ let raw = valid_event_json("hello", default_tags());
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_raw_json_bytes: 1,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::RawJsonTooLarge { .. })
+ ));
+
+ let raw = valid_event_json("hello", default_tags());
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_content_bytes: 1,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::ContentTooLarge { .. })
+ ));
+
+ let raw = valid_event_json("hello", default_tags());
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_tag_count: 0,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::TooManyTags { .. })
+ ));
+
+ let raw = valid_event_json("hello", vec![vec!["t".to_owned(), "soil".to_owned()]]);
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_tag_element_bytes: 1,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::TagElementTooLarge { .. })
+ ));
+
+ let raw = valid_event_json("hello", default_tags());
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_total_tag_bytes: 1,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::TagsTooLarge { .. })
+ ));
+
+ let mut value = valid_event_value("hello", default_tags());
+ value
+ .as_object_mut()
+ .expect("object")
+ .insert("client".to_owned(), json!("radroots-test"));
+ let raw = raw_json(&value);
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_extra_fields: 0,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::TooManyExtraFields { .. })
+ ));
+
+ assert!(matches!(
+ RadrootsNip01EventWire::parse_json_with_limits(
+ raw.as_str(),
+ RadrootsEventWireLimits {
+ max_total_extra_json_bytes: 1,
+ ..RadrootsEventWireLimits::default()
+ }
+ ),
+ Err(RadrootsEventWireError::ExtraJsonTooLarge { .. })
+ ));
+ }
+
+ #[test]
+ fn checked_in_conformance_vectors_match_wire_behavior() {
+ let vectors =
+ include_str!("../../../contracts/conformance/vectors/event/nip01_wire.v1.json");
+ let document: Value = serde_json::from_str(vectors).expect("vectors json");
+ let entries = document
+ .get("vectors")
+ .and_then(Value::as_array)
+ .expect("vector entries");
+
+ for entry in entries {
+ match entry.get("kind").and_then(Value::as_str).expect("kind") {
+ "event.nip01_wire.valid" => {
+ let raw = entry
+ .get("input")
+ .and_then(|input| input.get("raw_json"))
+ .and_then(Value::as_str)
+ .expect("raw json");
+ let expected = entry.get("expected").expect("expected");
+ let wire = RadrootsNip01EventWire::parse_json(raw).expect("wire");
+ assert_eq!(
+ wire.canonical_id_preimage().expect("preimage"),
+ expected
+ .get("canonical_id_preimage")
+ .and_then(Value::as_str)
+ .expect("expected preimage")
+ );
+ assert_eq!(
+ wire.computed_event_id().expect("event id").as_str(),
+ expected
+ .get("computed_event_id")
+ .and_then(Value::as_str)
+ .expect("expected event id")
+ );
+ }
+ "event.nip01_wire.invalid" => {
+ let raw = entry
+ .get("input")
+ .and_then(|input| input.get("raw_json"))
+ .and_then(Value::as_str)
+ .expect("raw json");
+ assert!(RadrootsNip01EventWire::parse_json(raw).is_err());
+ }
+ other => panic!("unknown event wire vector kind {other}"),
+ }
+ }
+ }
+}