lib

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

decode.rs (5838B)


      1 #[cfg(not(feature = "std"))]
      2 use alloc::{
      3     string::{String, ToString},
      4     vec::Vec,
      5 };
      6 
      7 use radroots_event::{
      8     envelope::kind::KIND_REPORT,
      9     post::report::Report,
     10     social::{ReportFileTarget, ReportType, SocialTarget},
     11     tag::name::{TAG_A, TAG_E, TAG_MAGNET, TAG_P, TAG_SERVER, TAG_SHA256},
     12 };
     13 
     14 use crate::error::EventParseError;
     15 use crate::field_helpers::{parse_address_tag, required_tag_value, validate_lowercase_hex_64_tag};
     16 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent};
     17 use crate::social_helpers::first_tag_value;
     18 
     19 pub fn report_from_event(
     20     kind: u32,
     21     tags: &[Vec<String>],
     22     content: &str,
     23 ) -> Result<Report, EventParseError> {
     24     if kind != KIND_REPORT {
     25         return Err(EventParseError::InvalidKind {
     26             expected: "1984",
     27             got: kind,
     28         });
     29     }
     30     let p_tag = find_tag(tags, TAG_P).ok_or(EventParseError::MissingTag(TAG_P))?;
     31     let reported_pubkey = required_tag_value(tags, TAG_P)?;
     32     validate_lowercase_hex_64_tag(&reported_pubkey, TAG_P)?;
     33     let report_type = parse_report_type(
     34         p_tag
     35             .get(2)
     36             .map(|value| value.as_str())
     37             .ok_or(EventParseError::InvalidTag(TAG_P))?,
     38         TAG_P,
     39     )?;
     40     let event = parse_event_target(tags, &report_type)?;
     41     let file = parse_file_target(tags, &report_type)?;
     42     Ok(Report {
     43         reported_pubkey,
     44         report_type,
     45         event,
     46         file,
     47         content: if content.is_empty() {
     48             None
     49         } else {
     50             Some(content.to_string())
     51         },
     52     })
     53 }
     54 
     55 pub fn data_from_event(
     56     id: String,
     57     author: String,
     58     published_at: u64,
     59     kind: u32,
     60     content: String,
     61     tags: Vec<Vec<String>>,
     62 ) -> Result<RadrootsParsedData<Report>, EventParseError> {
     63     let report = report_from_event(kind, &tags, &content)?;
     64     Ok(RadrootsParsedData::new(
     65         id,
     66         author,
     67         published_at,
     68         kind,
     69         report,
     70     ))
     71 }
     72 
     73 pub fn parsed_from_event(
     74     id: String,
     75     author: String,
     76     published_at: u64,
     77     kind: u32,
     78     content: String,
     79     tags: Vec<Vec<String>>,
     80     sig: String,
     81 ) -> Result<RadrootsParsedEvent<Report>, EventParseError> {
     82     let data = data_from_event(
     83         id.clone(),
     84         author.clone(),
     85         published_at,
     86         kind,
     87         content.clone(),
     88         tags.clone(),
     89     )?;
     90     RadrootsParsedEvent::from_event_parts(id, author, published_at, kind, content, tags, sig, data)
     91 }
     92 
     93 fn parse_event_target(
     94     tags: &[Vec<String>],
     95     report_type: &ReportType,
     96 ) -> Result<Option<SocialTarget>, EventParseError> {
     97     if let Some(tag) = find_tag(tags, TAG_A) {
     98         validate_optional_target_report_type(tag, 2, report_type, TAG_A)?;
     99         let value = tag
    100             .get(1)
    101             .cloned()
    102             .ok_or(EventParseError::InvalidTag(TAG_A))?;
    103         let address = parse_address_tag(&value, TAG_A)?;
    104         return Ok(Some(SocialTarget::Address {
    105             address: value,
    106             author: Some(address.pubkey),
    107             event_kind: Some(address.kind),
    108             relays: relays_from_tag(tag, 3),
    109         }));
    110     }
    111     if let Some(tag) = find_tag(tags, TAG_E) {
    112         validate_optional_target_report_type(tag, 2, report_type, TAG_E)?;
    113         let id = tag
    114             .get(1)
    115             .cloned()
    116             .ok_or(EventParseError::InvalidTag(TAG_E))?;
    117         validate_lowercase_hex_64_tag(&id, TAG_E)?;
    118         return Ok(Some(SocialTarget::Event {
    119             id,
    120             author: first_tag_value(tags, TAG_P),
    121             event_kind: None,
    122             relays: relays_from_tag(tag, 3),
    123         }));
    124     }
    125     Ok(None)
    126 }
    127 
    128 fn parse_file_target(
    129     tags: &[Vec<String>],
    130     report_type: &ReportType,
    131 ) -> Result<Option<ReportFileTarget>, EventParseError> {
    132     let sha256 = if let Some(tag) = find_tag(tags, TAG_SHA256) {
    133         validate_optional_target_report_type(tag, 2, report_type, TAG_SHA256)?;
    134         let value = tag
    135             .get(1)
    136             .cloned()
    137             .ok_or(EventParseError::InvalidTag(TAG_SHA256))?;
    138         validate_lowercase_hex_64_tag(&value, TAG_SHA256)?;
    139         Some(value)
    140     } else {
    141         None
    142     };
    143     let url = first_tag_value(tags, TAG_SERVER);
    144     let magnet = first_tag_value(tags, TAG_MAGNET);
    145     if sha256.is_none() && url.is_none() && magnet.is_none() {
    146         Ok(None)
    147     } else {
    148         Ok(Some(ReportFileTarget {
    149             sha256,
    150             url,
    151             magnet,
    152         }))
    153     }
    154 }
    155 
    156 fn validate_optional_target_report_type(
    157     tag: &[String],
    158     index: usize,
    159     expected: &ReportType,
    160     tag_name: &'static str,
    161 ) -> Result<(), EventParseError> {
    162     let Some(value) = tag.get(index) else {
    163         return Ok(());
    164     };
    165     if parse_report_type(value, tag_name)? == *expected {
    166         Ok(())
    167     } else {
    168         Err(EventParseError::InvalidTag(tag_name))
    169     }
    170 }
    171 
    172 fn parse_report_type(value: &str, tag_name: &'static str) -> Result<ReportType, EventParseError> {
    173     match value {
    174         "nudity" => Ok(ReportType::Nudity),
    175         "malware" => Ok(ReportType::Malware),
    176         "profanity" => Ok(ReportType::Profanity),
    177         "illegal" => Ok(ReportType::Illegal),
    178         "spam" => Ok(ReportType::Spam),
    179         "impersonation" => Ok(ReportType::Impersonation),
    180         "other" => Ok(ReportType::Other),
    181         _ => Err(EventParseError::InvalidTag(tag_name)),
    182     }
    183 }
    184 
    185 fn find_tag<'a>(tags: &'a [Vec<String>], key: &'static str) -> Option<&'a Vec<String>> {
    186     tags.iter()
    187         .find(|tag| tag.first().map(|value| value.as_str()) == Some(key))
    188 }
    189 
    190 fn relays_from_tag(tag: &[String], start: usize) -> Option<Vec<String>> {
    191     let relays = tag
    192         .iter()
    193         .skip(start)
    194         .filter(|value| !value.trim().is_empty())
    195         .cloned()
    196         .collect::<Vec<_>>();
    197     if relays.is_empty() {
    198         None
    199     } else {
    200         Some(relays)
    201     }
    202 }