87 lines
2.2 KiB
Rust
87 lines
2.2 KiB
Rust
use std::{future::Future, string::FromUtf8Error};
|
|
|
|
use async_trait::async_trait;
|
|
use reqwest::{Method, StatusCode};
|
|
use serde::Deserialize;
|
|
|
|
use super::serde_ext::LDObject;
|
|
|
|
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)]
|
|
pub(crate) struct ResolveError(pub String);
|
|
|
|
impl From<reqwest::Error> for ResolveError {
|
|
fn from(err: reqwest::Error) -> Self {
|
|
Self(err.to_string())
|
|
}
|
|
}
|
|
impl From<FromUtf8Error> for ResolveError {
|
|
fn from(e: FromUtf8Error) -> Self {
|
|
Self(format!(
|
|
"invalid schema format (tried utf8): {}",
|
|
e.to_string()
|
|
))
|
|
}
|
|
}
|
|
|
|
pub(super) struct Resolver {
|
|
client: reqwest::Client,
|
|
}
|
|
|
|
impl Resolver {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
client: reqwest::ClientBuilder::new().build().unwrap(),
|
|
}
|
|
}
|
|
|
|
async fn get<Out, F, Fut>(&self, iri: String, ok: F) -> Result<Out, ResolveError>
|
|
where
|
|
F: FnOnce(reqwest::Response) -> Fut,
|
|
Fut: Future<Output = Result<Out, ResolveError>>,
|
|
{
|
|
let resp = self
|
|
.client
|
|
.request(Method::GET, iri)
|
|
.header("Accept", LD_CONTENT_TYPE)
|
|
.send()
|
|
.await?;
|
|
|
|
match resp.status() {
|
|
StatusCode::OK => Ok(ok(resp).await?),
|
|
status => Err(ResolveError(format!("non-ok status: {}", status))),
|
|
}
|
|
}
|
|
|
|
pub async fn resolve_into<'a, T>(&self, iri: String) -> Result<T, ResolveError>
|
|
where
|
|
T: for<'de> serde::Deserialize<'de>,
|
|
{
|
|
Ok(self
|
|
.get(iri, |resp| async { Ok(resp.json().await?) })
|
|
.await?)
|
|
}
|
|
|
|
pub async fn resolve(&self, iri: String) -> Result<Vec<u8>, ResolveError> {
|
|
Ok(self
|
|
.get(iri, |resp| async { Ok(resp.bytes().await?.to_vec()) })
|
|
.await?)
|
|
}
|
|
}
|