added resolve trait, fixed deserialization for vector types
This commit is contained in:
parent
5473512787
commit
d868170084
|
@ -3,16 +3,18 @@ use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
|
|
||||||
#[proc_macro_derive(IRI)]
|
#[proc_macro_derive(LD)]
|
||||||
pub fn derive_iri(input: TokenStream) -> TokenStream {
|
pub fn derive_ld(input: TokenStream) -> TokenStream {
|
||||||
let DeriveInput { ident, .. } = parse_macro_input!(input);
|
let DeriveInput { ident, .. } = parse_macro_input!(input);
|
||||||
quote! {
|
quote! {
|
||||||
impl std::str::FromStr for #ident {
|
impl crate::astreams::serde_ext::LDObject for #ident {
|
||||||
type Err = ();
|
fn from_iri(s: &str) -> Self {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let mut ident = #ident::default();
|
let mut ident = #ident::default();
|
||||||
ident.id = s.into();
|
ident.id = s.into();
|
||||||
Ok(ident)
|
ident
|
||||||
|
}
|
||||||
|
fn get_iri(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,26 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::serde_ext::LDObject;
|
||||||
|
|
||||||
pub const CONTEXT_ID: &str = "https://www.w3.org/ns/activitystreams";
|
pub const CONTEXT_ID: &str = "https://www.w3.org/ns/activitystreams";
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize)]
|
#[derive(Default, Debug, Clone, Deserialize)]
|
||||||
pub struct Context {
|
pub struct APContext {
|
||||||
pub id: Option<String>,
|
pub id: Option<String>,
|
||||||
#[serde(rename = "@context")]
|
#[serde(rename = "@context")]
|
||||||
pub ctx: ContextMap,
|
pub ctx: ContextMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Context {
|
impl LDObject for APContext {
|
||||||
type Err = ();
|
fn from_iri(iri: &str) -> Self {
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let mut ctx = Self::default();
|
let mut ctx = Self::default();
|
||||||
ctx.id = Some(s.into());
|
ctx.id = Some(iri.into());
|
||||||
Ok(ctx)
|
ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_iri(&self) -> String {
|
||||||
|
self.id.clone().unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use flabk_derive::{Resolvable, IRI};
|
use flabk_derive::LD;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::astreams::resolve::Resolvable;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
context::APContext,
|
|
||||||
resolve::ResolveError,
|
resolve::ResolveError,
|
||||||
serde_ext::{expand_partial, into_vec},
|
serde_ext::{expand_partial, expand_partial_into_vec, into_vec},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
@ -155,14 +156,14 @@ pub struct APResult {
|
||||||
#[derive(Debug, Clone, Deserialize, Default)]
|
#[derive(Debug, Clone, Deserialize, Default)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ObjectBase {
|
pub struct ObjectBase {
|
||||||
#[serde(rename = "@context")]
|
// #[serde(rename = "@context")]
|
||||||
#[serde(deserialize_with = "expand_partial")]
|
// #[serde(deserialize_with = "expand_partial")]
|
||||||
pub ap_context: APContext,
|
// pub ap_context: APContext,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub name_map: Option<HashMap<String, String>>,
|
pub name_map: Option<HashMap<String, String>>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
||||||
pub attachment: Vec<Attachment>,
|
pub attachment: Vec<Attachment>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "expand_partial_into_vec")]
|
||||||
pub attributed_to: Vec<Actor>,
|
pub attributed_to: Vec<Actor>,
|
||||||
pub audience: Option<Audience>,
|
pub audience: Option<Audience>,
|
||||||
pub media_type: Option<String>,
|
pub media_type: Option<String>,
|
||||||
|
@ -172,7 +173,7 @@ pub struct ObjectBase {
|
||||||
pub start_time: Option<String>,
|
pub start_time: Option<String>,
|
||||||
pub end_time: Option<String>,
|
pub end_time: Option<String>,
|
||||||
pub generator: Option<Actor>,
|
pub generator: Option<Actor>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
||||||
pub icon: Vec<Link>,
|
pub icon: Vec<Link>,
|
||||||
// omit in_reply_to
|
// omit in_reply_to
|
||||||
pub location: Option<Location>,
|
pub location: Option<Location>,
|
||||||
|
@ -182,21 +183,21 @@ pub struct ObjectBase {
|
||||||
pub replies: Option<Collection<()>>, // TODO: type
|
pub replies: Option<Collection<()>>, // TODO: type
|
||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
pub summary_map: Option<HashMap<String, String>>,
|
pub summary_map: Option<HashMap<String, String>>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "expand_partial_into_vec", default = "Vec::new")]
|
||||||
pub tag: Vec<Actor>,
|
pub tag: Vec<Actor>,
|
||||||
// omit url for now: need to merge into_vec & expand_partial
|
// omit url for now: need to merge into_vec & expand_partial
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
||||||
pub to: Vec<String>,
|
pub to: Vec<String>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
||||||
pub bto: Vec<String>,
|
pub bto: Vec<String>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
||||||
pub cc: Vec<String>,
|
pub cc: Vec<String>,
|
||||||
#[serde(deserialize_with = "into_vec")]
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
||||||
pub bcc: Vec<String>,
|
pub bcc: Vec<String>,
|
||||||
pub duration: Option<String>,
|
pub duration: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Resolvable, IRI, Default)]
|
#[derive(Debug, Clone, Deserialize, LD, Default)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: ObjectBase,
|
pub base: ObjectBase,
|
||||||
|
@ -227,7 +228,7 @@ pub struct Link {
|
||||||
/// for all types of activities.
|
/// for all types of activities.
|
||||||
/// It is important to note that the Activity type itself
|
/// It is important to note that the Activity type itself
|
||||||
/// does not carry any specific semantics about the kind of action being taken.
|
/// does not carry any specific semantics about the kind of action being taken.
|
||||||
#[derive(Debug, Clone, Deserialize, Resolvable)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Activity {
|
pub struct Activity {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -247,7 +248,7 @@ pub struct Activity {
|
||||||
|
|
||||||
/// Instances of IntransitiveActivity are a subtype of Activity
|
/// Instances of IntransitiveActivity are a subtype of Activity
|
||||||
/// representing intransitive actions.
|
/// representing intransitive actions.
|
||||||
#[derive(Debug, Clone, Deserialize, Resolvable)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct IntransitiveActivity {
|
pub struct IntransitiveActivity {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -277,7 +278,7 @@ pub struct Basic {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, IRI, Default, Resolvable)]
|
#[derive(Debug, Clone, Deserialize, LD, Default)]
|
||||||
pub struct Actor {
|
pub struct Actor {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -290,23 +291,10 @@ pub struct Actor {
|
||||||
pub conversation: Option<String>,
|
pub conversation: Option<String>,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
#[serde(rename = "to")]
|
#[serde(rename = "to")]
|
||||||
#[serde(deserialize_with = "pull_single_into_vec")]
|
#[serde(deserialize_with = "into_vec")]
|
||||||
pub to_uris: Vec<String>,
|
pub to_uris: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ActorType {
|
|
||||||
Application,
|
|
||||||
Group,
|
|
||||||
Organization,
|
|
||||||
Person,
|
|
||||||
Service,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, IRI, Default)]
|
|
||||||
pub struct Actor {
|
|
||||||
id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn test() {
|
pub async fn test() {
|
||||||
let obj = r#"{
|
let obj = r#"{
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
|
|
@ -1,9 +1,28 @@
|
||||||
use std::{future::Future, string::FromUtf8Error};
|
use std::{future::Future, string::FromUtf8Error};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use reqwest::{Method, StatusCode};
|
use reqwest::{Method, StatusCode};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::serde_ext::LDObject;
|
||||||
|
|
||||||
const LD_CONTENT_TYPE: &str = "application/ld+json";
|
const LD_CONTENT_TYPE: &str = "application/ld+json";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub(super) trait Resolvable: Sized {
|
||||||
|
async fn resolve(&self, resolver: &Resolver) -> Result<Self, ResolveError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T> Resolvable for T
|
||||||
|
where
|
||||||
|
T: LDObject + for<'de> Deserialize<'de> + Sync,
|
||||||
|
{
|
||||||
|
async fn resolve(&self, resolver: &Resolver) -> Result<Self, ResolveError> {
|
||||||
|
resolver.resolve_into(self.get_iri()).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ResolveError(pub String);
|
pub(crate) struct ResolveError(pub String);
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,40 @@
|
||||||
use std::{fmt, marker::PhantomData, str::FromStr};
|
use std::{fmt, marker::PhantomData};
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{self, MapAccess, Visitor},
|
de::{self, MapAccess, Visitor},
|
||||||
Deserialize, Deserializer,
|
Deserialize, Deserializer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub trait LDObject {
|
||||||
|
fn from_iri(iri: &str) -> Self;
|
||||||
|
fn get_iri(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LDObject for Option<T>
|
||||||
|
where
|
||||||
|
T: LDObject,
|
||||||
|
{
|
||||||
|
fn from_iri(iri: &str) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_iri(&self) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allows a value that's a string to be expanded into an object (move string into id property via From<IRI>)
|
// Allows a value that's a string to be expanded into an object (move string into id property via From<IRI>)
|
||||||
// AND the serialization of that object itself
|
// AND the serialization of that object itself
|
||||||
pub fn expand_partial<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
pub fn expand_partial<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||||
where
|
where
|
||||||
T: Deserialize<'de> + FromStr<Err = ()>,
|
T: Deserialize<'de> + LDObject,
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
struct BaseExpander<T>(PhantomData<fn() -> T>);
|
struct BaseExpander<T>(PhantomData<fn() -> T>);
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for BaseExpander<T>
|
impl<'de, T> Visitor<'de> for BaseExpander<T>
|
||||||
where
|
where
|
||||||
T: Deserialize<'de> + FromStr<Err = ()>,
|
T: Deserialize<'de> + LDObject,
|
||||||
{
|
{
|
||||||
type Value = T;
|
type Value = T;
|
||||||
|
|
||||||
|
@ -28,7 +46,7 @@ where
|
||||||
where
|
where
|
||||||
E: de::Error,
|
E: de::Error,
|
||||||
{
|
{
|
||||||
Ok(FromStr::from_str(value).unwrap())
|
Ok(LDObject::from_iri(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_map<M>(self, map: M) -> Result<T, M::Error>
|
fn visit_map<M>(self, map: M) -> Result<T, M::Error>
|
||||||
|
@ -42,18 +60,59 @@ where
|
||||||
deserializer.deserialize_any(BaseExpander(PhantomData))
|
deserializer.deserialize_any(BaseExpander(PhantomData))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn expand_partial_into_vec<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de> + LDObject,
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct BaseExpander<T>(PhantomData<fn() -> T>);
|
||||||
|
|
||||||
|
impl<'de, T> Visitor<'de> for BaseExpander<T>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de> + LDObject,
|
||||||
|
{
|
||||||
|
type Value = Vec<T>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("string or map")
|
||||||
|
}
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(vec![LDObject::from_iri(value)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map)).map(|v| vec![v])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::de::SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(visitor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(BaseExpander(PhantomData))
|
||||||
|
}
|
||||||
|
|
||||||
// Allows deserialization of a single item into a vector of that item
|
// Allows deserialization of a single item into a vector of that item
|
||||||
// As long as they implement the From<String> trait
|
// As long as they implement the From<String> trait
|
||||||
pub(super) fn pull_single_into_vec<'de, D, Out>(deserializer: D) -> Result<Vec<Out>, D::Error>
|
pub(super) fn into_vec<'de, D, Out>(deserializer: D) -> Result<Vec<Out>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
Out: From<String> + serde::Deserialize<'de>,
|
Out: serde::Deserialize<'de>,
|
||||||
{
|
{
|
||||||
struct VecVisitor<Out>(PhantomData<Vec<Out>>);
|
struct VecVisitor<Out>(PhantomData<Vec<Out>>);
|
||||||
|
|
||||||
impl<'de, Out> Visitor<'de> for VecVisitor<Out>
|
impl<'de, Out> Visitor<'de> for VecVisitor<Out>
|
||||||
where
|
where
|
||||||
Out: From<String> + serde::Deserialize<'de>,
|
Out: serde::Deserialize<'de>,
|
||||||
{
|
{
|
||||||
type Value = Vec<Out>;
|
type Value = Vec<Out>;
|
||||||
|
|
||||||
|
@ -66,7 +125,14 @@ where
|
||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
Ok(vec![value.to_string().into()])
|
Deserialize::deserialize(de::value::StrDeserializer::new(value)).map(|v| vec![v])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map)).map(|v| vec![v])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
|
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
|
||||||
|
@ -82,14 +148,14 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use flabk_derive::IRI;
|
use flabk_derive::LD;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use super::expand_partial;
|
use super::expand_partial;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expand_partial_populates_iri_from_string() {
|
fn expand_partial_populates_iri_from_string() {
|
||||||
#[derive(Deserialize, IRI, Default)]
|
#[derive(Deserialize, LD, Default)]
|
||||||
struct Context {
|
struct Context {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub context: bool,
|
pub context: bool,
|
||||||
|
@ -111,7 +177,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expand_partial_expands_into_object_fully() {
|
fn expand_partial_expands_into_object_fully() {
|
||||||
#[derive(Deserialize, IRI, Default)]
|
#[derive(Deserialize, LD, Default)]
|
||||||
struct Expanded {
|
struct Expanded {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub truth: bool,
|
pub truth: bool,
|
||||||
|
|
Loading…
Reference in New Issue