Added basic expansion logic and ability to deserialize single objects into vectors of that type

This commit is contained in:
emilis 2022-11-04 16:01:33 +00:00
parent 5d21e028db
commit 7b538c0a0c
8 changed files with 887 additions and 30 deletions

345
Cargo.lock generated
View File

@ -36,9 +36,9 @@ dependencies = [
[[package]]
name = "async-trait"
version = "0.1.57"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
dependencies = [
"proc-macro2",
"quote",
@ -211,6 +211,22 @@ dependencies = [
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.5"
@ -250,6 +266,15 @@ dependencies = [
"subtle",
]
[[package]]
name = "encoding_rs"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
"cfg-if",
]
[[package]]
name = "failure"
version = "0.1.8"
@ -278,12 +303,22 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "flabk"
version = "0.0.1"
dependencies = [
"anyhow",
"argon2",
"async-trait",
"axum",
"base-62",
"handlebars",
@ -291,6 +326,7 @@ dependencies = [
"mime_guess",
"rand",
"rand_core",
"reqwest",
"rust-embed",
"serde",
"serde_json",
@ -305,6 +341,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
@ -395,6 +446,25 @@ version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
[[package]]
name = "h2"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "handlebars"
version = "4.3.3"
@ -409,6 +479,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -477,6 +553,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
@ -490,6 +567,54 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "ipnet"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
[[package]]
name = "itoa"
version = "1.0.3"
@ -519,6 +644,12 @@ dependencies = [
"simple_asn1",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.132"
@ -602,6 +733,24 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "native-tls"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "num-bigint"
version = "0.2.6"
@ -683,6 +832,51 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -826,6 +1020,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "postgres-protocol"
version = "0.6.4"
@ -920,6 +1120,52 @@ dependencies = [
"bitflags",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "reqwest"
version = "0.11.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -990,12 +1236,45 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"windows-sys",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "serde"
version = "1.0.144"
@ -1177,6 +1456,20 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "thiserror"
version = "1.0.34"
@ -1262,6 +1555,16 @@ dependencies = [
"syn",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-postgres"
version = "0.7.7"
@ -1445,6 +1748,23 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
@ -1503,6 +1823,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
@ -1615,3 +1947,12 @@ name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0.64"
argon2 = "0.4.1"
async-trait = "0.1.58"
axum = "0.5.16"
base-62 = "0.1.1"
handlebars = "4.3.3"
@ -15,8 +16,17 @@ jsonwebtoken = "8.1.1"
mime_guess = "2.0.4"
rand = "0.8.5"
rand_core = { version = "0.6.3", features = ["std"] }
reqwest = { version = "0.11.12", features = [
"__tls",
"default-tls",
"hyper-tls",
"native-tls-crate",
"tokio-native-tls",
"serde_json",
"json",
] }
rust-embed = "6.4.0"
serde = { version = "1.0.144", features = ["derive", "std", "serde_derive"]}
serde = { version = "1.0.144", features = ["derive", "std", "serde_derive"] }
serde_json = "1.0.85"
tokio = { version = "1", features = ["full"] }
tokio-postgres = { version = "0.7.7", features = ["with-serde_json-1"] }

264
src/astreams/context.rs Normal file
View File

@ -0,0 +1,264 @@
use serde::Deserialize;
pub const CONTEXT_ID: &str = "https://www.w3.org/ns/activitystreams";
#[derive(Default, Debug, Clone, Deserialize)]
pub struct Context {
#[serde(rename = "@context")]
pub ctx: ContextMap,
}
#[derive(Default, Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ContextMap {
#[serde(rename = "@vocab")]
pub vocab: String,
#[serde(rename = "xsd")]
pub xsd: String,
#[serde(rename = "as")]
pub as_field: String,
#[serde(rename = "ldp")]
pub ldp: String,
#[serde(rename = "vcard")]
pub vcard: String,
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "type")]
pub type_field: String,
pub accept: String,
pub activity: String,
pub intransitive_activity: String,
pub add: String,
pub announce: String,
pub application: String,
pub arrive: String,
pub article: String,
pub audio: String,
pub block: String,
pub collection: String,
pub collection_page: String,
pub relationship: String,
pub create: String,
pub delete: String,
pub dislike: String,
pub document: String,
pub event: String,
pub follow: String,
pub flag: String,
pub group: String,
pub ignore: String,
pub image: String,
pub invite: String,
pub join: String,
pub leave: String,
pub like: String,
pub link: String,
pub mention: String,
pub note: String,
pub object: String,
pub offer: String,
pub ordered_collection: String,
pub ordered_collection_page: String,
pub organization: String,
pub page: String,
pub person: String,
pub place: String,
pub profile: String,
pub question: String,
pub reject: String,
pub remove: String,
pub service: String,
pub tentative_accept: String,
pub tentative_reject: String,
pub tombstone: String,
pub undo: String,
pub update: String,
pub video: String,
pub view: String,
pub listen: String,
pub read: String,
#[serde(rename = "Move")]
pub move_field: String,
pub travel: String,
pub is_following: String,
pub is_followed_by: String,
pub is_contact: String,
pub is_member: String,
#[serde(rename = "subject")]
pub subject: Option<TypedField>,
#[serde(rename = "relationship")]
pub relationship2: Option<TypedField>,
#[serde(rename = "actor")]
pub actor: Option<TypedField>,
#[serde(rename = "attributedTo")]
pub attributed_to: Option<TypedField>,
#[serde(rename = "attachment")]
pub attachment: Option<TypedField>,
#[serde(rename = "bcc")]
pub bcc: Option<TypedField>,
#[serde(rename = "bto")]
pub bto: Option<TypedField>,
#[serde(rename = "cc")]
pub cc: Option<TypedField>,
#[serde(rename = "context")]
pub context: Option<TypedField>,
#[serde(rename = "current")]
pub current: Option<TypedField>,
#[serde(rename = "first")]
pub first: Option<TypedField>,
#[serde(rename = "generator")]
pub generator: Option<TypedField>,
#[serde(rename = "icon")]
pub icon: Option<TypedField>,
#[serde(rename = "image")]
pub image2: Option<TypedField>,
#[serde(rename = "inReplyTo")]
pub in_reply_to: Option<TypedField>,
#[serde(rename = "items")]
pub items: Option<TypedField>,
#[serde(rename = "instrument")]
pub instrument: Option<TypedField>,
#[serde(rename = "orderedItems")]
pub ordered_items: Option<TypedField>,
#[serde(rename = "last")]
pub last: Option<TypedField>,
#[serde(rename = "location")]
pub location: Option<TypedField>,
#[serde(rename = "next")]
pub next: Option<TypedField>,
#[serde(rename = "object")]
pub object2: Option<TypedField>,
#[serde(rename = "oneOf")]
pub one_of: Option<TypedField>,
#[serde(rename = "anyOf")]
pub any_of: Option<TypedField>,
#[serde(rename = "closed")]
pub closed: Option<TypedField>,
#[serde(rename = "origin")]
pub origin: Option<TypedField>,
#[serde(rename = "accuracy")]
pub accuracy: Option<TypedField>,
#[serde(rename = "prev")]
pub prev: Option<TypedField>,
#[serde(rename = "preview")]
pub preview: Option<TypedField>,
#[serde(rename = "replies")]
pub replies: Option<TypedField>,
#[serde(rename = "result")]
pub result: Option<TypedField>,
#[serde(rename = "audience")]
pub audience: Option<TypedField>,
#[serde(rename = "partOf")]
pub part_of: Option<TypedField>,
#[serde(rename = "tag")]
pub tag: Option<TypedField>,
#[serde(rename = "target")]
pub target: Option<TypedField>,
#[serde(rename = "to")]
pub to: Option<TypedField>,
#[serde(rename = "url")]
pub url: Option<TypedField>,
#[serde(rename = "altitude")]
pub altitude: Option<TypedField>,
#[serde(rename = "content")]
pub content: String,
#[serde(rename = "contentMap")]
pub content_map: Option<TypedField>,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "nameMap")]
pub name_map: Option<TypedField>,
#[serde(rename = "duration")]
pub duration: Option<TypedField>,
#[serde(rename = "endTime")]
pub end_time: Option<TypedField>,
#[serde(rename = "height")]
pub height: Option<TypedField>,
#[serde(rename = "href")]
pub href: Option<TypedField>,
#[serde(rename = "hreflang")]
pub hreflang: String,
#[serde(rename = "latitude")]
pub latitude: Option<TypedField>,
#[serde(rename = "longitude")]
pub longitude: Option<TypedField>,
#[serde(rename = "mediaType")]
pub media_type: String,
#[serde(rename = "published")]
pub published: Option<TypedField>,
#[serde(rename = "radius")]
pub radius: Option<TypedField>,
#[serde(rename = "rel")]
pub rel: String,
#[serde(rename = "startIndex")]
pub start_index: Option<TypedField>,
#[serde(rename = "startTime")]
pub start_time: Option<TypedField>,
#[serde(rename = "summary")]
pub summary: String,
#[serde(rename = "summaryMap")]
pub summary_map: Option<TypedField>,
#[serde(rename = "totalItems")]
pub total_items: Option<TypedField>,
#[serde(rename = "units")]
pub units: String,
#[serde(rename = "updated")]
pub updated: Option<TypedField>,
#[serde(rename = "width")]
pub width: Option<TypedField>,
#[serde(rename = "describes")]
pub describes: Option<TypedField>,
#[serde(rename = "formerType")]
pub former_type: Option<TypedField>,
#[serde(rename = "deleted")]
pub deleted: Option<TypedField>,
#[serde(rename = "inbox")]
pub inbox: Option<TypedField>,
#[serde(rename = "outbox")]
pub outbox: Option<TypedField>,
#[serde(rename = "following")]
pub following: Option<TypedField>,
#[serde(rename = "followers")]
pub followers: Option<TypedField>,
#[serde(rename = "streams")]
pub streams: Option<TypedField>,
#[serde(rename = "preferredUsername")]
pub preferred_username: String,
#[serde(rename = "endpoints")]
pub endpoints: Option<TypedField>,
#[serde(rename = "uploadMedia")]
pub upload_media: Option<TypedField>,
#[serde(rename = "proxyUrl")]
pub proxy_url: Option<TypedField>,
#[serde(rename = "liked")]
pub liked: Option<TypedField>,
#[serde(rename = "oauthAuthorizationEndpoint")]
pub oauth_authorization_endpoint: Option<TypedField>,
#[serde(rename = "oauthTokenEndpoint")]
pub oauth_token_endpoint: Option<TypedField>,
#[serde(rename = "provideClientKey")]
pub provide_client_key: Option<TypedField>,
#[serde(rename = "signClientKey")]
pub sign_client_key: Option<TypedField>,
#[serde(rename = "sharedInbox")]
pub shared_inbox: Option<TypedField>,
pub public: Option<TypedField>,
#[serde(rename = "source")]
pub source: String,
#[serde(rename = "likes")]
pub likes: Option<TypedField>,
#[serde(rename = "shares")]
pub shares: Option<TypedField>,
#[serde(rename = "alsoKnownAs")]
pub also_known_as: Option<TypedField>,
}
#[derive(Default, Debug, Clone, Deserialize)]
pub struct TypedField {
#[serde(rename = "@id")]
pub id: String,
#[serde(rename = "@type")]
pub kind: Option<String>,
#[serde(rename = "@container")]
pub container: Option<String>,
}

95
src/astreams/mod.rs Normal file
View File

@ -0,0 +1,95 @@
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use self::{context::Context, note::Note, resolve::ResolveError, serde_ext::pull_single_into_vec};
pub mod context;
pub mod note;
pub mod resolve;
mod serde_ext;
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub enum ActivityKind {
Create,
Like,
Note,
}
#[derive(Debug, Clone)]
pub enum ExpandError {
InvalidKind(Option<ActivityKind>),
NoAttribution,
ResolveIRI(String),
Other(String),
}
impl From<ResolveError> for ExpandError {
fn from(err: ResolveError) -> Self {
Self::ResolveIRI(err.0)
}
}
#[async_trait]
pub trait ExpandLD<T> {
async fn expand(obj: &ObjectLD) -> Result<T, ExpandError>;
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ActivityLD {
#[serde(rename = "@context")]
pub context_uri: String,
pub kind: ActivityKind,
#[serde(rename = "actor")]
pub actor_uri: String,
#[serde(rename = "to")]
pub to_uris: Vec<String>,
pub object: Option<ObjectLD>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct ObjectLD {
#[serde(rename = "@context")]
pub context_uri: Option<String>,
pub id: String,
#[serde(rename = "type")]
pub kind: Option<ActivityKind>,
#[serde(rename = "attributedTo")]
pub attributed_to: Option<String>,
pub published: Option<String>,
pub content: Option<String>,
pub context: Option<String>,
pub conversation: Option<String>,
pub url: Option<String>,
#[serde(rename = "to")]
#[serde(deserialize_with = "pull_single_into_vec")]
pub to_uris: Vec<String>,
}
pub enum ActorType {}
#[derive(Debug, Clone)]
pub struct Actor {
id: String,
}
pub async fn test() {
let obj = r#"{
"@context": "https://www.w3.org/ns/activitystreams",
"attributedTo": "https://localhost/u/test",
"content": "honk donk",
"context": "data:,electrichonkytonk-2jqQ42HyJXctnBKTy1",
"conversation": "data:,electrichonkytonk-2jqQ42HyJXctnBKTy1",
"id": "https://localhost/u/test/h/6Q8BFF8W6PZT2ddngZ",
"published": "2022-09-30T19:04:45Z",
"summary": "",
"to": "https://www.w3.org/ns/activitystreams#Public",
"type": "Note",
"url": "https://localhost/u/test/h/6Q8BFF8W6PZT2ddngZ"
}"#;
let obj = serde_json::from_str::<ObjectLD>(obj).unwrap();
let unwrapped = Note::expand(&obj).await.unwrap();
println!("{:#?}", &unwrapped);
println!("to: {:#?}", obj.to_uris);
println!("{}", serde_json::to_string_pretty(&obj).unwrap());
}

64
src/astreams/note.rs Normal file
View File

@ -0,0 +1,64 @@
use async_trait::async_trait;
use super::{
context::{self, Context},
resolve, ActivityKind, Actor, ExpandError, ExpandLD, ObjectLD,
};
#[derive(Debug, Clone)]
pub struct Note {
pub id: String,
pub content: Option<String>,
pub context: Option<String>,
pub published_at: Option<String>,
pub author: Actor,
pub to: Vec<Actor>,
pub url: Option<String>,
pub conversation: Option<String>,
}
#[async_trait]
impl ExpandLD<Note> for Note {
async fn expand(obj: &ObjectLD) -> Result<Note, ExpandError> {
let obj = obj.clone();
let resolver = resolve::Resolver::new();
if let Some(kind) = obj.kind {
if let ActivityKind::Note = kind {
Ok(Note {
id: obj.id,
content: obj.content,
published_at: obj.published,
author: obj
.attributed_to
.map(|id| Actor { id })
.ok_or(ExpandError::NoAttribution)?,
to: obj.to_uris.into_iter().map(|id| Actor { id }).collect(),
url: obj.url,
context: obj.context,
conversation: obj.conversation,
})
} else {
Err(ExpandError::InvalidKind(Some(kind)))
}
} else {
Err(ExpandError::InvalidKind(None))
}
}
}
impl Into<ObjectLD> for Note {
fn into(self) -> ObjectLD {
ObjectLD {
context_uri: Some(context::CONTEXT_ID.to_string()),
id: self.id.clone(),
kind: Some(ActivityKind::Note),
attributed_to: Some(self.author.id),
published: self.published_at,
content: self.content,
context: self.context,
conversation: self.conversation,
url: Some(self.id),
to_uris: self.to.into_iter().map(|a| a.id).collect(),
}
}
}

66
src/astreams/resolve.rs Normal file
View File

@ -0,0 +1,66 @@
use std::{future::Future, string::FromUtf8Error};
use reqwest::{Method, StatusCode};
const LD_CONTENT_TYPE: &str = "application/ld+json";
pub(crate) struct ResolveError(pub String);
impl From<reqwest::Error> for ResolveError {
fn from(err: reqwest::Error) -> Self {
Self(err.to_string())
}
}
impl From<FromUtf8Error> for ResolveError {
fn from(e: FromUtf8Error) -> Self {
Self(format!(
"invalid schema format (tried utf8): {}",
e.to_string()
))
}
}
pub(super) struct Resolver {
client: reqwest::Client,
}
impl Resolver {
pub fn new() -> Self {
Self {
client: reqwest::ClientBuilder::new().build().unwrap(),
}
}
async fn get<Out, F, Fut>(&self, iri: String, ok: F) -> Result<Out, ResolveError>
where
F: FnOnce(reqwest::Response) -> Fut,
Fut: Future<Output = Result<Out, ResolveError>>,
{
let resp = self
.client
.request(Method::GET, iri)
.header("Accept", LD_CONTENT_TYPE)
.send()
.await?;
match resp.status() {
StatusCode::OK => Ok(ok(resp).await?),
status => Err(ResolveError(format!("non-ok status: {}", status))),
}
}
pub async fn resolve_into<'a, T: for<'de> serde::Deserialize<'de>>(
&self,
iri: String,
) -> Result<T, ResolveError> {
Ok(self
.get(iri, |resp| async { Ok(resp.json().await?) })
.await?)
}
pub async fn resolve(&self, iri: String) -> Result<Vec<u8>, ResolveError> {
Ok(self
.get(iri, |resp| async { Ok(resp.bytes().await?.to_vec()) })
.await?)
}
}

41
src/astreams/serde_ext.rs Normal file
View File

@ -0,0 +1,41 @@
use std::{fmt, marker::PhantomData};
use serde::{de, Deserialize, Deserializer};
// Allows deserialization of a single item into a vector of that item
// As long as they implement the From<String> trait
pub(super) fn pull_single_into_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: From<String> + serde::Deserialize<'de>,
{
struct VecComposer<T>(PhantomData<Vec<T>>);
impl<'de, T> de::Visitor<'de> for VecComposer<T>
where
T: From<String> + serde::Deserialize<'de>,
{
type Value = Vec<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let name = std::any::type_name::<T>();
formatter.write_str(format!("expected {} or Vec<{}>", name, name).as_str())
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![value.to_string().into()])
}
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
where
S: de::SeqAccess<'de>,
{
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor))
}
}
deserializer.deserialize_any(VecComposer(PhantomData))
}

View File

@ -1,41 +1,17 @@
#![feature(let_chains)]
mod astreams;
mod database;
mod sec;
mod servek;
mod svc;
use database::db::DB;
use serde::{Deserialize, Serialize};
use servek::servek::Server;
use svc::{auth::Auth, profiles::Profiler};
#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum ActivityKind {
Create,
Like,
Note,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ActivityLD {
pub context_uri: String,
pub kind: ActivityKind,
pub actor_uri: String,
pub to_uris: Vec<String>,
pub object: Option<ObjectLD>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ObjectLD {
pub context_uri: String,
pub id: Option<String>,
pub kind: Option<ActivityKind>,
pub attributed_to: Option<String>,
pub published: Option<String>,
pub content: Option<String>,
}
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
astreams::test().await;
let db = DB::new(
"localhost".to_owned(),
"flabk".to_owned(),