dialtone_common 0.1.0

Dialtone Common Code
Documentation
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(untagged)]
pub enum ApObjects {
    SingleHref(String),
    MultipleHrefs(Vec<String>),
    SingleObj(Box<ApObject>),
    MultipleObjs(Vec<ApObject>),
}

impl ApObjects {
    fn ap_objects_vec(&self) -> Vec<ApObject> {
        match self {
            ApObjects::SingleHref(href) => {
                vec![ApObject::from(href.to_string())]
            }
            ApObjects::MultipleHrefs(hrefs) => hrefs
                .iter()
                .map(|href| ApObject::from(href.to_string()))
                .collect(),
            ApObjects::SingleObj(obj) => {
                vec![*obj.clone()]
            }
            ApObjects::MultipleObjs(objs) => objs.clone(),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(untagged)]
pub enum ApObjectAddresses {
    SingleUrl(String),
    MultipleUrls(Vec<String>),
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum ApObjectType {
    Article,
    Note,
    Document,
    Image,
    Video,
    Audio,
    Page,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum ApObjectMediaType {
    #[serde(rename = "text/html")]
    TextHtml,

    #[serde(rename = "image/png")]
    ImagePng,

    #[serde(rename = "image/jpeg")]
    ImageJpeg,

    #[serde(rename = "image/gif")]
    ImageGif,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ApObject {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    #[serde(rename = "mediaType")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub media_type: Option<ApObjectMediaType>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "type")]
    pub ap_type: Option<ApObjectType>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub content: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub summary: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub actor: Option<String>,

    #[serde(rename = "attributedTo")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attributed_to: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub to: Option<ApObjectAddresses>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub cc: Option<ApObjectAddresses>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub bto: Option<ApObjectAddresses>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub bcc: Option<ApObjectAddresses>,
}

impl From<String> for ApObject {
    fn from(url: String) -> Self {
        ApObject {
            name: None,
            id: None,
            url: Option::from(url),
            media_type: None,
            ap_type: None,
            content: None,
            summary: None,
            actor: None,
            attributed_to: None,
            to: None,
            cc: None,
            bto: None,
            bcc: None,
        }
    }
}

#[cfg(test)]
mod ap_object_tests {
    use super::*;

    #[test]
    fn parse_single_url() {
        let u1 = "http://example.com/icon.png";
        let j = format!("\"{}\"", u1);
        let ap_obj: ApObjects = serde_json::from_str(&j).unwrap();
        let v = ap_obj.ap_objects_vec();
        assert_eq!(1, v.len());
        assert_eq!(u1.to_string(), v[0].url.clone().unwrap());
    }

    #[test]
    fn parse_array_of_urls() {
        let u1 = "http://example.com/icon.png";
        let u2 = "http://example.org/foo.png";
        let j = format!("[\"{}\",\"{}\"]", u1, u2);
        let ap_obj: ApObjects = serde_json::from_str(&j).unwrap();
        let v = ap_obj.ap_objects_vec();
        assert_eq!(2, v.len());
        assert_eq!(u1.to_string(), v[0].url.clone().unwrap());
        assert_eq!(u2.to_string(), v[1].url.clone().unwrap());
    }

    #[test]
    fn parse_obj() {
        let u1 = "http://example.com/icon.png";
        let j = format!("{{\"url\":\"{}\"}}", u1);
        let ap_obj: ApObjects = serde_json::from_str(&j).unwrap();
        let v = ap_obj.ap_objects_vec();
        assert_eq!(1, v.len());
        assert_eq!(u1.to_string(), v[0].url.clone().unwrap());
    }

    #[test]
    fn parse_multiple_objs() {
        let u1 = "http://example.com/icon.png";
        let u2 = "http://example.org/foo.png";
        let j = format!("[{{\"url\":\"{}\"}},{{\"url\":\"{}\"}}]", u1, u2);
        let ap_obj: ApObjects = serde_json::from_str(&j).unwrap();
        let v = ap_obj.ap_objects_vec();
        assert_eq!(2, v.len());
        assert_eq!(u1.to_string(), v[0].url.clone().unwrap());
        assert_eq!(u2.to_string(), v[1].url.clone().unwrap());
    }

    #[test]
    fn size_of_icons() {
        println!("Size of Icons is {}", std::mem::size_of::<ApObjects>())
    }

    #[test]
    fn roundtrip_mastodon_example_test() {
        let ex = r#"
            {
               "@context" : [
                  "https://www.w3.org/ns/activitystreams",
                  {
                     "atomUri" : "ostatus:atomUri",
                     "blurhash" : "toot:blurhash",
                     "conversation" : "ostatus:conversation",
                     "focalPoint" : {
                        "@container" : "@list",
                        "@id" : "toot:focalPoint"
                     },
                     "inReplyToAtomUri" : "ostatus:inReplyToAtomUri",
                     "ostatus" : "http://ostatus.org#",
                     "sensitive" : "as:sensitive",
                     "toot" : "http://joinmastodon.org/ns#",
                     "votersCount" : "toot:votersCount"
                  }
               ],
               "atomUri" : "https://fosstodon.org/users/xpil/statuses/107831067777241377",
               "attachment" : [
                  {
                     "blurhash" : "UHH.KH00t7M{xuWBaya|WBj[fQayofWVj[ay",
                     "height" : 249,
                     "mediaType" : "image/jpeg",
                     "name" : null,
                     "type" : "Document",
                     "url" : "https://cdn.fosstodon.org/media_attachments/files/107/831/067/339/133/535/original/7117410b8c95755d.jpg",
                     "width" : 599
                  }
               ],
               "attributedTo" : "https://fosstodon.org/users/xpil",
               "cc" : [
                  "https://fosstodon.org/users/xpil/followers"
               ],
               "mediaType": "text/html",
               "content" : "<p>I was 4 years old when this was published. Could as well be yesterday.</p>",
               "contentMap" : {
                  "en" : "<p>I was 4 years old when this was published. Could as well be yesterday.</p>"
               },
               "conversation" : "tag:fosstodon.org,2022-02-20:objectId=43364305:objectType=Conversation",
               "id" : "https://fosstodon.org/users/xpil/statuses/107831067777241377",
               "inReplyTo" : null,
               "inReplyToAtomUri" : null,
               "published" : "2022-02-20T15:38:37Z",
               "replies" : {
                  "first" : {
                     "items" : [],
                     "next" : "https://fosstodon.org/users/xpil/statuses/107831067777241377/replies?only_other_accounts=true&page=true",
                     "partOf" : "https://fosstodon.org/users/xpil/statuses/107831067777241377/replies",
                     "type" : "CollectionPage"
                  },
                  "id" : "https://fosstodon.org/users/xpil/statuses/107831067777241377/replies",
                  "type" : "Collection"
               },
               "sensitive" : false,
               "summary" : null,
               "tag" : [],
               "to" : [
                  "https://www.w3.org/ns/activitystreams#Public"
               ],
               "type" : "Note",
               "url" : "https://fosstodon.org/@xpil/107831067777241377"
            }
        "#;
        let ap_obj: ApObject = serde_json::from_str(ex).unwrap();
        let j = serde_json::to_string(&ap_obj).unwrap();
        let _ao2: ApObject = serde_json::from_str(&j).unwrap();
    }

    #[test]
    fn roundtrip_pleforma_example_test() {
        let ex = r#"
            {
               "@context" : [
                  "https://www.w3.org/ns/activitystreams",
                  "https://kiwifarms.cc/schemas/litepub-0.1.jsonld",
                  {
                     "@language" : "und"
                  }
               ],
               "actors" : "https://kiwifarms.cc/users/Harmful-if-Swallowed",
               "attachment" : [
                  {
                     "mediaType" : "image/png",
                     "name" : "",
                     "type" : "Document",
                     "url" : "https://kiwifarms.cc/media/9decd4556cac13af33b069d53520bdbfbda083d73fe8da26cf92a052b23db3c9.png"
                  }
               ],
               "attributedTo" : "https://kiwifarms.cc/users/Harmful-if-Swallowed",
               "cc" : [
                  "https://kiwifarms.cc/users/Harmful-if-Swallowed/followers"
               ],
               "content" : "<span class=\"h-card\"><a class=\"u-url mention\" data-users=\"A34BHSwtq9XQSf9M3c\" href=\"https://kiwifarms.cc/users/vinnegan\" rel=\"ugc\">@<span>vinnegan</span></a></span> <span class=\"h-card\"><a class=\"u-url mention\" data-users=\"A38VtEtI3uaDG561NA\" href=\"https://kiwifarms.cc/users/Not_a_cat\" rel=\"ugc\">@<span>Not_a_cat</span></a></span> <span class=\"h-card\"><a class=\"u-url mention\" data-users=\"A36kcmMTTYAz8hR6O0\" href=\"https://kiwifarms.cc/users/Pagliano\" rel=\"ugc\">@<span>Pagliano</span></a></span> archiving this already because you know the guy is gonna purge everything as per usual",
               "context" : "https://kiwifarms.cc/contexts/4628da99-30ad-4016-82db-686e1397ab85",
               "conversation" : "https://kiwifarms.cc/contexts/4628da99-30ad-4016-82db-686e1397ab85",
               "id" : "https://kiwifarms.cc/objects/3a10192b-6113-490c-9f66-c0a6615121fc",
               "published" : "2022-02-20T23:37:27.762136Z",
               "sensitive" : null,
               "source" : "@vinnegan @Not_a_cat @Pagliano archiving this already because you know the guy is gonna purge everything as per usual",
               "summary" : "",
               "tag" : [
                  {
                     "href" : "https://kiwifarms.cc/users/Not_a_cat",
                     "name" : "@Not_a_cat",
                     "type" : "Mention"
                  },
                  {
                     "href" : "https://kiwifarms.cc/users/Pagliano",
                     "name" : "@Pagliano",
                     "type" : "Mention"
                  },
                  {
                     "href" : "https://kiwifarms.cc/users/vinnegan",
                     "name" : "@vinnegan",
                     "type" : "Mention"
                  }
               ],
               "to" : [
                  "https://www.w3.org/ns/activitystreams#Public",
                  "https://kiwifarms.cc/users/Not_a_cat",
                  "https://kiwifarms.cc/users/Pagliano",
                  "https://kiwifarms.cc/users/vinnegan"
               ],
               "type" : "Note"
            }
        "#;
        let ap_obj: ApObject = serde_json::from_str(ex).unwrap();
        let j = serde_json::to_string(&ap_obj).unwrap();
        let _ao2: ApObject = serde_json::from_str(&j).unwrap();
    }
}