200 lines
5.5 KiB
Rust
200 lines
5.5 KiB
Rust
use std::{fmt, marker::PhantomData};
|
|
|
|
use serde::{
|
|
de::{self, MapAccess, Visitor},
|
|
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>)
|
|
// AND the serialization of that object itself
|
|
pub fn expand_partial<'de, T, D>(deserializer: D) -> Result<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 = T;
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
formatter.write_str("string or map")
|
|
}
|
|
|
|
fn visit_str<E>(self, value: &str) -> Result<T, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(LDObject::from_iri(value))
|
|
}
|
|
|
|
fn visit_map<M>(self, map: M) -> Result<T, M::Error>
|
|
where
|
|
M: MapAccess<'de>,
|
|
{
|
|
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
|
|
}
|
|
}
|
|
|
|
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
|
|
// As long as they implement the From<String> trait
|
|
pub(super) fn into_vec<'de, D, Out>(deserializer: D) -> Result<Vec<Out>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
Out: serde::Deserialize<'de>,
|
|
{
|
|
struct VecVisitor<Out>(PhantomData<Vec<Out>>);
|
|
|
|
impl<'de, Out> Visitor<'de> for VecVisitor<Out>
|
|
where
|
|
Out: 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,
|
|
{
|
|
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>
|
|
where
|
|
S: serde::de::SeqAccess<'de>,
|
|
{
|
|
Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(visitor))
|
|
}
|
|
}
|
|
|
|
deserializer.deserialize_any(VecVisitor(PhantomData))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use flabk_derive::LD;
|
|
use serde::Deserialize;
|
|
|
|
use super::expand_partial;
|
|
|
|
#[test]
|
|
fn expand_partial_populates_iri_from_string() {
|
|
#[derive(Deserialize, LD, Default)]
|
|
struct Context {
|
|
pub id: String,
|
|
pub context: bool,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct WithContext {
|
|
#[serde(deserialize_with = "expand_partial")]
|
|
pub 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.id == "https://www.w3.org/ns/activitystreams");
|
|
assert!(result.context.context == false);
|
|
}
|
|
|
|
#[test]
|
|
fn expand_partial_expands_into_object_fully() {
|
|
#[derive(Deserialize, LD, Default)]
|
|
struct Expanded {
|
|
pub id: String,
|
|
pub truth: bool,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct Expandable {
|
|
#[serde(deserialize_with = "expand_partial")]
|
|
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);
|
|
}
|
|
}
|