lib

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

encode.rs (5390B)


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