From bd93207c255c313761bfb5849e09da633d307ab2 Mon Sep 17 00:00:00 2001 From: emilis Date: Fri, 4 Nov 2022 19:15:16 +0000 Subject: [PATCH] added ld-expansion serde functionality (WIP) --- src/astreams/resolve.rs | 9 +-- src/astreams/serde_ext.rs | 137 +++++++++++++++++++++++++++++++++----- 2 files changed, 127 insertions(+), 19 deletions(-) diff --git a/src/astreams/resolve.rs b/src/astreams/resolve.rs index 78d835f..4b1c086 100644 --- a/src/astreams/resolve.rs +++ b/src/astreams/resolve.rs @@ -4,6 +4,7 @@ use reqwest::{Method, StatusCode}; const LD_CONTENT_TYPE: &str = "application/ld+json"; +#[derive(Debug)] pub(crate) struct ResolveError(pub String); impl From for ResolveError { @@ -49,10 +50,10 @@ impl Resolver { } } - pub async fn resolve_into<'a, T: for<'de> serde::Deserialize<'de>>( - &self, - iri: String, - ) -> Result { + pub async fn resolve_into<'a, T>(&self, iri: String) -> Result + where + T: for<'de> serde::Deserialize<'de>, + { Ok(self .get(iri, |resp| async { Ok(resp.json().await?) }) .await?) diff --git a/src/astreams/serde_ext.rs b/src/astreams/serde_ext.rs index 9d49ea0..0702757 100644 --- a/src/astreams/serde_ext.rs +++ b/src/astreams/serde_ext.rs @@ -1,41 +1,148 @@ +use super::resolve::Resolver; use std::{fmt, marker::PhantomData}; -use serde::{de, Deserialize, Deserializer}; +use serde::{de::Visitor, Deserialize, Deserializer}; -// Allows deserialization of a single item into a vector of that item -// As long as they implement the From trait -pub(super) fn pull_single_into_vec<'de, D, T>(deserializer: D) -> Result, D::Error> +// Allows a value that's a string to be expanded into an object AND the serialization of that object itself +// TODO: deserializing the actual object isnt supported atm LOL +pub(super) fn expand<'de, D, Out>(deserializer: D) -> Result where D: Deserializer<'de>, - T: From + serde::Deserialize<'de>, + Out: for<'dt> serde::Deserialize<'dt>, { - struct VecComposer(PhantomData>); + let resolver = Resolver::new(); + struct ResolveVisitor { + resolver: Resolver, + _type: PhantomData, + } - impl<'de, T> de::Visitor<'de> for VecComposer + impl<'de, Out> Visitor<'de> for ResolveVisitor where - T: From + serde::Deserialize<'de>, + Out: for<'dt> serde::Deserialize<'dt>, { - type Value = Vec; + type Value = Out; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let name = std::any::type_name::(); - formatter.write_str(format!("expected {} or Vec<{}>", name, name).as_str()) + formatter.write_str(std::any::type_name::()) + } + + fn visit_map(self, map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + todo!() } fn visit_str(self, value: &str) -> Result where - E: de::Error, + E: serde::de::Error, + { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(self.resolver.resolve_into::(value.to_string())) + .map_err(|e| { + let name = std::any::type_name::(); + serde::de::Error::invalid_value( + serde::de::Unexpected::Other( + format!("failed resolving iri [{}]: {}", value, e.0).as_str(), + ), + &name, + ) + }) // TODO try this error + } + + fn visit_seq(self, visitor: S) -> Result + where + S: serde::de::SeqAccess<'de>, + { + Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(visitor)) + } + } + + deserializer.deserialize_any(ResolveVisitor { + resolver: resolver, + _type: PhantomData, + }) +} + +// Allows deserialization of a single item into a vector of that item +// As long as they implement the From trait +pub(super) fn pull_single_into_vec<'de, D, Out>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + Out: From + serde::Deserialize<'de>, +{ + struct VecVisitor(PhantomData>); + + impl<'de, Out> Visitor<'de> for VecVisitor + where + Out: From + serde::Deserialize<'de>, + { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let name = std::any::type_name::(); + formatter.write_str(format!("{} or Vec<{}>", name, name).as_str()) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, { Ok(vec![value.to_string().into()]) } fn visit_seq(self, visitor: S) -> Result where - S: de::SeqAccess<'de>, + S: serde::de::SeqAccess<'de>, { - Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor)) + Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(visitor)) } } - deserializer.deserialize_any(VecComposer(PhantomData)) + deserializer.deserialize_any(VecVisitor(PhantomData)) +} + +#[cfg(test)] +mod tests { + use serde::Deserialize; + + use super::expand; + + #[test] + fn expand_context_from_iri() { + #[derive(Deserialize)] + struct WithContext { + #[serde(deserialize_with = "expand")] + pub context: super::super::context::Context, + } + + const JSONLD_INPUT: &str = r#"{"context": "https://www.w3.org/ns/activitystreams"}"#; + let result = + serde_json::from_str::(JSONLD_INPUT).expect("deserializing with expand"); + + assert!(result.context.ctx.xsd == "http://www.w3.org/2001/XMLSchema#") + } + + #[test] + fn expand_when_json_isnt_iri_but_the_object_itself() { + #[derive(Deserialize)] + struct Expanded { + pub id: String, + pub truth: bool, + } + + #[derive(Deserialize)] + struct Expandable { + #[serde(deserialize_with = "expand")] + pub expansive: Expanded, + } + + const JSONLD_INPUT: &str = r#"{"expansive": { "id": "1", "truth": true }}"#; + let result = + serde_json::from_str::(JSONLD_INPUT).expect("deserializing with expand"); + + assert!(result.expansive.id == "1"); + assert!(result.expansive.truth) + } }