lib

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

decode.rs (5536B)


      1 #![cfg(feature = "serde_json")]
      2 
      3 #[cfg(not(feature = "std"))]
      4 use alloc::{
      5     string::{String, ToString},
      6     vec::Vec,
      7 };
      8 
      9 use radroots_events::{
     10     RadrootsNostrEvent,
     11     farm_crdt::{KIND_FARM_CRDT_CHANGE, RADROOTS_FARM_CRDT_TAG, RadrootsFarmCrdtChange},
     12     farm_workspace::KIND_FARM_WORKSPACE_MANIFEST,
     13     tags::{TAG_A, TAG_D, TAG_H, TAG_P, TAG_T},
     14 };
     15 
     16 use crate::d_tag::validate_d_tag_tag;
     17 use crate::error::EventParseError;
     18 use crate::farm_crdt::encode::validate_change;
     19 use crate::field_helpers::{
     20     optional_tag_value, parse_address_tag_with_kind, required_tag_value, tag_values,
     21     validate_non_empty_tag_value,
     22 };
     23 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent};
     24 
     25 const EXPECTED_KIND: &str = "78";
     26 
     27 pub fn farm_crdt_change_from_event(
     28     kind: u32,
     29     tags: &[Vec<String>],
     30     content: &str,
     31 ) -> Result<RadrootsFarmCrdtChange, EventParseError> {
     32     farm_crdt_change_from_event_inner(kind, tags, content, None)
     33 }
     34 
     35 pub fn farm_crdt_change_from_event_with_author(
     36     kind: u32,
     37     tags: &[Vec<String>],
     38     content: &str,
     39     author_pubkey: &str,
     40 ) -> Result<RadrootsFarmCrdtChange, EventParseError> {
     41     validate_non_empty_tag_value(author_pubkey, TAG_P)?;
     42     farm_crdt_change_from_event_inner(kind, tags, content, Some(author_pubkey))
     43 }
     44 
     45 pub fn data_from_event(
     46     id: String,
     47     author: String,
     48     published_at: u32,
     49     kind: u32,
     50     content: String,
     51     tags: Vec<Vec<String>>,
     52 ) -> Result<RadrootsParsedData<RadrootsFarmCrdtChange>, EventParseError> {
     53     let change = farm_crdt_change_from_event_with_author(kind, &tags, &content, &author)?;
     54     Ok(RadrootsParsedData::new(
     55         id,
     56         author,
     57         published_at,
     58         kind,
     59         change,
     60     ))
     61 }
     62 
     63 pub fn parsed_from_event(
     64     id: String,
     65     author: String,
     66     published_at: u32,
     67     kind: u32,
     68     content: String,
     69     tags: Vec<Vec<String>>,
     70     sig: String,
     71 ) -> Result<RadrootsParsedEvent<RadrootsFarmCrdtChange>, EventParseError> {
     72     let data = data_from_event(
     73         id.clone(),
     74         author.clone(),
     75         published_at,
     76         kind,
     77         content.clone(),
     78         tags.clone(),
     79     )?;
     80     Ok(RadrootsParsedEvent {
     81         event: RadrootsNostrEvent {
     82             id,
     83             author,
     84             created_at: published_at,
     85             kind,
     86             content,
     87             tags,
     88             sig,
     89         },
     90         data,
     91     })
     92 }
     93 
     94 fn farm_crdt_change_from_event_inner(
     95     kind: u32,
     96     tags: &[Vec<String>],
     97     content: &str,
     98     author_pubkey: Option<&str>,
     99 ) -> Result<RadrootsFarmCrdtChange, EventParseError> {
    100     if kind != KIND_FARM_CRDT_CHANGE {
    101         return Err(EventParseError::InvalidKind {
    102             expected: EXPECTED_KIND,
    103             got: kind,
    104         });
    105     }
    106     if content.trim().is_empty() {
    107         return Err(EventParseError::InvalidJson("content"));
    108     }
    109 
    110     let farm_group_id = required_tag_value(tags, TAG_H)?;
    111     let document_id = required_tag_value(tags, TAG_D)?;
    112     validate_d_tag_tag(&document_id, TAG_D)?;
    113     let workspace_address = required_tag_value(tags, TAG_A)?;
    114     let workspace =
    115         parse_address_tag_with_kind(&workspace_address, KIND_FARM_WORKSPACE_MANIFEST, TAG_A)?;
    116     let marker_tags = tag_values(tags, TAG_T)?;
    117     if !marker_tags
    118         .iter()
    119         .any(|value| value == RADROOTS_FARM_CRDT_TAG)
    120     {
    121         return Err(EventParseError::MissingTag(TAG_T));
    122     }
    123     if let Some(tag_author) = optional_tag_value(tags, TAG_P)?
    124         && let Some(author_pubkey) = author_pubkey
    125         && tag_author != author_pubkey
    126     {
    127         return Err(EventParseError::InvalidTag(TAG_P));
    128     }
    129 
    130     let change: RadrootsFarmCrdtChange =
    131         serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?;
    132     validate_change(&change).map_err(encode_error_to_parse_error)?;
    133     if change.farm_group_id != farm_group_id {
    134         return Err(EventParseError::InvalidTag(TAG_H));
    135     }
    136     if change.document_id != document_id {
    137         return Err(EventParseError::InvalidTag(TAG_D));
    138     }
    139     if change.workspace.pubkey != workspace.pubkey || change.workspace.d_tag != workspace.d_tag {
    140         return Err(EventParseError::InvalidTag(TAG_A));
    141     }
    142     Ok(change)
    143 }
    144 
    145 fn encode_error_to_parse_error(error: crate::error::EventEncodeError) -> EventParseError {
    146     match error {
    147         crate::error::EventEncodeError::InvalidKind(kind) => EventParseError::InvalidKind {
    148             expected: EXPECTED_KIND,
    149             got: kind,
    150         },
    151         crate::error::EventEncodeError::EmptyRequiredField(field)
    152         | crate::error::EventEncodeError::InvalidField(field) => match field {
    153             "farm_group_id" => EventParseError::InvalidTag(TAG_H),
    154             "document_id" => EventParseError::InvalidTag(TAG_D),
    155             "workspace.pubkey" | "workspace.d_tag" => EventParseError::InvalidTag(TAG_A),
    156             _ => EventParseError::InvalidJson(field),
    157         },
    158         crate::error::EventEncodeError::Json => EventParseError::InvalidJson("content"),
    159     }
    160 }
    161 
    162 #[cfg(test)]
    163 mod tests {
    164     use super::*;
    165     use crate::error::EventEncodeError;
    166 
    167     #[test]
    168     fn encode_error_mapper_covers_kind_and_json_edges() {
    169         assert!(matches!(
    170             encode_error_to_parse_error(EventEncodeError::InvalidKind(1)),
    171             EventParseError::InvalidKind {
    172                 expected: EXPECTED_KIND,
    173                 got: 1
    174             }
    175         ));
    176         assert!(matches!(
    177             encode_error_to_parse_error(EventEncodeError::Json),
    178             EventParseError::InvalidJson("content")
    179         ));
    180     }
    181 }