list_sets.rs (9318B)
1 #![forbid(unsafe_code)] 2 3 #[cfg(not(feature = "std"))] 4 use alloc::{ 5 format, 6 string::{String, ToString}, 7 vec, 8 vec::Vec, 9 }; 10 11 use radroots_event::envelope::kind::KIND_CLASSIFIED_LISTING; 12 use radroots_event::farm::plot::Plot; 13 use radroots_event::listing::operational::OperationalListing; 14 use radroots_event::social::list::ListEntry; 15 use radroots_event::social::list_set::ListSet; 16 17 use crate::d_tag::validate_d_tag; 18 use crate::error::EventEncodeError; 19 use crate::plot::encode::plot_address; 20 21 const MEMBER_OF_FARMS: &str = "member_of.farms"; 22 23 fn farm_list_set_id(farm_id: &str, suffix: &str) -> Result<String, EventEncodeError> { 24 let farm_id = farm_id.trim(); 25 if farm_id.is_empty() { 26 return Err(EventEncodeError::EmptyRequiredField("farm_id")); 27 } 28 validate_d_tag(farm_id, "farm_id")?; 29 Ok(format!("farm:{farm_id}:{suffix}")) 30 } 31 32 fn list_entries<I, S>(tag: &str, values: I) -> Result<Vec<ListEntry>, EventEncodeError> 33 where 34 I: IntoIterator<Item = S>, 35 S: AsRef<str>, 36 { 37 let mut entries = Vec::new(); 38 for value in values { 39 let value = value.as_ref().trim(); 40 if value.is_empty() { 41 return Err(EventEncodeError::EmptyRequiredField("entry.values")); 42 } 43 entries.push(ListEntry { 44 tag: tag.to_string(), 45 values: vec![value.to_string()], 46 }); 47 } 48 Ok(entries) 49 } 50 51 pub fn farm_members_list_set<I, S>(farm_id: &str, members: I) -> Result<ListSet, EventEncodeError> 52 where 53 I: IntoIterator<Item = S>, 54 S: AsRef<str>, 55 { 56 Ok(ListSet { 57 d_tag: farm_list_set_id(farm_id, "members")?, 58 content: String::new(), 59 entries: list_entries("p", members)?, 60 title: None, 61 description: None, 62 image: None, 63 }) 64 } 65 66 pub fn farm_owners_list_set<I, S>(farm_id: &str, owners: I) -> Result<ListSet, EventEncodeError> 67 where 68 I: IntoIterator<Item = S>, 69 S: AsRef<str>, 70 { 71 Ok(ListSet { 72 d_tag: farm_list_set_id(farm_id, "members.owners")?, 73 content: String::new(), 74 entries: list_entries("p", owners)?, 75 title: None, 76 description: None, 77 image: None, 78 }) 79 } 80 81 pub fn farm_workers_list_set<I, S>(farm_id: &str, workers: I) -> Result<ListSet, EventEncodeError> 82 where 83 I: IntoIterator<Item = S>, 84 S: AsRef<str>, 85 { 86 Ok(ListSet { 87 d_tag: farm_list_set_id(farm_id, "members.workers")?, 88 content: String::new(), 89 entries: list_entries("p", workers)?, 90 title: None, 91 description: None, 92 image: None, 93 }) 94 } 95 96 pub fn farm_plots_list_set<I, S>( 97 farm_id: &str, 98 farm_pubkey: &str, 99 plot_ids: I, 100 ) -> Result<ListSet, EventEncodeError> 101 where 102 I: IntoIterator<Item = S>, 103 S: AsRef<str>, 104 { 105 let mut entries = Vec::new(); 106 for plot_id in plot_ids { 107 let plot_id = plot_id.as_ref(); 108 let address = plot_address(farm_pubkey, plot_id)?; 109 entries.push(ListEntry { 110 tag: "a".to_string(), 111 values: vec![address], 112 }); 113 } 114 Ok(ListSet { 115 d_tag: farm_list_set_id(farm_id, "plots")?, 116 content: String::new(), 117 entries, 118 title: None, 119 description: None, 120 image: None, 121 }) 122 } 123 124 pub fn farm_operational_listings_list_set<I, S>( 125 farm_id: &str, 126 farm_pubkey: &str, 127 listing_ids: I, 128 ) -> Result<ListSet, EventEncodeError> 129 where 130 I: IntoIterator<Item = S>, 131 S: AsRef<str>, 132 { 133 let mut entries = Vec::new(); 134 for listing_id in listing_ids { 135 let listing_id = listing_id.as_ref().trim(); 136 if listing_id.is_empty() { 137 return Err(EventEncodeError::EmptyRequiredField("listing_id")); 138 } 139 validate_d_tag(listing_id, "listing_id")?; 140 let mut address = String::new(); 141 address.push_str(&KIND_CLASSIFIED_LISTING.to_string()); 142 address.push(':'); 143 address.push_str(farm_pubkey); 144 address.push(':'); 145 address.push_str(listing_id); 146 entries.push(ListEntry { 147 tag: "a".to_string(), 148 values: vec![address], 149 }); 150 } 151 Ok(ListSet { 152 d_tag: farm_list_set_id(farm_id, "listings")?, 153 content: String::new(), 154 entries, 155 title: None, 156 description: None, 157 image: None, 158 }) 159 } 160 161 pub fn farm_operational_listings_list_set_from_listings<'a, I>( 162 farm_id: &str, 163 farm_pubkey: &str, 164 listings: I, 165 ) -> Result<ListSet, EventEncodeError> 166 where 167 I: IntoIterator<Item = &'a OperationalListing>, 168 { 169 farm_operational_listings_list_set( 170 farm_id, 171 farm_pubkey, 172 listings.into_iter().map(|listing| listing.d_tag.as_str()), 173 ) 174 } 175 176 pub fn farm_plots_list_set_from_plots<'a, I>( 177 farm_id: &str, 178 farm_pubkey: &str, 179 plots: I, 180 ) -> Result<ListSet, EventEncodeError> 181 where 182 I: IntoIterator<Item = &'a Plot>, 183 { 184 farm_plots_list_set( 185 farm_id, 186 farm_pubkey, 187 plots.into_iter().map(|plot| plot.d_tag.as_str()), 188 ) 189 } 190 191 pub fn member_of_farms_list_set<I, S>(farm_pubkeys: I) -> Result<ListSet, EventEncodeError> 192 where 193 I: IntoIterator<Item = S>, 194 S: AsRef<str>, 195 { 196 Ok(ListSet { 197 d_tag: MEMBER_OF_FARMS.to_string(), 198 content: String::new(), 199 entries: list_entries("p", farm_pubkeys)?, 200 title: None, 201 description: None, 202 image: None, 203 }) 204 } 205 206 #[cfg(test)] 207 mod tests { 208 use super::*; 209 use crate::test_fixtures::FIXTURE_ALICE_PUBLIC_KEY_HEX; 210 211 #[test] 212 fn farm_list_set_id_validates_farm_id() { 213 let err = farm_list_set_id(" ", "members").expect_err("expected farm_id error"); 214 assert!(matches!( 215 err, 216 EventEncodeError::EmptyRequiredField("farm_id") 217 )); 218 } 219 220 #[test] 221 fn farm_list_set_builders_cover_success_and_error_paths() { 222 let farm_id = "AAAAAAAAAAAAAAAAAAAAAA"; 223 let farm_pubkey = FIXTURE_ALICE_PUBLIC_KEY_HEX; 224 225 let err = farm_members_list_set("invalid", ["member-a"]).expect_err("invalid farm id"); 226 assert!(matches!(err, EventEncodeError::InvalidField("farm_id"))); 227 228 let owners = farm_owners_list_set(farm_id, ["owner-a"]).expect("owners list set"); 229 assert_eq!(owners.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:members.owners"); 230 assert_eq!(owners.entries[0].tag, "p"); 231 let err = farm_owners_list_set("invalid", ["owner-a"]).expect_err("invalid farm id"); 232 assert!(matches!(err, EventEncodeError::InvalidField("farm_id"))); 233 let err = farm_owners_list_set(farm_id, [" "]).expect_err("invalid owner entry"); 234 assert!(matches!( 235 err, 236 EventEncodeError::EmptyRequiredField("entry.values") 237 )); 238 239 let workers = farm_workers_list_set(farm_id, ["worker-a"]).expect("workers list set"); 240 assert_eq!(workers.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:members.workers"); 241 assert_eq!(workers.entries[0].tag, "p"); 242 let err = farm_workers_list_set("invalid", ["worker-a"]).expect_err("invalid farm id"); 243 assert!(matches!(err, EventEncodeError::InvalidField("farm_id"))); 244 let err = farm_workers_list_set(farm_id, [" "]).expect_err("invalid worker entry"); 245 assert!(matches!( 246 err, 247 EventEncodeError::EmptyRequiredField("entry.values") 248 )); 249 250 let plots = 251 farm_plots_list_set(farm_id, farm_pubkey, ["AAAAAAAAAAAAAAAAAAAAAA"]).expect("plots"); 252 assert_eq!(plots.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:plots"); 253 assert_eq!(plots.entries[0].tag, "a"); 254 let err = farm_plots_list_set("invalid", farm_pubkey, ["AAAAAAAAAAAAAAAAAAAAAA"]) 255 .expect_err("invalid farm id"); 256 assert!(matches!(err, EventEncodeError::InvalidField("farm_id"))); 257 let err = 258 farm_plots_list_set(farm_id, farm_pubkey, ["invalid"]).expect_err("invalid plot_id"); 259 assert!(matches!(err, EventEncodeError::InvalidField("plot.d_tag"))); 260 261 let listings = 262 farm_operational_listings_list_set(farm_id, farm_pubkey, ["AAAAAAAAAAAAAAAAAAAAAA"]) 263 .expect("listings"); 264 assert_eq!(listings.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:listings"); 265 assert_eq!(listings.entries[0].tag, "a"); 266 let err = 267 farm_operational_listings_list_set("invalid", farm_pubkey, ["AAAAAAAAAAAAAAAAAAAAAA"]) 268 .expect_err("invalid farm id"); 269 assert!(matches!(err, EventEncodeError::InvalidField("farm_id"))); 270 let err = farm_operational_listings_list_set(farm_id, farm_pubkey, ["invalid"]) 271 .expect_err("invalid listing_id"); 272 assert!(matches!(err, EventEncodeError::InvalidField("listing_id"))); 273 274 let err = farm_operational_listings_list_set(farm_id, farm_pubkey, [" "]) 275 .expect_err("empty listing_id"); 276 assert!(matches!( 277 err, 278 EventEncodeError::EmptyRequiredField("listing_id") 279 )); 280 281 let member_of = member_of_farms_list_set(["farm-pubkey"]).expect("member_of farms"); 282 assert_eq!(member_of.d_tag, "member_of.farms"); 283 assert_eq!(member_of.entries[0].tag, "p"); 284 let err = member_of_farms_list_set([" "]).expect_err("invalid member_of entry"); 285 assert!(matches!( 286 err, 287 EventEncodeError::EmptyRequiredField("entry.values") 288 )); 289 } 290 }