lib

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

decode.rs (5327B)


      1 #![cfg(feature = "json")]
      2 
      3 #[cfg(not(feature = "std"))]
      4 use alloc::{
      5     string::{String, ToString},
      6     vec::Vec,
      7 };
      8 
      9 use radroots_event::{
     10     envelope::kind::KIND_FARM,
     11     farm::Farm,
     12     farm::location::{has_textual_locality, is_public_geohash5},
     13     tag::name::TAG_D,
     14 };
     15 
     16 use crate::d_tag::validate_d_tag_tag;
     17 use crate::error::EventParseError;
     18 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent};
     19 
     20 const DEFAULT_KIND: u32 = KIND_FARM;
     21 const TAG_G: &str = "g";
     22 
     23 fn parse_d_tag(tags: &[Vec<String>]) -> Result<String, EventParseError> {
     24     let tag = tags
     25         .iter()
     26         .find(|t| t.first().map(|s| s.as_str()) == Some(TAG_D))
     27         .ok_or(EventParseError::MissingTag(TAG_D))?;
     28     let value = tag
     29         .get(1)
     30         .map(|s| s.to_string())
     31         .ok_or(EventParseError::InvalidTag(TAG_D))?;
     32     if value.trim().is_empty() {
     33         return Err(EventParseError::InvalidTag(TAG_D));
     34     }
     35     validate_d_tag_tag(&value, TAG_D)?;
     36     Ok(value)
     37 }
     38 
     39 pub fn farm_from_event(
     40     kind: u32,
     41     tags: &[Vec<String>],
     42     content: &str,
     43 ) -> Result<Farm, EventParseError> {
     44     if kind != DEFAULT_KIND {
     45         return Err(EventParseError::InvalidKind {
     46             expected: "30340",
     47             got: kind,
     48         });
     49     }
     50     if content.trim().is_empty() {
     51         return Err(EventParseError::InvalidJson("content"));
     52     }
     53     let d_tag = parse_d_tag(tags)?;
     54     reject_private_farm_location_tags(tags)?;
     55     reject_private_farm_ops_content(content)?;
     56     let mut farm: Farm =
     57         serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?;
     58 
     59     if farm.d_tag.trim().is_empty() {
     60         farm.d_tag = d_tag;
     61     } else if farm.d_tag != d_tag {
     62         return Err(EventParseError::InvalidTag(TAG_D));
     63     }
     64     if let Some(location) = farm.location.as_ref()
     65         && (!is_public_geohash5(&location.geohash)
     66             || !has_textual_locality(
     67                 &location.primary,
     68                 location.city.as_deref(),
     69                 location.region.as_deref(),
     70                 location.country.as_deref(),
     71             ))
     72     {
     73         return Err(EventParseError::InvalidTag(TAG_G));
     74     }
     75 
     76     Ok(farm)
     77 }
     78 
     79 fn reject_private_farm_location_tags(tags: &[Vec<String>]) -> Result<(), EventParseError> {
     80     for tag in tags {
     81         let Some(key) = tag.first().map(|value| value.as_str()) else {
     82             continue;
     83         };
     84         match key {
     85             TAG_G => {
     86                 let Some(value) = tag.get(1).map(|value| value.trim()) else {
     87                     return Err(EventParseError::InvalidTag(TAG_G));
     88                 };
     89                 if !is_public_geohash5(value) {
     90                     return Err(EventParseError::InvalidTag(TAG_G));
     91                 }
     92             }
     93             "dd" => return Err(EventParseError::InvalidTag("dd")),
     94             "dd.lat" => return Err(EventParseError::InvalidTag("dd.lat")),
     95             "dd.lon" => return Err(EventParseError::InvalidTag("dd.lon")),
     96             "l" => return Err(EventParseError::InvalidTag("l")),
     97             "L" => return Err(EventParseError::InvalidTag("L")),
     98             _ => {}
     99         }
    100     }
    101     Ok(())
    102 }
    103 
    104 fn reject_private_farm_ops_content(content: &str) -> Result<(), EventParseError> {
    105     let value: serde_json::Value =
    106         serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?;
    107     let Some(object) = value.as_object() else {
    108         return Err(EventParseError::InvalidJson("content"));
    109     };
    110     for key in [
    111         "workspace",
    112         "farm_group_id",
    113         "document_id",
    114         "document_kind",
    115         "crdt_backend",
    116         "encoded_change",
    117         "semantic_kind",
    118         "owner_document_kind",
    119         "owner_document_id",
    120         "relays",
    121         "media_servers",
    122         "supported_kinds",
    123         "protocol_version",
    124     ] {
    125         if object.contains_key(key) {
    126             return Err(EventParseError::InvalidJson("content"));
    127         }
    128     }
    129     if let Some(location) = object.get("location").and_then(|value| value.as_object()) {
    130         for key in [
    131             "gcs",
    132             "lat",
    133             "lng",
    134             "lon",
    135             "point",
    136             "polygon",
    137             "coordinates",
    138             "accuracy",
    139             "altitude",
    140             "label",
    141             "tag_0",
    142         ] {
    143             if location.contains_key(key) {
    144                 return Err(EventParseError::InvalidJson("content"));
    145             }
    146         }
    147     }
    148     Ok(())
    149 }
    150 
    151 pub fn data_from_event(
    152     id: String,
    153     author: String,
    154     published_at: u64,
    155     kind: u32,
    156     content: String,
    157     tags: Vec<Vec<String>>,
    158 ) -> Result<RadrootsParsedData<Farm>, EventParseError> {
    159     let farm = farm_from_event(kind, &tags, &content)?;
    160     Ok(RadrootsParsedData::new(
    161         id,
    162         author,
    163         published_at,
    164         kind,
    165         farm,
    166     ))
    167 }
    168 
    169 pub fn parsed_from_event(
    170     id: String,
    171     author: String,
    172     published_at: u64,
    173     kind: u32,
    174     content: String,
    175     tags: Vec<Vec<String>>,
    176     sig: String,
    177 ) -> Result<RadrootsParsedEvent<Farm>, EventParseError> {
    178     let data = data_from_event(
    179         id.clone(),
    180         author.clone(),
    181         published_at,
    182         kind,
    183         content.clone(),
    184         tags.clone(),
    185     )?;
    186     RadrootsParsedEvent::from_event_parts(id, author, published_at, kind, content, tags, sig, data)
    187 }