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