added resolve trait, fixed deserialization for vector types

This commit is contained in:
emilis 2022-12-19 19:24:43 +00:00
parent 5473512787
commit d868170084
5 changed files with 135 additions and 56 deletions

View File

@ -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()
} }
} }
} }

View File

@ -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()
} }
} }

View File

@ -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",

View File

@ -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);

View File

@ -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,