flabk/src/astreams/serde_ext.rs

149 lines
4.6 KiB
Rust

use super::resolve::Resolver;
use std::{fmt, marker::PhantomData};
use serde::{de::Visitor, Deserialize, Deserializer};
static TYPE_STR: &str = std::any::type_name::<&str>();
static TYPE_STRING: &str = std::any::type_name::<String>();
// 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<Out, D::Error>
where
D: Deserializer<'de>,
Out: for<'dt> serde::Deserialize<'dt>,
{
let resolver = Resolver::new();
struct ResolveVisitor<Out> {
resolver: Resolver,
_type: PhantomData<Out>,
}
impl<'de, Out> Visitor<'de> for ResolveVisitor<Out>
where
Out: for<'dt> serde::Deserialize<'dt>,
{
type Value = Out;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(std::any::type_name::<Out>())
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
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,
)
})
}
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))
}
}
let type_name = std::any::type_name::<Out>();
if type_name == TYPE_STR || type_name == TYPE_STRING {
deserializer.deserialize_any(ResolveVisitor {
resolver: resolver,
_type: PhantomData,
})
} else {
Out::deserialize(deserializer)
}
}
// 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()])
}
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(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)
}
}