encode.rs (5011B)
1 #[cfg(all(not(feature = "std"), test, feature = "json"))] 2 use alloc::vec; 3 #[cfg(all(not(feature = "std"), feature = "json"))] 4 use alloc::{format, string::ToString}; 5 #[cfg(not(feature = "std"))] 6 use alloc::{string::String, vec::Vec}; 7 8 #[cfg(feature = "json")] 9 use radroots_event::envelope::kind::{KIND_CLASSIFIED_LISTING, is_classified_listing_kind}; 10 use radroots_event::listing::operational::OperationalListing; 11 12 use crate::error::EventEncodeError; 13 use crate::operational_listing::tags::operational_listing_tags; 14 #[cfg(feature = "json")] 15 use crate::operational_listing::tags::operational_listing_tags_full; 16 #[cfg(feature = "json")] 17 use radroots_event::wire::Nip01EventWireParts; 18 19 #[cfg(feature = "json")] 20 const DEFAULT_KIND: u32 = KIND_CLASSIFIED_LISTING; 21 22 pub fn operational_listing_build_tags( 23 listing: &OperationalListing, 24 ) -> Result<Vec<Vec<String>>, EventEncodeError> { 25 operational_listing_tags(listing) 26 } 27 28 #[cfg(feature = "json")] 29 pub fn to_wire_parts( 30 listing: &OperationalListing, 31 ) -> Result<Nip01EventWireParts, EventEncodeError> { 32 to_wire_parts_with_kind(listing, DEFAULT_KIND) 33 } 34 35 #[cfg(feature = "json")] 36 pub fn to_wire_parts_with_kind( 37 listing: &OperationalListing, 38 kind: u32, 39 ) -> Result<Nip01EventWireParts, EventEncodeError> { 40 if !is_classified_listing_kind(kind) { 41 return Err(EventEncodeError::InvalidKind(kind)); 42 } 43 let tags = operational_listing_tags_full(listing)?; 44 let content = operational_listing_markdown_content(listing); 45 Ok(Nip01EventWireParts { 46 kind, 47 content, 48 tags, 49 }) 50 } 51 52 #[cfg(feature = "json")] 53 fn operational_listing_markdown_content(listing: &OperationalListing) -> String { 54 let title = listing.product.title.trim(); 55 let summary = listing 56 .product 57 .summary 58 .as_deref() 59 .map(str::trim) 60 .filter(|value| !value.is_empty()); 61 62 match (title.is_empty(), summary) { 63 (false, Some(summary)) => format!("# {title}\n\n{summary}"), 64 (false, None) => format!("# {title}"), 65 (true, Some(summary)) => summary.to_string(), 66 (true, None) => String::new(), 67 } 68 } 69 70 #[cfg(all(test, feature = "json"))] 71 mod tests { 72 use super::*; 73 use core::str::FromStr; 74 use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; 75 use radroots_event::{ 76 farm::FarmRef, 77 id::{DTag, InventoryBinId}, 78 listing::operational::{OperationalListingBin, OperationalListingProduct}, 79 }; 80 81 fn decimal(value: &str) -> Decimal { 82 Decimal::from_str(value).expect("decimal") 83 } 84 85 fn listing_with(title: &str, summary: Option<&str>) -> OperationalListing { 86 OperationalListing { 87 d_tag: DTag::parse("AAAAAAAAAAAAAAAAAAAAAA").expect("d tag"), 88 published_at: None, 89 farm: FarmRef { 90 pubkey: "a".repeat(64), 91 d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(), 92 }, 93 product: OperationalListingProduct { 94 key: "coffee".to_string(), 95 title: title.to_string(), 96 category: "produce".to_string(), 97 summary: summary.map(ToOwned::to_owned), 98 process: None, 99 lot: None, 100 location: None, 101 profile: None, 102 year: None, 103 }, 104 primary_bin_id: InventoryBinId::parse("bin-1").expect("bin id"), 105 bins: vec![OperationalListingBin { 106 bin_id: InventoryBinId::parse("bin-1").expect("bin id"), 107 quantity: Quantity::try_new(decimal("1"), Unit::MassG).unwrap(), 108 price_per_canonical_unit: QuantityPrice::try_new( 109 Money::try_new(decimal("1"), Currency::USD).unwrap(), 110 Quantity::try_new(Decimal::ONE, Unit::MassG).unwrap(), 111 ) 112 .unwrap(), 113 display_amount: None, 114 display_unit: None, 115 display_label: None, 116 display_price: None, 117 display_price_unit: None, 118 }], 119 resource_area: None, 120 plot: None, 121 discounts: None, 122 inventory_available: None, 123 availability: None, 124 delivery_method: None, 125 location: None, 126 images: None, 127 } 128 } 129 130 #[test] 131 fn operational_listing_markdown_content_covers_title_summary_combinations() { 132 assert_eq!( 133 operational_listing_markdown_content(&listing_with("Coffee", Some("Washed"))), 134 "# Coffee\n\nWashed" 135 ); 136 assert_eq!( 137 operational_listing_markdown_content(&listing_with("Coffee", None)), 138 "# Coffee" 139 ); 140 assert_eq!( 141 operational_listing_markdown_content(&listing_with(" ", Some("Washed"))), 142 "Washed" 143 ); 144 assert_eq!( 145 operational_listing_markdown_content(&listing_with(" ", None)), 146 "" 147 ); 148 } 149 }