Initial commit
This commit is contained in:
commit
19471c125f
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
.idea
|
||||||
|
.vscode
|
|
@ -0,0 +1,156 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 = "itoa"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "magnetar"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.51"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.152"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.152"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.107"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-normalization"
|
||||||
|
version = "0.1.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "magnetar"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
url = "2.3"
|
|
@ -0,0 +1,6 @@
|
||||||
|
#[forbid(unsafe_code)]
|
||||||
|
mod web_model;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
struct JsonLD {}
|
|
@ -0,0 +1,109 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
pub mod jsonld;
|
||||||
|
pub mod webfinger;
|
||||||
|
|
||||||
|
trait ContentType: Serialize {
|
||||||
|
fn mime_type(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! content_type {
|
||||||
|
($visib:vis $typ:ident, $expression:expr) => {
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||||
|
$visib struct $typ;
|
||||||
|
|
||||||
|
impl AsRef<str> for $typ {
|
||||||
|
fn as_ref(&self) -> &'static str {
|
||||||
|
$expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for $typ {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serializer.serialize_str($expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentType for $typ {
|
||||||
|
fn mime_type(&self) -> &'static str {
|
||||||
|
$expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for $typ {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let content_type = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
if matches!(content_type.as_ref(), $expression) {
|
||||||
|
Ok(Self)
|
||||||
|
} else {
|
||||||
|
Err(Error::custom(format!(
|
||||||
|
"Invalid content type: {content_type}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod content_type {
|
||||||
|
use crate::web_model::ContentType;
|
||||||
|
use serde::de::Error;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
content_type!(pub ContentActivityPlusJson, "application/activity+json");
|
||||||
|
content_type!(pub ContentHtml, "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! link_rel {
|
||||||
|
($visib:vis $typ:ident, $expression:expr) => {
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||||
|
$visib struct $typ;
|
||||||
|
|
||||||
|
impl AsRef<str> for $typ {
|
||||||
|
fn as_ref(&self) -> &'static str {
|
||||||
|
$expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for $typ {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serializer.serialize_str($expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rel for $typ {
|
||||||
|
fn rel(&self) -> &'static str {
|
||||||
|
$expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for $typ {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let rel = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
if matches!(rel.as_ref(), $expression) {
|
||||||
|
Ok(Self)
|
||||||
|
} else {
|
||||||
|
Err(Error::custom(format!(
|
||||||
|
"Invalid rel: {rel}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Rel: Serialize {
|
||||||
|
fn rel(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rel {
|
||||||
|
use crate::web_model::Rel;
|
||||||
|
use serde::de::Error;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
link_rel!(pub RelWebFingerProfilePage, "http://webfinger.net/rel/profile-page");
|
||||||
|
link_rel!(pub RelSelf, "self");
|
||||||
|
link_rel!(pub RelOStatusSubscribe, "http://ostatus.org/schema/1.0/subscribe");
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
use crate::web_model::content_type::{ContentActivityPlusJson, ContentHtml};
|
||||||
|
use crate::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
|
||||||
|
use serde::de::Error;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
struct WebFinger {
|
||||||
|
subject: WebFingerSubject,
|
||||||
|
aliases: Vec<WebFingerSubject>,
|
||||||
|
links: Vec<WebFingerRel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum WebFingerSubject {
|
||||||
|
Acct(Acct),
|
||||||
|
Url(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum WebFingerRel {
|
||||||
|
RelWebFingerProfilePage {
|
||||||
|
rel: RelWebFingerProfilePage,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
content_type: ContentHtml,
|
||||||
|
href: String,
|
||||||
|
},
|
||||||
|
RelSelf {
|
||||||
|
rel: RelSelf,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
content_type: ContentActivityPlusJson,
|
||||||
|
href: String,
|
||||||
|
},
|
||||||
|
RelOStatusSubscribe {
|
||||||
|
rel: RelOStatusSubscribe,
|
||||||
|
template: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct Acct(String);
|
||||||
|
|
||||||
|
impl AsRef<str> for Acct {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Acct {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("acct:{}", self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Acct {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let acct = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
if let Some(rem) = acct.strip_prefix("acct:") {
|
||||||
|
Ok(Acct(rem.to_owned()))
|
||||||
|
} else {
|
||||||
|
Err(Error::custom(
|
||||||
|
"Missing acct protocol for account!".to_owned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::web_model::content_type::{ContentActivityPlusJson, ContentHtml};
|
||||||
|
use crate::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
|
||||||
|
use crate::web_model::webfinger::WebFingerSubject::Url;
|
||||||
|
use crate::web_model::webfinger::{Acct, WebFinger, WebFingerRel, WebFingerSubject};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_remove_acct_prefix() {
|
||||||
|
let json = json!("acct:natty@tech.lgbt");
|
||||||
|
|
||||||
|
let acct: Acct = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(acct, Acct("natty@tech.lgbt".to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_add_acct_prefix() {
|
||||||
|
let acct = Acct("natty@tech.lgbt".to_owned());
|
||||||
|
let json = serde_json::to_value(acct).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(json, json!("acct:natty@tech.lgbt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_webfinger() {
|
||||||
|
let json = json!({
|
||||||
|
"subject": "acct:natty@tech.lgbt",
|
||||||
|
"aliases": [
|
||||||
|
"https://tech.lgbt/@natty",
|
||||||
|
"https://tech.lgbt/users/natty"
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "http://webfinger.net/rel/profile-page",
|
||||||
|
"type": "text/html",
|
||||||
|
"href": "https://tech.lgbt/@natty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "self",
|
||||||
|
"type": "application/activity+json",
|
||||||
|
"href": "https://tech.lgbt/users/natty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "http://ostatus.org/schema/1.0/subscribe",
|
||||||
|
"template": "https://tech.lgbt/authorize_interaction?uri={uri}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let webfinger: WebFinger = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
let real = WebFinger {
|
||||||
|
subject: WebFingerSubject::Acct(Acct("natty@tech.lgbt".to_owned())),
|
||||||
|
aliases: vec![
|
||||||
|
Url("https://tech.lgbt/@natty".to_owned()),
|
||||||
|
Url("https://tech.lgbt/users/natty".to_owned()),
|
||||||
|
],
|
||||||
|
links: vec![
|
||||||
|
WebFingerRel::RelWebFingerProfilePage {
|
||||||
|
rel: RelWebFingerProfilePage,
|
||||||
|
content_type: ContentHtml,
|
||||||
|
href: "https://tech.lgbt/@natty".to_owned(),
|
||||||
|
},
|
||||||
|
WebFingerRel::RelSelf {
|
||||||
|
rel: RelSelf,
|
||||||
|
content_type: ContentActivityPlusJson,
|
||||||
|
href: "https://tech.lgbt/users/natty".to_owned(),
|
||||||
|
},
|
||||||
|
WebFingerRel::RelOStatusSubscribe {
|
||||||
|
rel: RelOStatusSubscribe,
|
||||||
|
template: "https://tech.lgbt/authorize_interaction?uri={uri}".to_owned(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(webfinger, real)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue