use crate::ap::image_attr::ImageAttributes;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
pub enum ActorType {
Application,
Group,
Organization,
Person,
Service,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Actor {
#[serde(rename = "type")]
pub ap_type: ActorType,
pub id: String,
#[serde(rename = "preferredUsername")]
pub preferred_user_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icon: Option<ImageAttributes>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<ImageAttributes>,
#[serde(skip_serializing_if = "Option::is_none")]
pub following: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub followers: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub liked: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub likes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inbox: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub outbox: Option<String>,
#[serde(rename = "publicKey")]
pub public_key: PublicKey,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoints: Option<Endpoints>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Endpoints {
#[serde(rename = "sharedInbox")]
#[serde(skip_serializing_if = "Option::is_none")]
pub shared_inbox: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct PublicKey {
pub id: String,
pub owner: String,
#[serde(rename = "publicKeyPem")]
pub public_key_pem: String,
}
#[cfg(test)]
mod actor_tests {
use super::*;
use crate::ap::image_attr::ImageAttributes;
#[test]
fn roundtrip_serde() {
let a = super::Actor {
ap_type: ActorType::Person,
id: "http://example.com/foo".to_string(),
preferred_user_name: "foo".to_string(),
name: Option::from("foooo chillin".to_string()),
summary: Option::from("I am the foo".to_string()),
icon: Option::from(ImageAttributes::SingleUrl(
"http://example.com/icon".to_string(),
)),
image: Option::from(ImageAttributes::SingleUrl(
"https://example.com/image".to_string(),
)),
following: None,
followers: None,
liked: None,
likes: None,
inbox: None,
outbox: None,
public_key: PublicKey {
id: "https://example.com/foo#key1".to_string(),
owner: "https://example.com/foo".to_string(),
public_key_pem: "-----BEGIN PUBLIC KEY----- lslslslssllsslsl".to_string(),
},
endpoints: None,
};
let j = serde_json::to_string(&a);
let a2 = serde_json::from_str(&j.unwrap()).unwrap();
assert_eq!(a, a2)
}
#[test]
fn deserialize_without_optional_attributes() {
let a = r#"
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "ja"}],
"type": "Person",
"id": "https://kenzoishii.example.com/",
"preferredUsername": "kenzoishii",
"publicKey" : {
"id": "https://kenzoishii.example.com/foo#key1",
"owner": "https://kenzoishii.example.com/foo",
"publicKeyPem": "-----BEGIN PUBLIC KEY----- lcllllll"
}
}
"#;
let j: Actor = serde_json::from_str(a).unwrap();
assert_eq!(None, j.name);
assert_eq!(None, j.summary);
assert_eq!(None, j.icon);
}
#[test]
fn deserialize_with_icons() {
let a = r#"
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "ja"}],
"type": "Person",
"id": "https://kenzoishii.example.com/",
"preferredUsername": "kenzoishii",
"icon": "https://example.com/foo.png",
"publicKey" : {
"id": "https://kenzoishii.example.com/foo#key1",
"owner": "https://kenzoishii.example.com/foo",
"publicKeyPem": "-----BEGIN PUBLIC KEY----- lcllllll"
}
}
"#;
let j: Actor = serde_json::from_str(a).unwrap();
assert_eq!(None, j.name);
assert_eq!(None, j.summary);
assert_eq!(
Some(ImageAttributes::SingleUrl(
"https://example.com/foo.png".to_string()
)),
j.icon
);
}
#[test]
fn roundtrip_pleroma_example_test() {
let ex = r#"
{
"@context" : [
"https://www.w3.org/ns/activitystreams",
"https://kiwifarms.cc/schemas/litepub-0.1.jsonld",
{
"@language" : "und"
}
],
"alsoKnownAs" : [],
"attachment" : [],
"capabilities" : {
"acceptsChatMessages" : true
},
"discoverable" : false,
"endpoints" : {
"oauthAuthorizationEndpoint" : "https://kiwifarms.cc/oauth/authorize",
"oauthRegistrationEndpoint" : "https://kiwifarms.cc/api/v1/apps",
"oauthTokenEndpoint" : "https://kiwifarms.cc/oauth/token",
"sharedInbox" : "https://kiwifarms.cc/inbox",
"uploadMedia" : "https://kiwifarms.cc/api/ap/upload_media"
},
"followers" : "https://kiwifarms.cc/users/nxdomain/followers",
"following" : "https://kiwifarms.cc/users/nxdomain/following",
"icon" : {
"height" : 400,
"name" : "Note (400x400)",
"type" : "Image",
"url" : "https://apb.betabuild.dev/media/remote/2021/212/16-10-6916,kiwifarms.cc-109878132-jpg",
"width" : 400
},
"id" : "https://kiwifarms.cc/users/nxdomain",
"image" : {
"name" : "Banner Image",
"type" : "Image",
"url" : "https://apb.betabuild.dev/media/remote/2021/212/16-11-8141,kiwifarms.cc-1491114090-jpg"
},
"inbox" : "https://kiwifarms.cc/users/nxdomain/inbox",
"manuallyApprovesFollowers" : false,
"name" : "Loyal Subject of the CrunkLord",
"outbox" : "https://kiwifarms.cc/users/nxdomain/outbox",
"preferredUsername" : "nxdomain",
"publicKey" : {
"id" : "https://kiwifarms.cc/users/nxdomain#main-key",
"owner" : "https://kiwifarms.cc/users/nxdomain",
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw6lTHHUjvw6912osmLIO\nHkaRyQ+wITeXc4FdwLPUn8Zaqhek/VYdFlTbU8mN1SHuoGNoXM9jekAfvlpEEwKZ\nYfZszRPucKKYpFew9Y1TWWaGWZEi78n+inNXMM6djPqS7QZGJmGz+BDWyelnUk+x\nk6ywJSmvy/KEVYxiZH+TON9kMObOWfzclta9opPYJpyoZmxPAbZfwRp61lWiXAHr\nQYbmpTZePTTE3iCbEUt43QoG/+z1DbOJ7orzlviqWrhqApiagwKXvIsxSE3SBtzb\nIl94DBuDa7lR9x+4uaqLc/t5lxwf5NvfeSMBD/7U5Jba8xpEFbDSEwGAlU4HH79t\ntwIDAQAB\n-----END PUBLIC KEY-----\n\n"
},
"summary" : "I came here to harsh my mellow.\n<br>\n<br>Not my first rodeo nor my first fedi account. I'm just running from my past.\n<br>\n<br>He/Him/You/Me/Everybody/Everybody",
"tag" : [],
"type" : "Person",
"url" : "https://kiwifarms.cc/users/nxdomain"
}
"#;
let actor: Actor = serde_json::from_str(ex).unwrap();
let j = serde_json::to_string(&actor).unwrap();
let _a2: Actor = serde_json::from_str(&j).unwrap();
}
#[test]
fn roundtrip_mastodon_example_test() {
let ex = r#"
{
"@context" : [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"Curve25519Key" : "toot:Curve25519Key",
"Device" : "toot:Device",
"Ed25519Key" : "toot:Ed25519Key",
"Ed25519Signature" : "toot:Ed25519Signature",
"EncryptedMessage" : "toot:EncryptedMessage",
"IdentityProof" : "toot:IdentityProof",
"PropertyValue" : "schema:PropertyValue",
"alsoKnownAs" : {
"@id" : "as:alsoKnownAs",
"@type" : "@id"
},
"cipherText" : "toot:cipherText",
"claim" : {
"@id" : "toot:claim",
"@type" : "@id"
},
"deviceId" : "toot:deviceId",
"devices" : {
"@id" : "toot:devices",
"@type" : "@id"
},
"discoverable" : "toot:discoverable",
"featured" : {
"@id" : "toot:featured",
"@type" : "@id"
},
"featuredTags" : {
"@id" : "toot:featuredTags",
"@type" : "@id"
},
"fingerprintKey" : {
"@id" : "toot:fingerprintKey",
"@type" : "@id"
},
"focalPoint" : {
"@container" : "@list",
"@id" : "toot:focalPoint"
},
"identityKey" : {
"@id" : "toot:identityKey",
"@type" : "@id"
},
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
"messageFranking" : "toot:messageFranking",
"messageType" : "toot:messageType",
"movedTo" : {
"@id" : "as:movedTo",
"@type" : "@id"
},
"publicKeyBase64" : "toot:publicKeyBase64",
"schema" : "http://schema.org#",
"suspended" : "toot:suspended",
"toot" : "http://joinmastodon.org/ns#",
"value" : "schema:value"
}
],
"attachment" : [],
"devices" : "https://fosstodon.org/users/rcode3/collections/devices",
"discoverable" : false,
"endpoints" : {
"sharedInbox" : "https://fosstodon.org/inbox"
},
"featured" : "https://fosstodon.org/users/rcode3/collections/featured",
"featuredTags" : "https://fosstodon.org/users/rcode3/collections/tags",
"followers" : "https://fosstodon.org/users/rcode3/followers",
"following" : "https://fosstodon.org/users/rcode3/following",
"icon" : {
"mediaType" : "image/png",
"type" : "Image",
"url" : "https://fosstodon.b-cdn.net/accounts/avatars/000/317/625/original/35b6bcca67d12842.png"
},
"id" : "https://fosstodon.org/users/rcode3",
"image" : {
"mediaType" : "image/jpeg",
"type" : "Image",
"url" : "https://fosstodon.b-cdn.net/accounts/headers/000/317/625/original/21617028d8237f84.jpg"
},
"inbox" : "https://fosstodon.org/users/rcode3/inbox",
"manuallyApprovesFollowers" : false,
"name" : "RCODE3",
"outbox" : "https://fosstodon.org/users/rcode3/outbox",
"preferredUsername" : "rcode3",
"publicKey" : {
"id" : "https://fosstodon.org/users/rcode3#main-key",
"owner" : "https://fosstodon.org/users/rcode3",
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxWvsQskS6Y0We0wD6n22\nZLiDive0EyqBFIsgGx/Hp14lEd9FhkY50B5bqF+uqESrvxcp9pycxBFWA3t/dP8P\nC6lIcxLzTl/tIiyHFjXx+BFZ0WiiMKzhLVauaAx63zr3jwTtPKy0/Rfm+MRZ6y0a\nTzF/1PhuAscPRUzwKRFGEN/E7jh8evkMPTtGbXIce5XJkCivJ4Wt3rGZNUwHKo1O\nwvaCzEU6W8IciFO/Bw0h6iGOJJ6r8D0KJ9cIT1pUYTkJgLV9ahPz4u45AeO4tnvP\nR3YdDKM/hiqWmjpJ2m8FYYR3kLpPXshmRtB20ofCirf7dT3/RkY6uTE8pxWmm+oZ\noQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"published" : "2021-02-24T00:00:00Z",
"summary" : "<p>Something clever goes here.</p>",
"tag" : [],
"type" : "Person",
"url" : "https://fosstodon.org/@rcode3"
}
"#;
let actor: Actor = serde_json::from_str(ex).unwrap();
let j = serde_json::to_string(&actor).unwrap();
let _a2: Actor = serde_json::from_str(&j).unwrap();
}
}