Compare commits
6 commits
c972fbff31
...
f26c7a04f4
Author | SHA1 | Date | |
---|---|---|---|
f26c7a04f4 | |||
4a2e2ff381 | |||
829396846e | |||
f756e10557 | |||
5fdbf4a827 | |||
5d958630f6 |
13 changed files with 265 additions and 18 deletions
|
@ -15,7 +15,7 @@ acrate_utils = { path = "../acrate_utils" }
|
|||
anyhow = "1.0.93"
|
||||
axum = { version = "0.7.7", features = ["macros"] }
|
||||
log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] }
|
||||
micronfig = "0.3.0"
|
||||
micronfig = "1.0.0"
|
||||
minijinja = "2.5.0"
|
||||
pretty_env_logger = "0.5.0"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
|
@ -25,3 +25,4 @@ mediatype = { version = "0.19.18", features = ["serde"] }
|
|||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
|
@ -13,6 +13,7 @@ pub async fn healthcheck_handler() -> Result<StatusCode, StatusCode> {
|
|||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
#[allow(unused_mut)] // TODO
|
||||
pub async fn inbox_handler() -> Result<Response<String>, StatusCode> {
|
||||
log::debug!("Handling an inbox request!");
|
||||
|
||||
|
|
|
@ -13,12 +13,18 @@ categories = ["web-programming"]
|
|||
iref = "3.2.2"
|
||||
json-ld = { version = "0.21.1", features = ["serde", "reqwest"] }
|
||||
log = "0.4.22"
|
||||
anyhow = "1.0.95"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
static-iref = "3.0.0"
|
||||
thiserror = "2.0.3"
|
||||
|
||||
mediatype = { version = "0.19.18", features = ["serde"] }
|
||||
language-tags = { version = "0.3.2", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread"] }
|
||||
tokio-test = "0.4.4"
|
||||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
//! Struct definitions for ActivityStreams Core and Extended Types.
|
||||
//!
|
||||
//! # Specification
|
||||
//!
|
||||
//! - <https://www.w3.org/TR/activitystreams-vocabulary/>
|
||||
//!
|
109
acrate_astreams/src/activitystreams/jsonld.rs
Normal file
109
acrate_astreams/src/activitystreams/jsonld.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use anyhow::{anyhow, Error};
|
||||
use static_iref::iri;
|
||||
use anyhow::Result as AResult;
|
||||
use iref::Iri;
|
||||
use mediatype::MediaType;
|
||||
use crate::activitystreams::StreamsLink;
|
||||
|
||||
pub trait StreamsJsonLD {
|
||||
fn get_one_str(&self, id: &Iri) -> Option<AResult<String>>;
|
||||
fn get_multiple_str(&self, id: &Iri) -> impl Iterator<Item = AResult<String>>;
|
||||
fn get_one_mediatype(&self, id: &Iri) -> Option<AResult<MediaType>>;
|
||||
}
|
||||
|
||||
impl StreamsJsonLD for &json_ld::Node {
|
||||
fn get_one_str(&self, id: &Iri) -> Option<AResult<String>> {
|
||||
let property = match self.properties.get_any(&id) {
|
||||
None => return None,
|
||||
Some(property) => property,
|
||||
};
|
||||
|
||||
let value = match property.as_value() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD value"))),
|
||||
Some(value) => value,
|
||||
};
|
||||
|
||||
let r#str = match value.as_str() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD string"))),
|
||||
Some(string) => string
|
||||
};
|
||||
|
||||
let string = r#str.to_string();
|
||||
|
||||
Some(Ok(string))
|
||||
}
|
||||
|
||||
fn get_multiple_str(&self, id: &Iri) -> impl Iterator<Item = AResult<String>> {
|
||||
let properties = self.properties.get(&id);
|
||||
|
||||
let values = properties.map(|v| v
|
||||
.as_value()
|
||||
.ok_or(anyhow!("Couldn't process property as JSON-LD value"))
|
||||
);
|
||||
|
||||
let strs = values.flat_map(|v| v
|
||||
.map(|v| v
|
||||
.as_str()
|
||||
.ok_or(anyhow!("Couldn't process property as JSON-LD string"))
|
||||
)
|
||||
);
|
||||
|
||||
let strings = strs.map(|v| v
|
||||
.map(|v| v
|
||||
.to_string()
|
||||
)
|
||||
);
|
||||
|
||||
strings
|
||||
}
|
||||
|
||||
fn get_one_mediatype(&self, id: &Iri) -> Option<AResult<MediaType>> {
|
||||
let property = match self.properties.get_any(&id) {
|
||||
None => return None,
|
||||
Some(property) => property,
|
||||
};
|
||||
|
||||
let value = match property.as_value() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD value"))),
|
||||
Some(value) => value,
|
||||
};
|
||||
|
||||
let string = match value.as_str() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD string"))),
|
||||
Some(string) => string
|
||||
};
|
||||
|
||||
let mediatype = match MediaType::parse(string) {
|
||||
Err(e) => return Some(Err(Error::from(e).context("Couldn't parse property as MIME media type"))),
|
||||
Ok(mediatype) => mediatype,
|
||||
};
|
||||
|
||||
Some(Ok(mediatype))
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamsLink for &json_ld::Node {
|
||||
fn streams_href(&self) -> Option<AResult<String>> {
|
||||
self.get_one_str(
|
||||
iri!("https://www.w3.org/ns/activitystreams#href")
|
||||
)
|
||||
}
|
||||
|
||||
fn streams_rel(&self) -> impl Iterator<Item = AResult<String>> {
|
||||
self.get_multiple_str(
|
||||
iri!("https://www.w3.org/ns/activitystreams#rel")
|
||||
)
|
||||
}
|
||||
|
||||
fn streams_media_type(&self) -> Option<AResult<MediaType>> {
|
||||
self.get_one_mediatype(
|
||||
iri!("https://www.w3.org/ns/activitystreams#mediaType")
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
fn streams_name(&self) -> impl Iterator<Item=AResult<(Option<LanguageTag>, String)>> {
|
||||
todo!()
|
||||
}
|
||||
*/
|
||||
}
|
36
acrate_astreams/src/activitystreams/mod.rs
Normal file
36
acrate_astreams/src/activitystreams/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
//! Struct definitions for ActivityStreams Core and Extended Types.
|
||||
//!
|
||||
//! # Functional
|
||||
//!
|
||||
//! > Properties marked as being "Functional" can have only one value.
|
||||
//! >
|
||||
//! > Items not marked as "Functional" can have multiple values.
|
||||
//!
|
||||
//! # Specification
|
||||
//!
|
||||
//! - <https://www.w3.org/TR/activitystreams-vocabulary/>
|
||||
//!
|
||||
|
||||
use anyhow::Result as AResult;
|
||||
use mediatype::MediaType;
|
||||
|
||||
pub mod jsonld;
|
||||
|
||||
/// Something that can be considered a `https://www.w3.org/ns/activitystreams#Object`.
|
||||
pub trait StreamsObject {
|
||||
|
||||
}
|
||||
|
||||
/// Something that can be considered a `https://www.w3.org/ns/activitystreams#Link`.
|
||||
pub trait StreamsLink {
|
||||
fn streams_href(&self) -> Option<AResult<String>>;
|
||||
|
||||
// FIXME: This accepts any kind of string, and does not filter to HTML link relations
|
||||
fn streams_rel(&self) -> impl Iterator<Item = AResult<String>>;
|
||||
|
||||
fn streams_media_type(&self) -> Option<AResult<MediaType>>;
|
||||
|
||||
/*
|
||||
fn streams_name(&self) -> impl Iterator<Item = AResult<(LanguageTag, String)>>;
|
||||
*/
|
||||
}
|
|
@ -2,9 +2,7 @@
|
|||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"name": "A thank-you note",
|
||||
"type": "Note",
|
||||
"content": "Thank you <a href='http://sally.example.org'>@sally</a>
|
||||
for all your hard work!
|
||||
<a href='http://example.org/tags/givingthanks'>#givingthanks</a>",
|
||||
"content": "Thank you <a href='http://sally.example.org'>@sally</a> for all your hard work! <a href='http://example.org/tags/givingthanks'>#givingthanks</a>",
|
||||
"to": {
|
||||
"name": "Sally",
|
||||
"type": "Person",
|
||||
|
@ -14,4 +12,4 @@
|
|||
"id": "http://example.org/tags/givingthanks",
|
||||
"name": "#givingthanks"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,44 @@
|
|||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
macro_rules! test_example {
|
||||
($modname:ident, $filename:literal) => {
|
||||
mod $modname {
|
||||
const DATA: &'static str = include_str!($filename);
|
||||
use json_ld::syntax::Parse;
|
||||
use json_ld::Expand;
|
||||
|
||||
#[test]
|
||||
fn test_example_exists() {
|
||||
assert_ne!(DATA.len(), 0)
|
||||
pub const DATA: &'static str = include_str!($filename);
|
||||
|
||||
pub fn parse() -> json_ld::syntax::Value {
|
||||
let (value, _codemap) = json_ld::syntax::Value::parse_str(DATA)
|
||||
.expect("Failed to parse example");
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
pub fn document() -> json_ld::RemoteDocument {
|
||||
let document = json_ld::RemoteDocument::new(
|
||||
None,
|
||||
None,
|
||||
parse(),
|
||||
);
|
||||
|
||||
document
|
||||
}
|
||||
|
||||
pub async fn expand() -> json_ld::ExpandedDocument {
|
||||
let mut loader = json_ld::ReqwestLoader::new();
|
||||
let doc = document();
|
||||
let expanded = doc.expand(&mut loader)
|
||||
.await
|
||||
.expect("Failed to expand JSON-LD document");
|
||||
|
||||
expanded
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_expand() {
|
||||
let doc = expand().await;
|
||||
println!("{doc:#?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,3 +203,67 @@ test_example!(e156, "./activitystreams/examples/156.json");
|
|||
test_example!(e157, "./activitystreams/examples/157.json");
|
||||
test_example!(e158, "./activitystreams/examples/158.json");
|
||||
test_example!(e159, "./activitystreams/examples/159.json");
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_href() {
|
||||
let doc = e121::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let href = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.streams_href()
|
||||
.expect("Property `href` was not found")
|
||||
.expect("Property `href` failed to process")
|
||||
};
|
||||
|
||||
assert_eq!(href, "http://example.org/abc");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_rel() {
|
||||
let doc = e131::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let mut rels = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.streams_rel()
|
||||
.map(|v| v
|
||||
.expect("Property `rel` failed to process")
|
||||
)
|
||||
};
|
||||
|
||||
let rel = rels.next()
|
||||
.expect("Expected `rel` [0] to be present");
|
||||
|
||||
assert_eq!(rel, "canonical");
|
||||
|
||||
let rel = rels.next()
|
||||
.expect("Expected `rel` [1] to be present");
|
||||
|
||||
assert_eq!(rel, "preview");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_media_type() {
|
||||
let doc = e126::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let href = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.streams_media_type()
|
||||
.expect("Property `mediaType` was not found")
|
||||
.expect("Property `mediaType` failed to process")
|
||||
};
|
||||
|
||||
assert_eq!(href, mediatype::media_type!(TEXT/HTML));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ diesel-async = { version = "0.5.1", features = ["postgres"] }
|
|||
diesel_migrations = { version = "2.2.0", optional = true }
|
||||
log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] }
|
||||
mediatype = "0.19.18"
|
||||
micronfig = { version = "0.3.0", optional = true }
|
||||
micronfig = { version = "1.0.0", optional = true }
|
||||
mime = "0.3.17"
|
||||
pretty_env_logger = { version = "0.5.0", optional = true }
|
||||
uuid = "1.11.0"
|
||||
|
@ -36,3 +36,4 @@ required-features = ["bin"]
|
|||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
|
@ -25,3 +25,4 @@ tokio-test = "0.4.4"
|
|||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
|
@ -25,3 +25,4 @@ tokio-test = "0.4.4"
|
|||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
|
@ -17,7 +17,7 @@ anyhow = "1.0.93"
|
|||
axum = { version = "0.7.7", features = ["macros"] }
|
||||
axum-extra = { version = "0.9.4", features = ["query"] }
|
||||
log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] }
|
||||
micronfig = "0.3.0"
|
||||
micronfig = "1.0.0"
|
||||
pretty_env_logger = "0.5.0"
|
||||
quick-xml = { version = "0.37.0", features = ["serialize"] }
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
|
@ -28,3 +28,4 @@ mediatype = { version = "0.19.18", features = ["serde"] }
|
|||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
type-complexity = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
|
@ -20,3 +20,4 @@ tokio = { version = "1.41.1", features = ["net"] }
|
|||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
let-and-return = "allow"
|
||||
|
|
Loading…
Add table
Reference in a new issue