added ld-expansion serde functionality (WIP)
This commit is contained in:
parent
7b538c0a0c
commit
bd93207c25
|
@ -4,6 +4,7 @@ use reqwest::{Method, StatusCode};
|
||||||
|
|
||||||
const LD_CONTENT_TYPE: &str = "application/ld+json";
|
const LD_CONTENT_TYPE: &str = "application/ld+json";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct ResolveError(pub String);
|
pub(crate) struct ResolveError(pub String);
|
||||||
|
|
||||||
impl From<reqwest::Error> for ResolveError {
|
impl From<reqwest::Error> for ResolveError {
|
||||||
|
@ -49,10 +50,10 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn resolve_into<'a, T: for<'de> serde::Deserialize<'de>>(
|
pub async fn resolve_into<'a, T>(&self, iri: String) -> Result<T, ResolveError>
|
||||||
&self,
|
where
|
||||||
iri: String,
|
T: for<'de> serde::Deserialize<'de>,
|
||||||
) -> Result<T, ResolveError> {
|
{
|
||||||
Ok(self
|
Ok(self
|
||||||
.get(iri, |resp| async { Ok(resp.json().await?) })
|
.get(iri, |resp| async { Ok(resp.json().await?) })
|
||||||
.await?)
|
.await?)
|
||||||
|
|
|
@ -1,41 +1,148 @@
|
||||||
|
use super::resolve::Resolver;
|
||||||
use std::{fmt, marker::PhantomData};
|
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
|
// Allows a value that's a string to be expanded into an object AND the serialization of that object itself
|
||||||
// As long as they implement the From<String> trait
|
// TODO: deserializing the actual object isnt supported atm LOL
|
||||||
pub(super) fn pull_single_into_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
|
pub(super) fn expand<'de, D, Out>(deserializer: D) -> Result<Out, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
T: From<String> + serde::Deserialize<'de>,
|
Out: for<'dt> serde::Deserialize<'dt>,
|
||||||
{
|
{
|
||||||
struct VecComposer<T>(PhantomData<Vec<T>>);
|
let resolver = Resolver::new();
|
||||||
|
struct ResolveVisitor<Out> {
|
||||||
|
resolver: Resolver,
|
||||||
|
_type: PhantomData<Out>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'de, T> de::Visitor<'de> for VecComposer<T>
|
impl<'de, Out> Visitor<'de> for ResolveVisitor<Out>
|
||||||
where
|
where
|
||||||
T: From<String> + serde::Deserialize<'de>,
|
Out: for<'dt> serde::Deserialize<'dt>,
|
||||||
{
|
{
|
||||||
type Value = Vec<T>;
|
type Value = Out;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let name = std::any::type_name::<T>();
|
formatter.write_str(std::any::type_name::<Out>())
|
||||||
formatter.write_str(format!("expected {} or Vec<{}>", name, name).as_str())
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: de::Error,
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
tokio::runtime::Runtime::new()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(self.resolver.resolve_into::<Out>(value.to_string()))
|
||||||
|
.map_err(|e| {
|
||||||
|
let name = std::any::type_name::<Out>();
|
||||||
|
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<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(ResolveVisitor {
|
||||||
|
resolver: resolver,
|
||||||
|
_type: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows deserialization of a single item into a vector of that item
|
||||||
|
// 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>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
Out: From<String> + serde::Deserialize<'de>,
|
||||||
|
{
|
||||||
|
struct VecVisitor<Out>(PhantomData<Vec<Out>>);
|
||||||
|
|
||||||
|
impl<'de, Out> Visitor<'de> for VecVisitor<Out>
|
||||||
|
where
|
||||||
|
Out: From<String> + serde::Deserialize<'de>,
|
||||||
|
{
|
||||||
|
type Value = Vec<Out>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let name = std::any::type_name::<Out>();
|
||||||
|
formatter.write_str(format!("{} or Vec<{}>", name, name).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
Ok(vec![value.to_string().into()])
|
Ok(vec![value.to_string().into()])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
|
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
|
||||||
where
|
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::<WithContext>(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::<Expandable>(JSONLD_INPUT).expect("deserializing with expand");
|
||||||
|
|
||||||
|
assert!(result.expansive.id == "1");
|
||||||
|
assert!(result.expansive.truth)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue