Added basic ActivityStreams context parsing
This commit is contained in:
parent
322619e82d
commit
3e2849929d
|
@ -153,4 +153,5 @@ dependencies = [
|
|||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
|
|
@ -7,4 +7,4 @@ edition = "2021"
|
|||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
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;
|
||||
|
||||
trait ContentType: Serialize {
|
||||
|
@ -51,8 +55,11 @@ pub mod content_type {
|
|||
use serde::de::Error;
|
||||
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 ContentJson, "application/json");
|
||||
content_type!(pub ContentMultipartFormData, "multipart/form-data");
|
||||
content_type!(pub ContentUrlEncoded, "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
macro_rules! link_rel {
|
||||
|
@ -107,3 +114,36 @@ pub mod rel {
|
|||
link_rel!(pub RelSelf, "self");
|
||||
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 serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
@ -30,7 +30,7 @@ enum WebFingerRel {
|
|||
RelSelf {
|
||||
rel: RelSelf,
|
||||
#[serde(rename = "type")]
|
||||
content_type: ContentActivityPlusJson,
|
||||
content_type: ContentActivityStreams,
|
||||
href: String,
|
||||
},
|
||||
RelOStatusSubscribe {
|
||||
|
@ -73,7 +73,7 @@ impl<'de> Deserialize<'de> for Acct {
|
|||
|
||||
#[cfg(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::webfinger::WebFingerSubject::Url;
|
||||
use crate::web_model::webfinger::{Acct, WebFinger, WebFingerRel, WebFingerSubject};
|
||||
|
@ -138,7 +138,7 @@ mod test {
|
|||
},
|
||||
WebFingerRel::RelSelf {
|
||||
rel: RelSelf,
|
||||
content_type: ContentActivityPlusJson,
|
||||
content_type: ContentActivityStreams,
|
||||
href: "https://tech.lgbt/users/natty".to_owned(),
|
||||
},
|
||||
WebFingerRel::RelOStatusSubscribe {
|
||||
|
|
Loading…
Reference in New Issue