Added basic ActivityStreams context parsing
This commit is contained in:
parent
322619e82d
commit
3e2849929d
|
@ -153,4 +153,5 @@ dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,4 +7,4 @@ edition = "2021"
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
url = "2.3"
|
url = { version = "2.3", features = ["serde"] }
|
|
@ -0,0 +1,151 @@
|
||||||
|
use crate::web_model::ListContaining;
|
||||||
|
use serde::de::Error;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||||
|
pub struct ContextActivityStreams;
|
||||||
|
|
||||||
|
impl AsRef<str> for ContextActivityStreams {
|
||||||
|
fn as_ref(&self) -> &'static str {
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ContextActivityStreams {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serializer.serialize_str(self.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ContextActivityStreams {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if matches!(
|
||||||
|
s,
|
||||||
|
"https://www.w3.org/ns/activitystreams" | "http://www.w3.org/ns/activitystreams"
|
||||||
|
) {
|
||||||
|
Ok(Self)
|
||||||
|
} else {
|
||||||
|
Err(format!("Invalid context: {s}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ContextActivityStreams {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let context = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
ContextActivityStreams::from_str(&context).map_err(Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Context {
|
||||||
|
String(ContextActivityStreams),
|
||||||
|
Object {
|
||||||
|
#[serde(rename = "@vocab")]
|
||||||
|
ld_vocab: ContextActivityStreams,
|
||||||
|
},
|
||||||
|
List(ListContaining<ContextActivityStreams>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self {
|
||||||
|
Context::String(ContextActivityStreams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
|
struct ActivityStreamsDocument<T> {
|
||||||
|
#[serde(rename = "@context", default)]
|
||||||
|
ld_context: Context,
|
||||||
|
#[serde(flatten)]
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::web_model::activity_streams::{
|
||||||
|
ActivityStreamsDocument, Context, ContextActivityStreams,
|
||||||
|
};
|
||||||
|
use crate::web_model::ListContaining;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_context() {
|
||||||
|
let json = json!({
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"some": "stuff"
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(doc.ld_context, Context::String(ContextActivityStreams));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_missing_context() {
|
||||||
|
let json = json!({
|
||||||
|
"some": "stuff"
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(doc.ld_context, Context::String(ContextActivityStreams));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_context_http() {
|
||||||
|
let json = json!({
|
||||||
|
"@context": "http://www.w3.org/ns/activitystreams",
|
||||||
|
"some": "stuff"
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(doc.ld_context, Context::String(ContextActivityStreams));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_context_vocab() {
|
||||||
|
let json = json!({
|
||||||
|
"@context": {
|
||||||
|
"@vocab": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"some": "stuff"
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
doc.ld_context,
|
||||||
|
Context::Object {
|
||||||
|
ld_vocab: ContextActivityStreams
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_context_array() {
|
||||||
|
let json = json!({
|
||||||
|
"@context": [
|
||||||
|
{
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
],
|
||||||
|
"some": "stuff"
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
doc.ld_context,
|
||||||
|
Context::List(ListContaining(ContextActivityStreams))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
struct JsonLD {}
|
|
|
@ -1,6 +1,10 @@
|
||||||
use serde::Serialize;
|
use serde::de::Error;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub mod jsonld;
|
pub mod activity_streams;
|
||||||
pub mod webfinger;
|
pub mod webfinger;
|
||||||
|
|
||||||
trait ContentType: Serialize {
|
trait ContentType: Serialize {
|
||||||
|
@ -51,8 +55,11 @@ pub mod content_type {
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
content_type!(pub ContentActivityPlusJson, "application/activity+json");
|
content_type!(pub ContentActivityStreams, "application/activity+json");
|
||||||
content_type!(pub ContentHtml, "text/html");
|
content_type!(pub ContentHtml, "text/html");
|
||||||
|
content_type!(pub ContentJson, "application/json");
|
||||||
|
content_type!(pub ContentMultipartFormData, "multipart/form-data");
|
||||||
|
content_type!(pub ContentUrlEncoded, "application/x-www-form-urlencoded");
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! link_rel {
|
macro_rules! link_rel {
|
||||||
|
@ -107,3 +114,36 @@ pub mod rel {
|
||||||
link_rel!(pub RelSelf, "self");
|
link_rel!(pub RelSelf, "self");
|
||||||
link_rel!(pub RelOStatusSubscribe, "http://ostatus.org/schema/1.0/subscribe");
|
link_rel!(pub RelOStatusSubscribe, "http://ostatus.org/schema/1.0/subscribe");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
pub struct ListContaining<T: Clone + Eq + PartialEq + Debug + AsRef<str> + FromStr>(pub T);
|
||||||
|
|
||||||
|
impl<T> Serialize for ListContaining<T>
|
||||||
|
where
|
||||||
|
T: Clone + Eq + PartialEq + Debug + AsRef<str> + FromStr,
|
||||||
|
{
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
vec![AsRef::<str>::as_ref(&self.0)].serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, T> Deserialize<'de> for ListContaining<T>
|
||||||
|
where
|
||||||
|
T: Clone + Eq + PartialEq + Debug + AsRef<str> + FromStr,
|
||||||
|
{
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let data = Vec::<Value>::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
let dt = data
|
||||||
|
.iter()
|
||||||
|
.filter_map(Value::as_str)
|
||||||
|
.filter_map(|val| T::from_str(val).ok())
|
||||||
|
.next();
|
||||||
|
|
||||||
|
if let Some(value) = dt {
|
||||||
|
Ok(ListContaining(value))
|
||||||
|
} else {
|
||||||
|
Err(Error::custom("Count not find item in list.".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::web_model::content_type::{ContentActivityPlusJson, ContentHtml};
|
use crate::web_model::content_type::{ContentActivityStreams, ContentHtml};
|
||||||
use crate::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
|
use crate::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
@ -30,7 +30,7 @@ enum WebFingerRel {
|
||||||
RelSelf {
|
RelSelf {
|
||||||
rel: RelSelf,
|
rel: RelSelf,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
content_type: ContentActivityPlusJson,
|
content_type: ContentActivityStreams,
|
||||||
href: String,
|
href: String,
|
||||||
},
|
},
|
||||||
RelOStatusSubscribe {
|
RelOStatusSubscribe {
|
||||||
|
@ -73,7 +73,7 @@ impl<'de> Deserialize<'de> for Acct {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::web_model::content_type::{ContentActivityPlusJson, ContentHtml};
|
use crate::web_model::content_type::{ContentActivityStreams, ContentHtml};
|
||||||
use crate::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
|
use crate::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
|
||||||
use crate::web_model::webfinger::WebFingerSubject::Url;
|
use crate::web_model::webfinger::WebFingerSubject::Url;
|
||||||
use crate::web_model::webfinger::{Acct, WebFinger, WebFingerRel, WebFingerSubject};
|
use crate::web_model::webfinger::{Acct, WebFinger, WebFingerRel, WebFingerSubject};
|
||||||
|
@ -138,7 +138,7 @@ mod test {
|
||||||
},
|
},
|
||||||
WebFingerRel::RelSelf {
|
WebFingerRel::RelSelf {
|
||||||
rel: RelSelf,
|
rel: RelSelf,
|
||||||
content_type: ContentActivityPlusJson,
|
content_type: ContentActivityStreams,
|
||||||
href: "https://tech.lgbt/users/natty".to_owned(),
|
href: "https://tech.lgbt/users/natty".to_owned(),
|
||||||
},
|
},
|
||||||
WebFingerRel::RelOStatusSubscribe {
|
WebFingerRel::RelOStatusSubscribe {
|
||||||
|
|
Loading…
Reference in New Issue