lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

encode.rs (5247B)


      1 #[cfg(not(feature = "std"))]
      2 use alloc::{
      3     string::{String, ToString},
      4     vec,
      5     vec::Vec,
      6 };
      7 
      8 use radroots_event::{
      9     envelope::kind::KIND_RESOURCE_AREA, farm::resource_cap::ResourceHarvestCap, tag::name::TAG_D,
     10 };
     11 
     12 use crate::d_tag::validate_d_tag;
     13 use crate::error::EventEncodeError;
     14 
     15 #[cfg(feature = "json")]
     16 use radroots_event::envelope::kind::KIND_RESOURCE_HARVEST_CAP;
     17 #[cfg(feature = "json")]
     18 use radroots_event::wire::Nip01EventWireParts;
     19 
     20 const TAG_A: &str = "a";
     21 const TAG_P: &str = "p";
     22 const TAG_T: &str = "t";
     23 const TAG_KEY: &str = "key";
     24 const TAG_CATEGORY: &str = "category";
     25 const TAG_START: &str = "start";
     26 const TAG_END: &str = "end";
     27 
     28 fn push_tag(tags: &mut Vec<Vec<String>>, key: &str, value: &str) {
     29     tags.push(vec![key.to_string(), value.to_string()]);
     30 }
     31 
     32 fn resource_area_address(cap: &ResourceHarvestCap) -> Result<String, EventEncodeError> {
     33     let area = &cap.resource_area;
     34     if area.pubkey.trim().is_empty() {
     35         return Err(EventEncodeError::EmptyRequiredField("resource_area.pubkey"));
     36     }
     37     if area.d_tag.trim().is_empty() {
     38         return Err(EventEncodeError::EmptyRequiredField("resource_area.d_tag"));
     39     }
     40     validate_d_tag(&area.d_tag, "resource_area.d_tag")?;
     41     let mut addr = String::new();
     42     addr.push_str(&KIND_RESOURCE_AREA.to_string());
     43     addr.push(':');
     44     addr.push_str(&area.pubkey);
     45     addr.push(':');
     46     addr.push_str(&area.d_tag);
     47     Ok(addr)
     48 }
     49 
     50 pub fn resource_harvest_cap_build_tags(
     51     cap: &ResourceHarvestCap,
     52 ) -> Result<Vec<Vec<String>>, EventEncodeError> {
     53     if cap.d_tag.trim().is_empty() {
     54         return Err(EventEncodeError::EmptyRequiredField("d_tag"));
     55     }
     56     validate_d_tag(&cap.d_tag, "d_tag")?;
     57     if cap.product.key.trim().is_empty() {
     58         return Err(EventEncodeError::EmptyRequiredField("product.key"));
     59     }
     60     let mut tags = Vec::new();
     61     push_tag(&mut tags, TAG_D, &cap.d_tag);
     62     let addr = resource_area_address(cap)?;
     63     push_tag(&mut tags, TAG_A, &addr);
     64     push_tag(&mut tags, TAG_P, &cap.resource_area.pubkey);
     65     push_tag(&mut tags, TAG_KEY, &cap.product.key);
     66     if let Some(category) = cap.product.category.as_deref()
     67         && !category.trim().is_empty()
     68     {
     69         push_tag(&mut tags, TAG_CATEGORY, category);
     70     }
     71     push_tag(&mut tags, TAG_START, &cap.start.to_string());
     72     push_tag(&mut tags, TAG_END, &cap.end.to_string());
     73     if let Some(items) = cap.tags.as_ref() {
     74         for item in items.iter().filter(|v| !v.trim().is_empty()) {
     75             push_tag(&mut tags, TAG_T, item);
     76         }
     77     }
     78     Ok(tags)
     79 }
     80 
     81 #[cfg(feature = "json")]
     82 pub fn to_wire_parts(cap: &ResourceHarvestCap) -> Result<Nip01EventWireParts, EventEncodeError> {
     83     to_wire_parts_with_kind(cap, KIND_RESOURCE_HARVEST_CAP)
     84 }
     85 
     86 #[cfg(feature = "json")]
     87 pub fn to_wire_parts_with_kind(
     88     cap: &ResourceHarvestCap,
     89     kind: u32,
     90 ) -> Result<Nip01EventWireParts, EventEncodeError> {
     91     if kind != KIND_RESOURCE_HARVEST_CAP {
     92         return Err(EventEncodeError::InvalidKind(kind));
     93     }
     94     let tags = resource_harvest_cap_build_tags(cap)?;
     95     let content = serde_json::to_string(cap).map_err(|_| EventEncodeError::Json)?;
     96     Ok(Nip01EventWireParts {
     97         kind,
     98         content,
     99         tags,
    100     })
    101 }
    102 
    103 #[cfg(test)]
    104 mod tests {
    105     use super::*;
    106     use crate::test_fixtures::FIXTURE_ALICE_PUBLIC_KEY_HEX;
    107     use radroots_core::{Decimal, Quantity, Unit};
    108     use radroots_event::farm::resource_area::ResourceAreaRef;
    109     use radroots_event::farm::resource_cap::ResourceHarvestProduct;
    110 
    111     fn sample_cap_with_category(category: Option<&str>) -> ResourceHarvestCap {
    112         ResourceHarvestCap {
    113             d_tag: "AAAAAAAAAAAAAAAAAAAABA".to_string(),
    114             resource_area: ResourceAreaRef {
    115                 pubkey: FIXTURE_ALICE_PUBLIC_KEY_HEX.to_string(),
    116                 d_tag: "AAAAAAAAAAAAAAAAAAAAAw".to_string(),
    117             },
    118             product: ResourceHarvestProduct {
    119                 key: "nutmeg".to_string(),
    120                 category: category.map(|value| value.to_string()),
    121             },
    122             start: 1,
    123             end: 2,
    124             cap_quantity: Quantity::try_new(Decimal::from(1000u32), Unit::MassG).unwrap(),
    125             display_amount: None,
    126             display_unit: None,
    127             display_label: None,
    128             tags: None,
    129         }
    130     }
    131 
    132     #[test]
    133     fn resource_harvest_cap_build_tags_omits_blank_category() {
    134         let tags = resource_harvest_cap_build_tags(&sample_cap_with_category(Some(" ")))
    135             .expect("resource harvest cap tags");
    136         assert!(
    137             !tags
    138                 .iter()
    139                 .any(|tag| tag.first().map(|v| v.as_str()) == Some("category"))
    140         );
    141 
    142         let tags = resource_harvest_cap_build_tags(&sample_cap_with_category(None))
    143             .expect("resource harvest cap tags");
    144         assert!(
    145             !tags
    146                 .iter()
    147                 .any(|tag| tag.first().map(|v| v.as_str()) == Some("category"))
    148         );
    149 
    150         let tags = resource_harvest_cap_build_tags(&sample_cap_with_category(Some("spice")))
    151             .expect("resource harvest cap tags");
    152         assert!(
    153             tags.iter()
    154                 .any(|tag| tag.first().map(|v| v.as_str()) == Some("category"))
    155         );
    156     }
    157 }