encode.rs (5400B)
1 //! Deterministic event encoding boundary. 2 3 #[cfg(all(not(feature = "std"), feature = "json"))] 4 use alloc::string::String; 5 use core::fmt; 6 #[cfg(all(feature = "std", feature = "json"))] 7 use std::string::String; 8 9 #[cfg(feature = "json")] 10 use radroots_event::envelope::EventEnvelope; 11 12 // Domain-specific algorithms live behind the canonical encoding namespace. 13 // Their implementation modules remain crate-private so the public API has one 14 // stable route for each operation. 15 pub use crate::error::{EventEncodeError, RadrootsEncodeError}; 16 17 macro_rules! encode_domain { 18 ($name:ident, $source:path) => { 19 pub mod $name { 20 pub use $source::*; 21 } 22 }; 23 } 24 25 encode_domain!(app_data, crate::app_data::encode); 26 encode_domain!(article, crate::article::encode); 27 encode_domain!(calendar, crate::calendar::encode); 28 pub mod coop { 29 pub use crate::coop::encode::*; 30 pub use crate::coop::list_sets::*; 31 } 32 encode_domain!(document, crate::document::encode); 33 encode_domain!(farm_crdt, crate::farm_crdt::encode); 34 encode_domain!(farm_file, crate::farm_file::encode); 35 encode_domain!(farm_workspace, crate::farm_workspace::encode); 36 encode_domain!(file_metadata, crate::file_metadata::encode); 37 encode_domain!(follow, crate::follow::encode); 38 encode_domain!(geochat, crate::geochat::encode); 39 encode_domain!(gift_wrap, crate::gift_wrap::encode); 40 encode_domain!(group, crate::group::encode); 41 encode_domain!(http_auth, crate::http_auth::encode); 42 encode_domain!(list, crate::list::encode); 43 encode_domain!(list_set, crate::list_set::encode); 44 encode_domain!(message, crate::message::encode); 45 encode_domain!(message_file, crate::message_file::encode); 46 encode_domain!(plot, crate::plot::encode); 47 encode_domain!(reaction, crate::reaction::encode); 48 encode_domain!(relay_auth, crate::relay_auth::encode); 49 #[cfg(feature = "json")] 50 encode_domain!(relay_document, crate::relay_document::encode); 51 encode_domain!(report, crate::report::encode); 52 encode_domain!(repost, crate::repost::encode); 53 pub mod resource_area { 54 pub use crate::resource_area::encode::*; 55 pub use crate::resource_area::list_sets::*; 56 } 57 encode_domain!(resource_cap, crate::resource_cap::encode); 58 encode_domain!(seal, crate::seal::encode); 59 60 pub mod comment { 61 pub use crate::comment::authored::*; 62 } 63 64 pub mod deletion { 65 pub use crate::deletion::authored::*; 66 } 67 68 pub mod farm { 69 pub use crate::farm::encode::*; 70 pub use crate::farm::list_sets::*; 71 } 72 73 pub mod food_availability { 74 pub use crate::food_availability::authored::*; 75 } 76 77 pub mod job { 78 pub use crate::job::encode::*; 79 80 pub mod feedback { 81 pub use crate::job::feedback::encode::*; 82 } 83 pub mod request { 84 pub use crate::job::request::encode::*; 85 } 86 pub mod result { 87 pub use crate::job::result::encode::*; 88 } 89 } 90 91 pub mod tag_builders { 92 pub use crate::tag_builders::*; 93 } 94 95 #[cfg(feature = "knowledge")] 96 pub mod knowledge { 97 pub use crate::knowledge::encode::*; 98 } 99 100 pub mod operational_listing { 101 pub use crate::operational_listing::encode::*; 102 pub use crate::operational_listing::tags::*; 103 } 104 105 pub mod order { 106 #[cfg(feature = "json")] 107 pub use crate::order::encode::*; 108 pub use crate::order::tags::*; 109 } 110 111 pub mod post { 112 pub use crate::post::authored::*; 113 } 114 115 #[cfg(feature = "json")] 116 pub mod profile { 117 pub use crate::profile::authored::*; 118 } 119 120 pub mod reply { 121 pub use crate::reply::authored::*; 122 } 123 124 #[cfg(feature = "json")] 125 pub mod rhi { 126 pub use crate::rhi::{ 127 RADROOTS_RHI_EVIDENCE_ATTESTATION_CONTRACT_ID, 128 RADROOTS_RHI_EVIDENCE_ATTESTATION_MAXIMUM_BYTES, RadrootsRhiEvidenceAttestationError, 129 RadrootsRhiEvidenceAttestationV1, rhi_evidence_attestation_event_build, 130 rhi_evidence_attestation_event_build_with_extra_tags, 131 }; 132 } 133 134 #[cfg(feature = "json")] 135 pub mod trade { 136 pub use crate::trade::{ 137 RadrootsTradeMutationError, trade_mutation_event_build, 138 trade_mutation_event_build_with_extra_tags, trade_mutation_tags, 139 }; 140 } 141 142 /// A failure while encoding an event. 143 #[non_exhaustive] 144 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 145 pub enum EncodeError { 146 /// The event could not be represented as canonical compact JSON. 147 Json, 148 } 149 150 impl EncodeError { 151 /// Returns a stable machine-readable error code. 152 #[must_use] 153 pub const fn code(self) -> &'static str { 154 match self { 155 Self::Json => "json", 156 } 157 } 158 } 159 160 impl fmt::Display for EncodeError { 161 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 162 formatter.write_str("failed to encode event JSON") 163 } 164 } 165 166 #[cfg(feature = "std")] 167 impl std::error::Error for EncodeError {} 168 169 /// Encodes an event as deterministic compact NIP-01 JSON. 170 /// 171 /// This operation preserves the supplied envelope; it does not imply that the 172 /// identifier, signature, or contract has been verified. 173 /// 174 /// ```no_run 175 /// # #[cfg(feature = "json")] 176 /// # { 177 /// # let raw_json = r#"{"id":"00","pubkey":"00","created_at":0,"kind":1,"tags":[],"content":"","sig":"00"}"#; 178 /// let raw = radroots_event_codec::decode::event(raw_json)?; 179 /// let encoded = radroots_event_codec::encode::event(raw.event())?; 180 /// # let _ = encoded; 181 /// # } 182 /// # Ok::<(), Box<dyn std::error::Error>>(()) 183 /// ``` 184 #[cfg(feature = "json")] 185 pub fn event(event: &EventEnvelope) -> Result<String, EncodeError> { 186 serde_json::to_string(&event.to_nip01_wire()).map_err(|_| EncodeError::Json) 187 }