diff --git a/.idea/acrate.iml b/.idea/acrate.iml
index b5a43c3..ac0db0c 100644
--- a/.idea/acrate.iml
+++ b/.idea/acrate.iml
@@ -4,12 +4,13 @@
-
-
-
+
+
+
+
diff --git a/Cargo.toml b/Cargo.toml
index 4a488a6..3776b1b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,3 @@
[workspace]
resolver = "2"
-members = ["acrate_database", "acrate_rd", "acrate-nodeinfo", "acrate-webfinger"]
+members = ["acrate_database", "acrate_rd", "acrate_nodeinfo", "acrate_rdserver", "acrate_mime"]
diff --git a/acrate_mime/Cargo.toml b/acrate_mime/Cargo.toml
new file mode 100644
index 0000000..f178e7a
--- /dev/null
+++ b/acrate_mime/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "acrate_mime"
+version = "0.1.0"
+authors = ["Stefano Pigozzi "]
+edition = "2021"
+description = "Rust typing and utilities for MIME / media types"
+repository = "https://forge.steffo.eu/unimore/tirocinio-canali-steffo-acrate"
+license = "EUPL-1.2"
+keywords = ["mime", "mimetype", "media", "media-type", "mime-type"]
+categories = ["web-programming"]
+
+[dependencies]
+mime = "0.3.17"
diff --git a/acrate_mime/src/lib.rs b/acrate_mime/src/lib.rs
new file mode 100644
index 0000000..b93cf3f
--- /dev/null
+++ b/acrate_mime/src/lib.rs
@@ -0,0 +1,14 @@
+pub fn add(left: u64, right: u64) -> u64 {
+ left + right
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn it_works() {
+ let result = add(2, 2);
+ assert_eq!(result, 4);
+ }
+}
diff --git a/acrate-nodeinfo/Cargo.toml b/acrate_nodeinfo/Cargo.toml
similarity index 52%
rename from acrate-nodeinfo/Cargo.toml
rename to acrate_nodeinfo/Cargo.toml
index b641faa..60e55aa 100644
--- a/acrate-nodeinfo/Cargo.toml
+++ b/acrate_nodeinfo/Cargo.toml
@@ -1,11 +1,18 @@
[package]
-name = "acrate-nodeinfo"
-version = "0.1.0"
+name = "acrate_nodeinfo"
+version = "0.3.0"
+authors = ["Stefano Pigozzi "]
edition = "2021"
+description = "Rust typing and utilities for the NodeInfo format"
+repository = "https://forge.steffo.eu/unimore/tirocinio-canali-steffo-acrate"
+license = "EUPL-1.2"
+keywords = ["nodeinfo", "fediverse"]
+categories = ["web-programming"]
[dependencies]
-acrate-hostmeta = { path = "../acrate-hostmeta" }
+acrate_rd = { path = "../acrate_rd" }
log = "0.4.22"
+mime = "0.3.17"
reqwest = { version = "0.12.9", features = ["json", "stream"] }
serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.132"
diff --git a/acrate-nodeinfo/src/lib.rs b/acrate_nodeinfo/src/lib.rs
similarity index 86%
rename from acrate-nodeinfo/src/lib.rs
rename to acrate_nodeinfo/src/lib.rs
index 8500f91..5bc85f0 100644
--- a/acrate-nodeinfo/src/lib.rs
+++ b/acrate_nodeinfo/src/lib.rs
@@ -1,4 +1,4 @@
-//! Serde-based NodeInfo fetcher and loose parser.
+//! Rust typing and utilities for the NodeInfo format.
//!
//! > NodeInfo is an effort to create a standardized way of exposing metadata about a server running one of the distributed social networks.
//!
@@ -6,7 +6,9 @@
//!
//! -
//! -
+//!
+use std::str::FromStr;
use serde::Deserialize;
use thiserror::Error;
@@ -15,6 +17,7 @@ use thiserror::Error;
/// # Specification
///
/// -
+///
#[derive(Debug, Clone)]
pub enum NodeInfo {
V1(NodeInfo1),
@@ -206,7 +209,7 @@ impl NodeInfo {
base.set_path(Self::WELLKNOWN_NODEINFO_PATH);
log::trace!("Discovering NodeInfo document locations...");
- let discovery = acrate_hostmeta::any::ResourceDescriptor::get(client, base)
+ let discovery = acrate_rd::any::ResourceDescriptor::get(client, base)
.await
.map_err(Get)?
.jrd();
@@ -315,7 +318,7 @@ impl NodeInfo {
pub enum NodeInfoGetWellknownError {
/// The discovery of possible locations for NodeInfo documents failed.
#[error("the discovery of possible locations for NodeInfo documents failed")]
- Get(acrate_hostmeta::any::GetError),
+ Get(acrate_rd::any::GetError),
/// No compatible NodeInfo documents were detected at the given URL.
#[error("no compatible NodeInfo documents were detected at the given URL")]
Unsupported,
@@ -346,21 +349,27 @@ impl NodeInfo1 {
let response = client.execute(request)
.await
.map_err(Request)?;
-
+
log::trace!("Checking `Content-Type` of the response...");
let content_type = response
.headers()
.get(reqwest::header::CONTENT_TYPE)
.ok_or(ContentTypeMissing)?;
-
- log::trace!("Extracting MIME type from the `Content-Type` header...");
- let mime_type = extract_mime_from_content_type(content_type)
- .ok_or(ContentTypeInvalid)?;
-
- log::trace!("Ensuring MIME type is acceptable for NodeInfo documents...");
- if mime_type != "application/json" {
- log::error!("MIME type `{mime_type}` is not acceptable for NodeInfo documents.");
- return Err(ContentTypeInvalid)
+
+ log::trace!("Extracting media type from the `Content-Type` header...");
+ let mime_type = content_type.to_str()
+ .map_err(ContentTypeUnprintable)?;
+
+ log::trace!("Parsing media type: {mime_type:?}");
+ let mime_type = mime::Mime::from_str(mime_type)
+ .map_err(ContentTypeInvalid)?;
+
+ log::trace!("Checking if media type is supported: {mime_type:?}");
+ let mime_is_json = mime_type == mime::APPLICATION_JSON;
+ log::trace!("Is media type application/json? {mime_is_json:?}");
+ if !mime_is_json {
+ log::error!("MIME type `{mime_type}` is not acceptable for JSON parsing.");
+ return Err(ContentTypeUnsupported);
}
log::trace!("Attempting to parse response as JSON...");
@@ -418,21 +427,27 @@ impl NodeInfo2 {
let response = client.execute(request)
.await
.map_err(Request)?;
-
+
log::trace!("Checking `Content-Type` of the response...");
let content_type = response
.headers()
.get(reqwest::header::CONTENT_TYPE)
.ok_or(ContentTypeMissing)?;
-
- log::trace!("Extracting MIME type from the `Content-Type` header...");
- let mime_type = extract_mime_from_content_type(content_type)
- .ok_or(ContentTypeInvalid)?;
-
- log::trace!("Ensuring MIME type is acceptable for NodeInfo documents...");
- if mime_type != "application/json" {
- log::error!("MIME type `{mime_type}` is not acceptable for NodeInfo documents.");
- return Err(ContentTypeInvalid)
+
+ log::trace!("Extracting media type from the `Content-Type` header...");
+ let mime_type = content_type.to_str()
+ .map_err(ContentTypeUnprintable)?;
+
+ log::trace!("Parsing media type: {mime_type:?}");
+ let mime_type = mime::Mime::from_str(mime_type)
+ .map_err(ContentTypeInvalid)?;
+
+ log::trace!("Checking if media type is supported...");
+ let mime_is_json = mime_type == mime::APPLICATION_JSON;
+ log::trace!("Is media type application/json? {mime_is_json:?}");
+ if !mime_is_json {
+ log::error!("MIME type `{mime_type}` is not acceptable for JSON parsing.");
+ return Err(ContentTypeUnsupported);
}
log::trace!("Attempting to parse response as JSON...");
@@ -455,14 +470,22 @@ pub enum NodeInfoGetError {
/// The HTTP request failed.
#[error("the HTTP request failed")]
Request(reqwest::Error),
-
+
/// The `Content-Type` header of the response is missing.
#[error("the Content-Type header of the response is missing")]
ContentTypeMissing,
-
- /// The `Content-Type` header of the response is invalid.
- #[error("the Content-Type header of the response is invalid")]
- ContentTypeInvalid,
+
+ /// The `Content-Type` header of the response can't be converted to a [`str`].
+ #[error("the Content-Type header of the response cannot be converted to a &str")]
+ ContentTypeUnprintable(reqwest::header::ToStrError),
+
+ /// The `Content-Type` header of the response is not a valid [`mime::Mime`] type.
+ #[error("the Content-Type header of the response is not a valid media type")]
+ ContentTypeInvalid(mime::FromStrError),
+
+ /// The `Content-Type` header of the response is not a supported [`mime::Mime`] type.
+ #[error("the Content-Type header of the response is not a supported media type")]
+ ContentTypeUnsupported,
/// The document failed to be parsed as JSON by [`reqwest`].
#[error("the document failed to be parsed as JSON")]
@@ -472,12 +495,3 @@ pub enum NodeInfoGetError {
#[error("the returned NodeInfo version would not match the version of the called method")]
Version,
}
-
-/// Extract the MIME type from the value of the `Content-Type` header.
-fn extract_mime_from_content_type(value: &reqwest::header::HeaderValue) -> Option {
- let value = value.to_str().ok()?;
- match value.split_once("; ") {
- None => Some(value.to_string()),
- Some((mime, _)) => Some(mime.to_string()),
- }
-}
diff --git a/acrate-nodeinfo/tests/nodeinfo_tests.rs b/acrate_nodeinfo/tests/integration_tests.rs
similarity index 100%
rename from acrate-nodeinfo/tests/nodeinfo_tests.rs
rename to acrate_nodeinfo/tests/integration_tests.rs
diff --git a/acrate_rd/Cargo.toml b/acrate_rd/Cargo.toml
index 68f4fbb..8568dd0 100644
--- a/acrate_rd/Cargo.toml
+++ b/acrate_rd/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
description = "Rust typing and utilities for the JSON and XML resource descriptior formats"
repository = "https://forge.steffo.eu/unimore/tirocinio-canali-steffo-acrate"
license = "EUPL-1.2"
-keywords = ["jrd", "xrd", "hostmeta", "webfinger", "resource-descriptor"]
+keywords = ["jrd", "xrd", "host-meta", "webfinger", "resource-descriptor"]
categories = ["web-programming"]
[dependencies]
diff --git a/acrate_rd/src/jrd.rs b/acrate_rd/src/jrd.rs
index 79dc599..1c08b40 100644
--- a/acrate_rd/src/jrd.rs
+++ b/acrate_rd/src/jrd.rs
@@ -181,7 +181,7 @@ impl ResourceDescriptorJRD {
let mime_type = mime::Mime::from_str(mime_type)
.map_err(ContentTypeInvalid)?;
- log::trace!("Checking if media type is supported...");
+ log::trace!("Checking if media type is supported: {mime_type:?}");
let mime_is_json = mime_type == mime::APPLICATION_JSON;
log::trace!("Is media type application/json? {mime_is_json:?}");
let mime_is_jrd =
diff --git a/acrate_rd/src/xrd.rs b/acrate_rd/src/xrd.rs
index 93d18d5..3d6ae6b 100644
--- a/acrate_rd/src/xrd.rs
+++ b/acrate_rd/src/xrd.rs
@@ -217,9 +217,9 @@ impl ResourceDescriptorXRD {
let mime_type = mime::Mime::from_str(mime_type)
.map_err(ContentTypeInvalid)?;
- log::trace!("Checking if media type is supported...");
+ log::trace!("Checking if media type is supported: {mime_type:?}");
let mime_is_xrd =
- mime_type.type_() == mime::APPLICATION
+ mime_type.type_().as_str() == mime::APPLICATION
&& mime_type.subtype() == "xrd"
&& mime_type.suffix() == Some(mime::XML);
log::trace!("Is media type application/xrd+xml? {mime_is_xrd:?}");
diff --git a/acrate-webfinger/Cargo.toml b/acrate_rdserver/Cargo.toml
similarity index 52%
rename from acrate-webfinger/Cargo.toml
rename to acrate_rdserver/Cargo.toml
index c6983f1..b47b7af 100644
--- a/acrate-webfinger/Cargo.toml
+++ b/acrate_rdserver/Cargo.toml
@@ -1,11 +1,17 @@
[package]
-name = "acrate-webfinger"
-version = "0.1.0"
+name = "acrate_rdserver"
+version = "0.3.0"
+authors = ["Stefano Pigozzi "]
edition = "2021"
+description = "Resource descriptor web server for the acrate project"
+repository = "https://forge.steffo.eu/unimore/tirocinio-canali-steffo-acrate"
+license = "EUPL-1.2"
+keywords = ["jrd", "xrd", "hostmeta", "webfinger", "resource-descriptor"]
+categories = ["web-programming"]
[dependencies]
acrate_database = { path = "../acrate_database" }
-acrate-hostmeta = { path = "../acrate-hostmeta" }
+acrate_rd = { path = "../acrate_rd" }
anyhow = "1.0.93"
axum = { version = "0.7.7", features = ["macros"] }
axum-extra = { version = "0.9.4", features = ["query"] }
@@ -17,3 +23,7 @@ quick-xml = { version = "0.37.0", features = ["serialize"] }
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.132"
tokio = { version = "1.41.1", features = ["macros", "net", "rt-multi-thread"] }
+mime = "0.3.17"
+
+[lints.clippy]
+tabs-in-doc-comments = "allow"
diff --git a/acrate-webfinger/src/config.rs b/acrate_rdserver/src/config.rs
similarity index 100%
rename from acrate-webfinger/src/config.rs
rename to acrate_rdserver/src/config.rs
diff --git a/acrate-webfinger/src/main.rs b/acrate_rdserver/src/main.rs
similarity index 100%
rename from acrate-webfinger/src/main.rs
rename to acrate_rdserver/src/main.rs
diff --git a/acrate-webfinger/src/route.rs b/acrate_rdserver/src/route.rs
similarity index 86%
rename from acrate-webfinger/src/route.rs
rename to acrate_rdserver/src/route.rs
index bda54ad..9ec95bc 100644
--- a/acrate-webfinger/src/route.rs
+++ b/acrate_rdserver/src/route.rs
@@ -6,8 +6,8 @@ use serde::Deserialize;
use acrate_database::diesel::GroupedBy;
use acrate_database::diesel_async::{AsyncConnection, AsyncPgConnection};
use acrate_database::meta::{MetaAlias, MetaLink, MetaLinkProperty, MetaLinkTitle, MetaProperty, MetaSubject};
-use acrate_hostmeta::jrd::ResourceDescriptorLinkJRD;
-use acrate_hostmeta::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD, ResourceDescriptorTitleXRD};
+use acrate_rd::jrd::ResourceDescriptorLinkJRD;
+use acrate_rd::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD, ResourceDescriptorTitleXRD};
use crate::config;
#[derive(Debug, Clone, Deserialize)]
@@ -47,7 +47,7 @@ pub async fn webfinger_handler(
.await
.map_err(|_| StatusCode::BAD_GATEWAY)?;
- let subjects = MetaSubject::query_matching(&mut conn, WEBFINGER_DOC, &resource)
+ let subjects = MetaSubject::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@@ -76,24 +76,24 @@ pub async fn webfinger_handler(
let subject = subject.subject.clone();
- let aliases = MetaAlias::query_matching(&mut conn, WEBFINGER_DOC, &resource)
+ let aliases = MetaAlias::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
- let properties = MetaProperty::query_matching(&mut conn, WEBFINGER_DOC, &resource)
+ let properties = MetaProperty::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
- let links = MetaLink::query_matching(&mut conn, WEBFINGER_DOC, &resource)
+ let links = MetaLink::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
- let link_properties = MetaLinkProperty::query_by_link(&mut conn, &links)
+ let link_properties = MetaLinkProperty::aquery_by_link(&mut conn, &links)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.grouped_by(&links);
- let link_titles = MetaLinkTitle::query_by_link(&mut conn, &links)
+ let link_titles = MetaLinkTitle::aquery_by_link(&mut conn, &links)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.grouped_by(&links);
@@ -143,7 +143,7 @@ pub async fn webfinger_handler(
.into_iter()
.map(|(link, properties, titles)| ResourceDescriptorLinkJRD {
rel: link.rel,
- r#type: link.type_,
+ r#type: link.type_.map(|m| m.0),
href: link.href,
template: link.template,
properties: properties
@@ -157,7 +157,7 @@ pub async fn webfinger_handler(
})
.collect::>();
- let rd = acrate_hostmeta::jrd::ResourceDescriptorJRD {
+ let rd = acrate_rd::jrd::ResourceDescriptorJRD {
subject,
aliases,
properties,
@@ -192,7 +192,7 @@ pub async fn webfinger_handler(
.into_iter()
.map(|(link, properties, titles)| ResourceDescriptorLinkXRD {
rel: link.rel,
- r#type: link.type_,
+ r#type: link.type_.map(|m| m.0),
href: link.href,
template: link.template,
properties: properties
@@ -212,7 +212,7 @@ pub async fn webfinger_handler(
})
.collect::>();
- let rd = acrate_hostmeta::xrd::ResourceDescriptorXRD {
+ let rd = acrate_rd::xrd::ResourceDescriptorXRD {
subject,
aliases,
properties,
@@ -241,12 +241,12 @@ pub async fn webfinger_handler(
})
.collect();
- let links: Vec<(String, Option, Option, Option, Vec<(String, Option)>, Vec<(String, String)>)> = links_full
+ let links: Vec<(String, Option, Option, Option, Vec<(String, Option)>, Vec<(String, String)>)> = links_full
.into_iter()
.map(|(link, properties, titles)| {
(
link.rel,
- link.type_,
+ link.type_.map(|m| m.0),
link.href,
link.template,
properties.into_iter()
diff --git a/acrate-webfinger/src/webfinger.html.j2 b/acrate_rdserver/src/webfinger.html.j2
similarity index 100%
rename from acrate-webfinger/src/webfinger.html.j2
rename to acrate_rdserver/src/webfinger.html.j2