feat: add simple models

actor, webfinger, activity, object
thanks gargron
https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
This commit is contained in:
əlemi 2023-12-30 05:08:05 +01:00
parent 43a5006f5f
commit c69027638f
Signed by: alemi
GPG key ID: A4895B84D311642C
7 changed files with 219 additions and 0 deletions

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "anwt"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.31", features = ["serde"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"

5
src/main.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod model;
fn main() {
println!("Hello, world!");
}

45
src/model/activity.rs Normal file
View file

@ -0,0 +1,45 @@
use serde::{Serialize, Deserialize};
use super::object::Object;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Activity {
#[serde(rename = "@context")]
context: String,
id: String,
#[serde(rename = "type")]
activity_type: ActivityType,
actor: String,
object: Object,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ActivityType {
Create,
}
#[cfg(test)]
mod test {
use super::{Activity, ActivityType};
use crate::model::object::Object;
#[test]
fn activity_serializes_as_expected() {
let activity = Activity {
context: "https://www.w3.org/ns/activitystreams".into(),
id: "https://my-example.com/create-hello-world".into(),
activity_type: ActivityType::Create,
actor: "https://my-example.com/actor".into(),
object: Object::default(),
};
let serialized_activity = serde_json::to_string(&activity).unwrap();
let expected_serialized_activity = "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://my-example.com/create-hello-world\",\"type\":\"Create\",\"actor\":\"https://my-example.com/actor\",\"object\":{\"id\":\"https://my-example.com/hello-world\",\"type\":\"Note\",\"published\":\"2018-06-23T17:17:11Z\",\"attributedTo\":\"https://my-example.com/actor\",\"inReplyTo\":\"https://mastodon.social/@Gargron/100254678717223630\",\"content\":\"<p>Hello world</p>\",\"to\":\"https://www.w3.org/ns/activitystreams#Public\"}}";
assert_eq!(serialized_activity, expected_serialized_activity);
}
}

57
src/model/actor.rs Normal file
View file

@ -0,0 +1,57 @@
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Actor {
#[serde(rename = "@context")]
pub context: Vec<String>, // note: must be @context
pub id: String,
#[serde(rename = "type")]
pub actor_type: ActorType,
#[serde(rename = "preferredUsername")]
pub preferred_username: String,
pub inbox: String,
#[serde(rename = "publicKey")]
pub public_key: PublicKey,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicKey {
pub id: String,
pub owner: String,
#[serde(rename = "publicKeyPem")]
pub public_key_pem: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ActorType {
Person,
}
#[cfg(test)]
mod test {
use super::{Actor, ActorType, PublicKey};
#[test]
fn actor_serializes_as_expected() {
let actor = Actor {
context: vec![
"https://www.w3.org/ns/activitystreams".into(),
"https://w3id.org/security/v1".into()
],
id: "https://my-example.com/actor".into(),
actor_type: ActorType::Person,
preferred_username: "alice".into(),
inbox: "https://my-example.com/inbox".into(),
public_key: PublicKey {
id: "https://my-example.com/actor#main-key".into(),
owner: "https://my-example.com/actor".into(),
public_key_pem: "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----".into(),
},
};
let serialized_actor = serde_json::to_string(&actor).unwrap();
let expected_serialized_actor = "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\"],\"id\":\"https://my-example.com/actor\",\"type\":\"Person\",\"preferredUsername\":\"alice\",\"inbox\":\"https://my-example.com/inbox\",\"publicKey\":{\"id\":\"https://my-example.com/actor#main-key\",\"owner\":\"https://my-example.com/actor\",\"publicKeyPem\":\"-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----\"}}";
assert_eq!(expected_serialized_actor, serialized_actor);
}
}

4
src/model/mod.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod activity;
pub mod actor;
pub mod webfinger;
pub mod object;

57
src/model/object.rs Normal file
View file

@ -0,0 +1,57 @@
use chrono::{Utc, DateTime};
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Object {
id: String,
#[serde(rename = "type")]
object_type: ObjectType,
published: DateTime<Utc>,
#[serde(rename = "attributedTo")]
attributed_to: String,
#[serde(rename = "inReplyTo")]
in_reply_to: String,
content: String,
to: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ObjectType {
Note,
}
#[cfg(test)]
impl Default for Object {
fn default() -> Self {
Object {
id: "https://my-example.com/hello-world".into(),
object_type: ObjectType::Note,
published: DateTime::parse_from_rfc3339("2018-06-23T17:17:11Z").unwrap().into(),
attributed_to: "https://my-example.com/actor".into(),
in_reply_to: "https://mastodon.social/@Gargron/100254678717223630".into(),
content: "<p>Hello world</p>".into(),
to: "https://www.w3.org/ns/activitystreams#Public".into(),
}
}
}
#[cfg(test)]
mod test {
use super::Object;
#[test]
fn object_serializes_as_expected() {
let object = Object::default();
let serialized_object = serde_json::to_string(&object).unwrap();
let expected_serialized_object = "{\"id\":\"https://my-example.com/hello-world\",\"type\":\"Note\",\"published\":\"2018-06-23T17:17:11Z\",\"attributedTo\":\"https://my-example.com/actor\",\"inReplyTo\":\"https://mastodon.social/@Gargron/100254678717223630\",\"content\":\"<p>Hello world</p>\",\"to\":\"https://www.w3.org/ns/activitystreams#Public\"}";
assert_eq!(serialized_object, expected_serialized_object);
}
}

40
src/model/webfinger.rs Normal file
View file

@ -0,0 +1,40 @@
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Webfinger {
subject: String,
links: Vec<WebfingerLink>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebfingerLink {
rel: String,
#[serde(rename = "type")]
link_type: String,
href: String,
}
#[cfg(test)]
mod test {
use super::{Webfinger, WebfingerLink};
#[test]
fn webfinger_serializes_as_expected() {
let webfinger = Webfinger {
subject: "acct:alice@my-example.com".into(),
links: vec![
WebfingerLink {
rel: "self".into(),
link_type: "application/activity+json".into(),
href: "https://my-example.com/actor".into(),
},
],
};
let serialized_webfinger = serde_json::to_string(&webfinger).unwrap();
let expected_serialized_webfinger = "{\"subject\":\"acct:alice@my-example.com\",\"links\":[{\"rel\":\"self\",\"type\":\"application/activity+json\",\"href\":\"https://my-example.com/actor\"}]}";
assert_eq!(serialized_webfinger, expected_serialized_webfinger);
}
}