lib

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

decode.rs (5261B)


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