use crate::{config::Data, error::Error, fetch::fetch_object_http, traits::Collection}; use serde::{Deserialize, Serialize}; use std::{ fmt::{Debug, Display, Formatter}, marker::PhantomData, }; use url::Url; /// Typed wrapper for Activitypub Collection ID which helps with dereferencing. #[derive(Serialize, Deserialize)] #[serde(transparent)] pub struct CollectionId(Box, PhantomData) where Kind: Collection, for<'de2> ::Kind: Deserialize<'de2>; impl CollectionId where Kind: Collection, for<'de2> ::Kind: Deserialize<'de2>, { /// Construct a new CollectionId instance pub fn parse(url: &str) -> Result { Ok(Self(Box::new(Url::parse(url)?), PhantomData::)) } /// Fetches collection over HTTP /// /// Unlike [ObjectId::dereference](crate::fetch::object_id::ObjectId::dereference) this method doesn't do /// any caching. pub async fn dereference( &self, owner: &::Owner, data: &Data<::DataType>, ) -> Result::Error> where ::Error: From, { let res = fetch_object_http(&self.0, data).await?; let redirect_url = &res.url; Kind::verify(&res.object, redirect_url, data).await?; Kind::from_json(res.object, owner, data).await } } /// Need to implement clone manually, to avoid requiring Kind to be Clone impl Clone for CollectionId where Kind: Collection, for<'de2> ::Kind: serde::Deserialize<'de2>, { fn clone(&self) -> Self { CollectionId(self.0.clone(), self.1) } } impl Display for CollectionId where Kind: Collection, for<'de2> ::Kind: serde::Deserialize<'de2>, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0.as_str()) } } impl Debug for CollectionId where Kind: Collection, for<'de2> ::Kind: serde::Deserialize<'de2>, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0.as_str()) } } impl From> for Url where Kind: Collection, for<'de2> ::Kind: serde::Deserialize<'de2>, { fn from(id: CollectionId) -> Self { *id.0 } } impl From for CollectionId where Kind: Collection + Send + 'static, for<'de2> ::Kind: serde::Deserialize<'de2>, { fn from(url: Url) -> Self { CollectionId(Box::new(url), PhantomData::) } } impl PartialEq for CollectionId where Kind: Collection, for<'de2> ::Kind: serde::Deserialize<'de2>, { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) && self.1 == other.1 } } #[cfg(feature = "diesel")] const _IMPL_DIESEL_NEW_TYPE_FOR_COLLECTION_ID: () = { use diesel::{ backend::Backend, deserialize::{FromSql, FromStaticSqlRow}, expression::AsExpression, internal::derives::as_expression::Bound, pg::Pg, query_builder::QueryId, serialize, serialize::{Output, ToSql}, sql_types::{HasSqlType, SingleValue, Text}, Expression, Queryable, }; // TODO: this impl only works for Postgres db because of to_string() call which requires reborrow impl ToSql for CollectionId where Kind: Collection, for<'de2> ::Kind: Deserialize<'de2>, String: ToSql, { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let v = self.0.to_string(); >::to_sql(&v, &mut out.reborrow()) } } impl<'expr, Kind, ST> AsExpression for &'expr CollectionId where Kind: Collection, for<'de2> ::Kind: Deserialize<'de2>, Bound: Expression, ST: SingleValue, { type Expression = Bound; fn as_expression(self) -> Self::Expression { Bound::new(self.0.as_str()) } } impl AsExpression for CollectionId where Kind: Collection, for<'de2> ::Kind: Deserialize<'de2>, Bound: Expression, ST: SingleValue, { type Expression = Bound; fn as_expression(self) -> Self::Expression { Bound::new(self.0.to_string()) } } impl FromSql for CollectionId where Kind: Collection + Send + 'static, for<'de2> ::Kind: Deserialize<'de2>, String: FromSql, DB: Backend, DB: HasSqlType, { fn from_sql( raw: DB::RawValue<'_>, ) -> Result> { let string: String = FromSql::::from_sql(raw)?; Ok(CollectionId::parse(&string)?) } } impl Queryable for CollectionId where Kind: Collection + Send + 'static, for<'de2> ::Kind: Deserialize<'de2>, String: FromStaticSqlRow, DB: Backend, DB: HasSqlType, { type Row = String; fn build(row: Self::Row) -> diesel::deserialize::Result { Ok(CollectionId::parse(&row)?) } } impl QueryId for CollectionId where Kind: Collection + 'static, for<'de2> ::Kind: Deserialize<'de2>, { type QueryId = Self; } };