//! Tables related to web page metadata, XRD and JRD, `host-meta`, `WebFinger`, and so on. //! //! # Intended usage //! //! 1. An user agent requests an application for the descriptor for a given resource; //! 2. **The application queries the database using these structures to:** //! 1. [Determine if a descriptor should be returned, or if it should be handled by another application](MetaSubject); //! 2. [Determine which aliases the resource has](MetaAlias); //! 3. [Determine which properties the resource has](MetaProperty); //! 4. [Determine which links the resource has](MetaLink); //! 5. [Determine the titles of each link](MetaLinkTitle); //! 6. [Determine the properties of each link](MetaLinkProperty); //! 3. The application compiles the data in a single structure that the user agent supports //! 4. The application returns the data to the user agent //! //! # Matching //! //! Matching is performed for each single record separately, allowing for example a system administrator to define many separate subjects while globally defining a property for all the resources served. //! //! # Reference //! //! See [`acrate_rd`] for more information on the specification these structures are based on. //! //! # Used by //! //! - [`acrate_rdserver`] //! use diesel::deserialize::FromSql; use diesel::{AsExpression, Associations, FromSqlRow, Identifiable, Insertable, IntoSql, PgTextExpressionMethods, QueryResult, Queryable, QueryableByName, Selectable, SelectableHelper, ExpressionMethods, BelongingToDsl}; use diesel::pg::{Pg, PgConnection}; use diesel::serialize::{Output, ToSql}; use diesel_async::AsyncPgConnection; use mediatype::MediaTypeBuf; use uuid::Uuid; use crate::schema; use crate::impl_to_insert; /// Wrapper to use [`mime::Mime`] with [`diesel`]. #[derive(Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression)] #[diesel(sql_type = diesel::sql_types::Text)] pub struct MediaTypeDatabase(pub MediaTypeBuf); /// A matchable record denoting the existence of a resource descriptor. /// /// # See also /// /// - [`MetaSubjectInsert`] /// #[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable)] #[diesel(table_name = schema::meta_subjects)] #[diesel(check_for_backend(Pg))] pub struct MetaSubject { /// The identity column of the record. pub id: Uuid, /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The value the `subject` property should take when this record is matched. /// /// If [`None`], its value is up for specification by the querying application. pub subject: Option, /// Where the querying application should redirect the user agent to when this record is matched. /// /// If [`Some`], should always override everything else. pub redirect: Option, } /// An [`Insertable`] version of [`MetaSubject`]. #[derive(Debug, Insertable)] #[diesel(table_name = schema::meta_subjects)] #[diesel(check_for_backend(Pg))] pub struct MetaSubjectInsert { /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The value the `subject` property should take when this record is matched. /// /// If [`None`], its value is up for specification by the querying application. pub subject: Option, /// Where the querying application should redirect the user agent to when this record is matched. /// /// If [`Some`], should always override everything else. pub redirect: Option, } /// A matchable record denoting an alias belonging to a subject. /// /// # See also /// /// - [`MetaAliasInsert`] /// #[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable)] #[diesel(table_name = schema::meta_aliases)] #[diesel(check_for_backend(Pg))] pub struct MetaAlias { /// The identity column of the record. pub id: Uuid, /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The alias to **add** to the list of aliases of the resource. pub alias: String, } /// An [`Insertable`] version of [`MetaAlias`]. #[derive(Debug, Insertable)] #[diesel(table_name = schema::meta_aliases)] #[diesel(check_for_backend(Pg))] pub struct MetaAliasInsert { /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The alias to **add** to the list of aliases of the resource. pub alias: String, } /// A matchable record denoting a link towards another resource someway related with the subject. /// /// # See also /// /// - [`MetaLinkInsert`] /// #[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable)] #[diesel(table_name = schema::meta_links)] #[diesel(check_for_backend(Pg))] pub struct MetaLink { /// The identity column of the record. pub id: Uuid, /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The relationship the link establishes between itself and the value of the link. pub rel: String, /// The media type of the value of the link. /// /// Can be [`None`] if it shouldn't be specified. pub type_: Option, /// The URI to the document this property is linking the subject to. /// /// Can be [`None`] if it shouldn't be specified, for example if [`Self::template`] is [`Some`]. pub href: Option, /// The template to the document this property is linking the subject to. /// /// Can be [`None`] if it shouldn't be specified, for example if [`Self::href`] is [`Some`]. pub template: Option, } /// An [`Insertable`] version of [`MetaLink`]. #[derive(Debug, Insertable)] #[diesel(table_name = schema::meta_links)] #[diesel(check_for_backend(Pg))] pub struct MetaLinkInsert { /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The relationship the link establishes between itself and the value of the link. pub rel: String, /// The media type of the value of the link. /// /// Can be [`None`] if it shouldn't be specified. pub type_: Option, /// The URI to the document this property is linking the subject to. /// /// Can be [`None`] if it shouldn't be specified, for example if [`Self::template`] is [`Some`]. pub href: Option, /// The template to the document this property is linking the subject to. /// /// Can be [`None`] if it shouldn't be specified, for example if [`Self::href`] is [`Some`]. pub template: Option, } /// A property that a [`MetaLink`] has. /// /// # See also /// /// - [`MetaLinkPropertyInsert`] /// #[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable, Associations)] #[diesel(belongs_to(MetaLink))] #[diesel(table_name = schema::meta_link_properties)] #[diesel(check_for_backend(Pg))] pub struct MetaLinkProperty { /// The identity column of the record. pub id: Uuid, /// The [`MetaLink::id`] this record refers to. pub meta_link_id: Uuid, /// The relationship the property establishes between the [`MetaLink`] and the [`MetaLinkProperty::value`]. pub rel: String, /// The value that the property has. pub value: Option, } /// An [`Insertable`] version of [`MetaLinkProperty`]. #[derive(Debug, Insertable)] #[diesel(belongs_to(MetaLink))] #[diesel(table_name = schema::meta_link_properties)] #[diesel(check_for_backend(Pg))] pub struct MetaLinkPropertyInsert { /// The [`MetaLink::id`] this record refers to. pub meta_link_id: Uuid, /// The relationship the property establishes between the [`MetaLink`] and the [`MetaLinkProperty::value`]. pub rel: String, /// The value that the property has. pub value: Option, } /// A title that a [`MetaLink`] has in a certain language. #[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable, Associations)] #[diesel(belongs_to(MetaLink))] #[diesel(table_name = schema::meta_link_titles)] #[diesel(check_for_backend(Pg))] pub struct MetaLinkTitle { /// The identity column of the record. pub id: Uuid, /// The [`MetaLink::id`] this record refers to. pub meta_link_id: Uuid, /// The language of the title. pub language: String, /// The actual contents of the title. pub value: String, } /// An [`Insertable`] version of [`MetaLinkTitle`]. #[derive(Debug, Insertable)] #[diesel(belongs_to(MetaLink))] #[diesel(table_name = schema::meta_link_titles)] #[diesel(check_for_backend(Pg))] pub struct MetaLinkTitleInsert { /// The [`MetaLink::id`] this record refers to. pub meta_link_id: Uuid, /// The language of the title. pub language: String, /// The actual contents of the title. pub value: String, } /// A matchable record denoting a property that a subject has. /// /// # See also /// /// - [`MetaPropertyInsert`] /// #[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable)] #[diesel(table_name = schema::meta_properties)] #[diesel(check_for_backend(Pg))] pub struct MetaProperty { /// The identity column of the record. pub id: Uuid, /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The relationship the property establishes between the [`MetaSubject`] and the [`MetaProperty::value`]. pub rel: String, /// The value that the property has. pub value: Option, } /// An [`Insertable`] version of [`MetaLinkTitle`]. #[derive(Debug, Insertable)] #[diesel(table_name = schema::meta_properties)] #[diesel(check_for_backend(Pg))] pub struct MetaPropertyInsert { /// The document the record is valid for. pub document: String, /// The [PostgreSQL ILIKE pattern] to match the search term against for the record to be valid. /// /// [PostgreSQL ILIKE pattern]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE pub pattern: String, /// The relationship the property establishes between the [`MetaSubject`] and the [`MetaProperty::value`]. pub rel: String, /// The value that the property has. pub value: Option, } /// Allow [`diesel::sql_types::Text`] values to be parsed as [`MediaTypeDatabase`]. impl FromSql for MediaTypeDatabase where DB: diesel::backend::Backend, String: FromSql, { fn from_sql(bytes: ::RawValue<'_>) -> diesel::deserialize::Result { log::trace!("Reading TEXT from the database..."); let s = >::from_sql(bytes)?; log::trace!("Attempting to parse as a media type: {s:?}"); let mt: MediaTypeBuf = s.parse()?; log::trace!("Successfully parsed media type: {mt:?}"); Ok(Self(mt)) } } /// Allow [`diesel::sql_types::Text`] values to be written to with [`MediaTypeDatabase`]. impl ToSql for MediaTypeDatabase where DB: diesel::backend::Backend, str: ToSql, { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { log::trace!("Getting the essence of a media type to prepare for serialization..."); let mt = self.0.as_str(); log::trace!("Serializing media type as TEXT: {mt:?}"); >::to_sql(mt, out) } } impl MetaSubject { /// Synchronously query the records matching the given document and resource. pub fn query_matching(conn: &mut PgConnection, doc: &str, resource: &str) -> QueryResult> { use diesel::QueryDsl; use diesel::RunQueryDsl; use schema::meta_subjects::dsl::*; let document_is_equal = document.eq(doc); let resource_matches_pattern = resource.into_sql::().ilike(pattern); meta_subjects .filter(document_is_equal) .filter(resource_matches_pattern) .select(Self::as_select()) .load(conn) } /// Asynchronously query the records matching the given document and resource. pub async fn aquery_matching(conn: &mut AsyncPgConnection, doc: &str, resource: &str) -> QueryResult> { use diesel::QueryDsl; use diesel_async::RunQueryDsl; use schema::meta_subjects::dsl::*; let document_is_equal = document.eq(doc); let resource_matches_pattern = resource.into_sql::().ilike(pattern); meta_subjects .filter(document_is_equal) .filter(resource_matches_pattern) .select(Self::as_select()) .load(conn) .await } } impl MetaAlias { /// Synchronously query the records matching the given document and resource. pub fn query_matching(conn: &mut PgConnection, doc: &str, subject: &str) -> QueryResult> { use diesel::QueryDsl; use diesel::RunQueryDsl; use schema::meta_aliases::dsl::*; let document_is_equal = document.eq(doc); let subject_matches_pattern = subject.into_sql::().ilike(pattern); meta_aliases .filter(document_is_equal) .filter(subject_matches_pattern) .select(Self::as_select()) .load(conn) } /// Asynchronously query the records matching the given document and resource. pub async fn aquery_matching(conn: &mut AsyncPgConnection, doc: &str, subject: &str) -> QueryResult> { use diesel::QueryDsl; use diesel_async::RunQueryDsl; use schema::meta_aliases::dsl::*; let document_is_equal = document.eq(doc); let subject_matches_pattern = subject.into_sql::().ilike(pattern); meta_aliases .filter(document_is_equal) .filter(subject_matches_pattern) .select(Self::as_select()) .load(conn) .await } } impl MetaLink { /// Synchronously query the records matching the given document and resource. pub async fn query_matching(conn: &mut PgConnection, doc: &str, subject: &str) -> QueryResult> { use diesel::QueryDsl; use diesel::RunQueryDsl; use schema::meta_links::dsl::*; let document_is_equal = document.eq(doc); let subject_matches_pattern = subject.into_sql::().ilike(pattern); meta_links .filter(document_is_equal) .filter(subject_matches_pattern) .select(Self::as_select()) .load(conn) } /// Asynchronously query the records matching the given document and resource. pub async fn aquery_matching(conn: &mut AsyncPgConnection, doc: &str, subject: &str) -> QueryResult> { use diesel::QueryDsl; use diesel_async::RunQueryDsl; use schema::meta_links::dsl::*; let document_is_equal = document.eq(doc); let subject_matches_pattern = subject.into_sql::().ilike(pattern); meta_links .filter(document_is_equal) .filter(subject_matches_pattern) .select(Self::as_select()) .load(conn) .await } } impl MetaLinkProperty { /// Synchronously query the records belonging to the given [`MetaLink`]s. pub fn query_by_link(conn: &mut PgConnection, links: &[MetaLink]) -> QueryResult> { use diesel::RunQueryDsl; Self::belonging_to(links) .load(conn) } /// Asynchronously query the records belonging to the given [`MetaLink`]s. pub async fn aquery_by_link(conn: &mut AsyncPgConnection, links: &[MetaLink]) -> QueryResult> { use diesel_async::RunQueryDsl; Self::belonging_to(links) .load(conn) .await } } impl MetaLinkTitle { /// Synchronously query the records belonging to the given [`MetaLink`]s. pub fn query_by_link(conn: &mut PgConnection, links: &[MetaLink]) -> QueryResult> { use diesel::RunQueryDsl; Self::belonging_to(links) .load(conn) } /// Asynchronously query the records belonging to the given [`MetaLink`]s. pub async fn aquery_by_link(conn: &mut AsyncPgConnection, links: &[MetaLink]) -> QueryResult> { use diesel_async::RunQueryDsl; Self::belonging_to(links) .load(conn) .await } } impl MetaProperty { /// Synchronously query the records matching the given document and resource. pub fn query_matching(conn: &mut PgConnection, doc: &str, subject: &str) -> QueryResult> { use diesel::QueryDsl; use diesel::RunQueryDsl; use schema::meta_properties::dsl::*; let document_is_equal = document.eq(doc); let subject_matches_pattern = subject.into_sql::().ilike(pattern); meta_properties .filter(document_is_equal) .filter(subject_matches_pattern) .select(Self::as_select()) .load(conn) } /// Asynchronously query the records matching the given document and resource. pub async fn aquery_matching(conn: &mut AsyncPgConnection, doc: &str, subject: &str) -> QueryResult> { use diesel::QueryDsl; use diesel_async::RunQueryDsl; use schema::meta_properties::dsl::*; let document_is_equal = document.eq(doc); let subject_matches_pattern = subject.into_sql::().ilike(pattern); meta_properties .filter(document_is_equal) .filter(subject_matches_pattern) .select(Self::as_select()) .load(conn) .await } } impl_to_insert!(MetaSubjectInsert => MetaSubject: schema::meta_subjects::table); impl_to_insert!(MetaAliasInsert => MetaAlias: schema::meta_aliases::table); impl_to_insert!(MetaLinkInsert => MetaLink: schema::meta_links::table); impl_to_insert!(MetaLinkPropertyInsert => MetaLinkProperty: schema::meta_link_properties::table); impl_to_insert!(MetaLinkTitleInsert => MetaLinkTitle: schema::meta_link_titles::table); impl_to_insert!(MetaPropertyInsert => MetaProperty: schema::meta_properties::table);