farm_workspace.rs (5376B)
1 #![forbid(unsafe_code)] 2 3 use crate::envelope::kind::KIND_FARM_WORKSPACE_MANIFEST as KIND_FARM_WORKSPACE_MANIFEST_EVENT; 4 use crate::farm::FarmRef; 5 6 #[cfg(not(feature = "std"))] 7 use alloc::{string::String, vec::Vec}; 8 9 pub const KIND_FARM_WORKSPACE_MANIFEST: u32 = KIND_FARM_WORKSPACE_MANIFEST_EVENT; 10 pub const RADROOTS_FARM_WORKSPACE_SCHEMA: &str = "radroots.farm.workspace.v1"; 11 pub const RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION: &str = "field-nostr-v1"; 12 pub const RADROOTS_FARM_WORKSPACE_TAG: &str = "radroots:farm:workspace"; 13 14 #[cfg_attr( 15 any(feature = "serde", test), 16 derive(serde::Serialize, serde::Deserialize) 17 )] 18 #[derive(Clone, Debug)] 19 pub struct FarmWorkspaceManifest { 20 pub d_tag: String, 21 pub schema: String, 22 pub farm_group_id: String, 23 pub name: String, 24 pub owner_pubkey: String, 25 pub farm: Option<FarmRef>, 26 pub relays: Vec<FarmWorkspaceRelay>, 27 pub media_servers: Vec<FarmWorkspaceMediaServer>, 28 pub supported_kinds: Vec<u32>, 29 pub protocol_version: String, 30 pub created_at_ms: u64, 31 pub updated_at_ms: Option<u64>, 32 } 33 34 #[cfg_attr( 35 any(feature = "serde", test), 36 derive(serde::Serialize, serde::Deserialize) 37 )] 38 #[derive(Clone, Debug, Default, PartialEq, Eq)] 39 pub struct FarmWorkspaceRef { 40 pub pubkey: String, 41 pub d_tag: String, 42 } 43 44 #[cfg_attr( 45 any(feature = "serde", test), 46 derive(serde::Serialize, serde::Deserialize) 47 )] 48 #[derive(Clone, Debug, PartialEq, Eq)] 49 pub struct FarmWorkspaceRelay { 50 pub url: String, 51 pub mode: FarmWorkspaceRelayMode, 52 } 53 54 #[cfg_attr( 55 any(feature = "serde", test), 56 derive(serde::Serialize, serde::Deserialize) 57 )] 58 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 59 pub enum FarmWorkspaceRelayMode { 60 Read, 61 Write, 62 ReadWrite, 63 } 64 65 #[cfg_attr( 66 any(feature = "serde", test), 67 derive(serde::Serialize, serde::Deserialize) 68 )] 69 #[derive(Clone, Debug, PartialEq, Eq)] 70 pub struct FarmWorkspaceMediaServer { 71 pub url: String, 72 pub service: String, 73 } 74 75 #[cfg(all(test, feature = "serde"))] 76 #[cfg_attr(coverage_nightly, coverage(off))] 77 mod tests { 78 use super::*; 79 use crate::envelope::kind::{ 80 KIND_APP_DATA, KIND_FARM_CRDT_CHANGE, KIND_FARM_FILE_METADATA, 81 KIND_FARM_WORKSPACE_MANIFEST as KIND_FARM_WORKSPACE_MANIFEST_KIND, KIND_HTTP_AUTH, 82 KIND_RELAY_AUTH, 83 }; 84 85 #[test] 86 fn manifest_kind_aliases_existing_app_data_kind() { 87 assert_eq!(KIND_FARM_WORKSPACE_MANIFEST, KIND_APP_DATA); 88 assert_eq!(KIND_FARM_WORKSPACE_MANIFEST_KIND, KIND_APP_DATA); 89 } 90 91 #[test] 92 fn manifest_represents_required_workspace_contract_fields() { 93 let manifest = sample_manifest(); 94 95 assert_eq!(manifest.d_tag, "ABCDEFGHIJKLMNOPQRSTUV"); 96 assert_eq!(manifest.schema, RADROOTS_FARM_WORKSPACE_SCHEMA); 97 assert_eq!(manifest.farm_group_id, "BCDEFGHIJKLMNOPQRSTUVW"); 98 assert_eq!(manifest.relays.len(), 1); 99 assert_eq!(manifest.media_servers.len(), 1); 100 assert!(manifest.supported_kinds.contains(&KIND_FARM_CRDT_CHANGE)); 101 assert!( 102 manifest 103 .supported_kinds 104 .contains(&KIND_FARM_WORKSPACE_MANIFEST) 105 ); 106 assert_eq!(manifest.created_at_ms, 1_780_000_000_000); 107 assert_eq!(manifest.updated_at_ms, None); 108 } 109 110 #[test] 111 fn manifest_serializes_stable_content_shape() { 112 let value = serde_json::to_value(sample_manifest()).unwrap(); 113 114 assert_eq!(value["schema"], RADROOTS_FARM_WORKSPACE_SCHEMA); 115 assert_eq!( 116 value["protocol_version"], 117 RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION 118 ); 119 assert_eq!(value["farm"]["d_tag"], "CDEFGHIJKLMNOPQRSTUVWX"); 120 assert_eq!(value["relays"][0]["mode"], "ReadWrite"); 121 assert_eq!(value["media_servers"][0]["service"], "RadrootsPrivateMedia"); 122 assert_eq!(value["supported_kinds"][0], KIND_FARM_CRDT_CHANGE); 123 assert_eq!(value["supported_kinds"][1], KIND_FARM_WORKSPACE_MANIFEST); 124 } 125 126 fn sample_manifest() -> FarmWorkspaceManifest { 127 FarmWorkspaceManifest { 128 d_tag: "ABCDEFGHIJKLMNOPQRSTUV".to_string(), 129 schema: RADROOTS_FARM_WORKSPACE_SCHEMA.to_string(), 130 farm_group_id: "BCDEFGHIJKLMNOPQRSTUVW".to_string(), 131 name: "Small Regen Farm".to_string(), 132 owner_pubkey: "owner_pubkey".to_string(), 133 farm: Some(FarmRef { 134 pubkey: "farm_pubkey".to_string(), 135 d_tag: "CDEFGHIJKLMNOPQRSTUVWX".to_string(), 136 }), 137 relays: vec![FarmWorkspaceRelay { 138 url: "wss://relay.example.invalid/farm/ABCDEFGHIJKLMNOPQRSTUV".to_string(), 139 mode: FarmWorkspaceRelayMode::ReadWrite, 140 }], 141 media_servers: vec![FarmWorkspaceMediaServer { 142 url: "https://media.example.invalid/farm/ABCDEFGHIJKLMNOPQRSTUV".to_string(), 143 service: "RadrootsPrivateMedia".to_string(), 144 }], 145 supported_kinds: vec![ 146 KIND_FARM_CRDT_CHANGE, 147 KIND_FARM_WORKSPACE_MANIFEST, 148 KIND_FARM_FILE_METADATA, 149 KIND_RELAY_AUTH, 150 KIND_HTTP_AUTH, 151 ], 152 protocol_version: RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION.to_string(), 153 created_at_ms: 1_780_000_000_000, 154 updated_at_ms: None, 155 } 156 } 157 }