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