Compare commits
8 commits
f26c7a04f4
...
70053021a5
Author | SHA1 | Date | |
---|---|---|---|
70053021a5 | |||
678daac2eb | |||
690afcd15f | |||
1ff33fcbdb | |||
71485b50af | |||
8c191cf9ae | |||
ea126d11f4 | |||
ffe5c3cc03 |
3 changed files with 378 additions and 38 deletions
|
@ -2,17 +2,28 @@ use anyhow::{anyhow, Error};
|
|||
use static_iref::iri;
|
||||
use anyhow::Result as AResult;
|
||||
use iref::Iri;
|
||||
use json_ld::Direction;
|
||||
use json_ld::object::Any;
|
||||
use json_ld::syntax::LangTagBuf;
|
||||
use mediatype::MediaType;
|
||||
use crate::activitystreams::StreamsLink;
|
||||
use crate::activitystreams::{StreamsEntity, 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>>;
|
||||
pub type LangTriple = (String, Option<LangTagBuf>, Option<Direction>);
|
||||
|
||||
pub trait StreamsJsonLD<Entity> {
|
||||
fn jsonld_any_value_string(&self, id: &Iri) -> Option<AResult<String>>;
|
||||
fn jsonld_iter_value_string(&self, id: &Iri) -> impl Iterator<Item = AResult<String>>;
|
||||
fn jsonld_iter_value_langstring(&self, id: &Iri) -> impl Iterator<Item = AResult<LangTriple>>;
|
||||
fn jsonld_any_value_mediatype(&self, id: &Iri) -> Option<AResult<MediaType>>;
|
||||
fn jsonld_any_node_string(&self, id: &Iri) -> Option<AResult<String>>;
|
||||
fn jsonld_any_value_langtag(&self, id: &Iri) -> Option<AResult<LangTagBuf>>;
|
||||
fn jsonld_any_value_u32(&self, id: &Iri) -> Option<AResult<u32>>;
|
||||
fn jsonld_any_value_u64(&self, id: &Iri) -> Option<AResult<u64>>;
|
||||
fn jsonld_iter_node_entity(&self, id: &Iri) -> impl Iterator<Item = AResult<Entity>>;
|
||||
}
|
||||
|
||||
impl StreamsJsonLD for &json_ld::Node {
|
||||
fn get_one_str(&self, id: &Iri) -> Option<AResult<String>> {
|
||||
impl StreamsJsonLD<json_ld::Node> for json_ld::Node {
|
||||
fn jsonld_any_value_string(&self, id: &Iri) -> Option<AResult<String>> {
|
||||
let property = match self.properties.get_any(&id) {
|
||||
None => return None,
|
||||
Some(property) => property,
|
||||
|
@ -25,7 +36,7 @@ impl StreamsJsonLD for &json_ld::Node {
|
|||
|
||||
let r#str = match value.as_str() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD string"))),
|
||||
Some(string) => string
|
||||
Some(r#str) => r#str
|
||||
};
|
||||
|
||||
let string = r#str.to_string();
|
||||
|
@ -33,7 +44,7 @@ impl StreamsJsonLD for &json_ld::Node {
|
|||
Some(Ok(string))
|
||||
}
|
||||
|
||||
fn get_multiple_str(&self, id: &Iri) -> impl Iterator<Item = AResult<String>> {
|
||||
fn jsonld_iter_value_string(&self, id: &Iri) -> impl Iterator<Item = AResult<String>> {
|
||||
let properties = self.properties.get(&id);
|
||||
|
||||
let values = properties.map(|v| v
|
||||
|
@ -57,7 +68,44 @@ impl StreamsJsonLD for &json_ld::Node {
|
|||
strings
|
||||
}
|
||||
|
||||
fn get_one_mediatype(&self, id: &Iri) -> Option<AResult<MediaType>> {
|
||||
fn jsonld_iter_value_langstring(&self, id: &Iri) -> impl Iterator<Item = AResult<LangTriple>> {
|
||||
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 values = values.flat_map(|v| v
|
||||
.map(|v| {
|
||||
let string = v
|
||||
.as_str()
|
||||
.ok_or(anyhow!("Expected property to be a langString, but no string was obtained"))?
|
||||
.to_string();
|
||||
|
||||
let lang = v
|
||||
.language()
|
||||
.map(|l| l
|
||||
.as_well_formed()
|
||||
.ok_or(anyhow!("Expected property to have a valid language tag, but got an invalid one instead"))
|
||||
);
|
||||
|
||||
let lang = match lang {
|
||||
None => None,
|
||||
Some(Ok(lang)) => Some(lang.to_owned()),
|
||||
Some(Err(err)) => return Err(err),
|
||||
};
|
||||
|
||||
let direction = v.direction();
|
||||
|
||||
Ok((string, lang, direction))
|
||||
})
|
||||
);
|
||||
|
||||
values
|
||||
}
|
||||
|
||||
fn jsonld_any_value_mediatype(&self, id: &Iri) -> Option<AResult<MediaType>> {
|
||||
let property = match self.properties.get_any(&id) {
|
||||
None => return None,
|
||||
Some(property) => property,
|
||||
|
@ -68,42 +116,177 @@ impl StreamsJsonLD for &json_ld::Node {
|
|||
Some(value) => value,
|
||||
};
|
||||
|
||||
let string = match value.as_str() {
|
||||
let r#str = match value.as_str() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD string"))),
|
||||
Some(string) => string
|
||||
Some(r#str) => r#str
|
||||
};
|
||||
|
||||
let mediatype = match MediaType::parse(string) {
|
||||
let mediatype = match MediaType::parse(r#str) {
|
||||
Err(e) => return Some(Err(Error::from(e).context("Couldn't parse property as MIME media type"))),
|
||||
Ok(mediatype) => mediatype,
|
||||
};
|
||||
|
||||
Some(Ok(mediatype))
|
||||
}
|
||||
|
||||
fn jsonld_any_node_string(&self, id: &Iri) -> Option<AResult<String>> {
|
||||
let property = match self.properties.get_any(&id) {
|
||||
None => return None,
|
||||
Some(property) => property,
|
||||
};
|
||||
|
||||
let node = match property.as_node() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD node"))),
|
||||
Some(value) => value,
|
||||
};
|
||||
|
||||
let id = match node.id() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property's JSON-LD node @id"))),
|
||||
Some(id) => id
|
||||
};
|
||||
|
||||
let string = id.to_string();
|
||||
|
||||
Some(Ok(string))
|
||||
}
|
||||
|
||||
fn jsonld_any_value_langtag(&self, id: &Iri) -> Option<AResult<LangTagBuf>> {
|
||||
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(r#str) => r#str
|
||||
};
|
||||
|
||||
let string = r#str.to_string();
|
||||
|
||||
let langtag = match LangTagBuf::new(string) {
|
||||
Err(e) => return Some(Err(anyhow!("Couldn't process property as a BCP47 language tag: {e:#?}"))),
|
||||
Ok(langtag) => langtag,
|
||||
};
|
||||
|
||||
Some(Ok(langtag))
|
||||
}
|
||||
|
||||
fn jsonld_any_value_u32(&self, id: &Iri) -> Option<AResult<u32>> {
|
||||
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 number = match value.as_number() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD number"))),
|
||||
Some(number) => number
|
||||
};
|
||||
|
||||
let r#u32 = match number.as_u32() {
|
||||
None => return Some(Err(anyhow!("Couldn't losslessly convert JSON-LD number to u64"))),
|
||||
Some(r#u32) => r#u32,
|
||||
};
|
||||
|
||||
Some(Ok(r#u32))
|
||||
}
|
||||
|
||||
fn jsonld_any_value_u64(&self, id: &Iri) -> Option<AResult<u64>> {
|
||||
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 number = match value.as_number() {
|
||||
None => return Some(Err(anyhow!("Couldn't process property as JSON-LD number"))),
|
||||
Some(number) => number
|
||||
};
|
||||
|
||||
let r#u64 = match number.as_u64() {
|
||||
None => return Some(Err(anyhow!("Couldn't losslessly convert JSON-LD number to u64"))),
|
||||
Some(r#u64) => r#u64,
|
||||
};
|
||||
|
||||
Some(Ok(r#u64))
|
||||
}
|
||||
|
||||
fn jsonld_iter_node_entity(&self, id: &Iri) -> impl Iterator<Item = AResult<json_ld::Node>> {
|
||||
let properties = self.properties.get(&id);
|
||||
|
||||
let nodes = properties.map(|v| v
|
||||
.as_node()
|
||||
.ok_or(anyhow!("Couldn't process property as JSON-LD node"))
|
||||
.map(|v| v
|
||||
.clone()
|
||||
)
|
||||
);
|
||||
|
||||
nodes
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamsLink for &json_ld::Node {
|
||||
fn streams_href(&self) -> Option<AResult<String>> {
|
||||
self.get_one_str(
|
||||
impl StreamsEntity<json_ld::Node> for json_ld::Node {
|
||||
fn activitystreams_previews(&self) -> impl Iterator<Item = AResult<json_ld::Node>> {
|
||||
self.jsonld_iter_node_entity(
|
||||
iri!("https://www.w3.org/ns/activitystreams#preview")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamsLink<json_ld::Node> for json_ld::Node {
|
||||
fn activitystreams_href(&self) -> Option<AResult<String>> {
|
||||
self.jsonld_any_node_string(
|
||||
iri!("https://www.w3.org/ns/activitystreams#href")
|
||||
)
|
||||
}
|
||||
|
||||
fn streams_rel(&self) -> impl Iterator<Item = AResult<String>> {
|
||||
self.get_multiple_str(
|
||||
fn activitystreams_rels_lenient(&self) -> impl Iterator<Item = AResult<String>> {
|
||||
self.jsonld_iter_value_string(
|
||||
iri!("https://www.w3.org/ns/activitystreams#rel")
|
||||
)
|
||||
}
|
||||
|
||||
fn streams_media_type(&self) -> Option<AResult<MediaType>> {
|
||||
self.get_one_mediatype(
|
||||
fn activitystreams_mediatype(&self) -> Option<AResult<MediaType>> {
|
||||
self.jsonld_any_value_mediatype(
|
||||
iri!("https://www.w3.org/ns/activitystreams#mediaType")
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
fn streams_name(&self) -> impl Iterator<Item=AResult<(Option<LanguageTag>, String)>> {
|
||||
todo!()
|
||||
fn activitystreams_names(&self) -> impl Iterator<Item = AResult<LangTriple>> {
|
||||
self.jsonld_iter_value_langstring(
|
||||
iri!("https://www.w3.org/ns/activitystreams#name")
|
||||
)
|
||||
}
|
||||
|
||||
fn activitystreams_hreflang(&self) -> Option<AResult<LangTagBuf>> {
|
||||
self.jsonld_any_value_langtag(
|
||||
iri!("https://www.w3.org/ns/activitystreams#hreflang")
|
||||
)
|
||||
}
|
||||
|
||||
fn activitystreams_height(&self) -> Option<AResult<u64>> {
|
||||
self.jsonld_any_value_u64(
|
||||
iri!("https://www.w3.org/ns/activitystreams#height")
|
||||
)
|
||||
}
|
||||
|
||||
fn activitystreams_width(&self) -> Option<AResult<u64>> {
|
||||
self.jsonld_any_value_u64(
|
||||
iri!("https://www.w3.org/ns/activitystreams#width")
|
||||
)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -12,25 +12,43 @@
|
|||
//!
|
||||
|
||||
use anyhow::Result as AResult;
|
||||
use json_ld::Direction;
|
||||
use json_ld::syntax::LangTagBuf;
|
||||
use mediatype::MediaType;
|
||||
|
||||
pub mod jsonld;
|
||||
|
||||
/// Something that is either a [`StreamsObject`] or a [`StreamsLink`].
|
||||
pub trait StreamsEntity<Preview> {
|
||||
fn activitystreams_previews(&self) -> impl Iterator<Item = AResult<Preview>>;
|
||||
}
|
||||
|
||||
/// Something that can be considered a `https://www.w3.org/ns/activitystreams#Object`.
|
||||
pub trait StreamsObject {
|
||||
pub trait StreamsObject<Preview> where
|
||||
Self: StreamsEntity<Preview>,
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Something that can be considered a `https://www.w3.org/ns/activitystreams#Link`.
|
||||
pub trait StreamsLink {
|
||||
fn streams_href(&self) -> Option<AResult<String>>;
|
||||
pub trait StreamsLink<Preview> where
|
||||
Self: StreamsEntity<Preview>,
|
||||
Preview: StreamsEntity<Preview>,
|
||||
{
|
||||
fn activitystreams_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 activitystreams_rels_lenient(&self) -> impl Iterator<Item = AResult<String>>;
|
||||
|
||||
fn streams_media_type(&self) -> Option<AResult<MediaType>>;
|
||||
fn activitystreams_mediatype(&self) -> Option<AResult<MediaType>>;
|
||||
|
||||
/*
|
||||
fn streams_name(&self) -> impl Iterator<Item = AResult<(LanguageTag, String)>>;
|
||||
*/
|
||||
fn activitystreams_names(&self) -> impl Iterator<Item = AResult<(String, Option<LangTagBuf>, Option<Direction>)>>;
|
||||
|
||||
fn activitystreams_hreflang(&self) -> Option<AResult<LangTagBuf>>;
|
||||
|
||||
// FIXME: This doesn't accept numbers greater than u64
|
||||
fn activitystreams_height(&self) -> Option<AResult<u64>>;
|
||||
|
||||
// FIXME: This doesn't accept numbers greater than u64
|
||||
fn activitystreams_width(&self) -> Option<AResult<u64>>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
use json_ld::syntax::LangTagBuf;
|
||||
use acrate_astreams::activitystreams::jsonld::LangTriple;
|
||||
|
||||
macro_rules! test_example {
|
||||
($modname:ident, $filename:literal) => {
|
||||
|
@ -215,7 +216,7 @@ async fn test_link_href() {
|
|||
let href = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.streams_href()
|
||||
node.activitystreams_href()
|
||||
.expect("Property `href` was not found")
|
||||
.expect("Property `href` failed to process")
|
||||
};
|
||||
|
@ -233,7 +234,7 @@ async fn test_link_rel() {
|
|||
let mut rels = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.streams_rel()
|
||||
node.activitystreams_rels_lenient()
|
||||
.map(|v| v
|
||||
.expect("Property `rel` failed to process")
|
||||
)
|
||||
|
@ -257,13 +258,151 @@ async fn test_link_media_type() {
|
|||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let href = {
|
||||
let mediatype = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.streams_media_type()
|
||||
node.activitystreams_mediatype()
|
||||
.expect("Property `mediaType` was not found")
|
||||
.expect("Property `mediaType` failed to process")
|
||||
};
|
||||
|
||||
assert_eq!(href, mediatype::media_type!(TEXT/HTML));
|
||||
assert_eq!(mediatype, mediatype::media_type!(TEXT/HTML));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_name_value() {
|
||||
let doc = e117::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let names: Vec<LangTriple> = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.activitystreams_names()
|
||||
.map(|v| v
|
||||
.expect("Property `name` failed to process")
|
||||
)
|
||||
.collect()
|
||||
};
|
||||
|
||||
let mut names = names.into_iter();
|
||||
|
||||
let (name, language, direction) = names.next().expect("Expected `name` [0] to be present");
|
||||
assert_eq!(name, "A simple note");
|
||||
assert_eq!(language, None);
|
||||
assert_eq!(direction, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_name_lang() {
|
||||
let doc = e118::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let names: Vec<LangTriple> = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.activitystreams_names()
|
||||
.map(|v| v
|
||||
.expect("Property `name` failed to process")
|
||||
)
|
||||
.collect()
|
||||
};
|
||||
|
||||
let mut names = names.into_iter();
|
||||
|
||||
let (name, language, direction) = names.next().expect("Expected `name` [0] to be present");
|
||||
assert_eq!(name, "A simple note");
|
||||
assert_eq!(language, Some(LangTagBuf::new("en".to_string()).unwrap()));
|
||||
assert_eq!(direction, None);
|
||||
|
||||
let (name, language, direction) = names.next().expect("Expected `name` [1] to be present");
|
||||
assert_eq!(name, "Una nota sencilla");
|
||||
assert_eq!(language, Some(LangTagBuf::new("es".to_string()).unwrap()));
|
||||
assert_eq!(direction, None);
|
||||
|
||||
let (name, language, direction) = names.next().expect("Expected `name` [2] to be present");
|
||||
assert_eq!(name, "一段简单的笔记");
|
||||
assert_eq!(language, Some(LangTagBuf::new("zh-Hans".to_string()).unwrap()));
|
||||
assert_eq!(direction, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_hreflang() {
|
||||
let doc = e122::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let hreflang = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.activitystreams_hreflang()
|
||||
.expect("Property `hreflang` was not found")
|
||||
.expect("Property `hreflang` failed to process")
|
||||
};
|
||||
|
||||
assert_eq!(hreflang, "en")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_height() {
|
||||
let doc = e120::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let height = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.activitystreams_width()
|
||||
.expect("Property `height` was not found")
|
||||
.expect("Property `height` failed to process")
|
||||
};
|
||||
|
||||
assert_eq!(height, 100u64)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_width() {
|
||||
let doc = e138::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let width = {
|
||||
use acrate_astreams::activitystreams::StreamsLink;
|
||||
|
||||
node.activitystreams_width()
|
||||
.expect("Property `width` was not found")
|
||||
.expect("Property `width` failed to process")
|
||||
};
|
||||
|
||||
assert_eq!(width, 100u64)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_link_preview() {
|
||||
let doc = e102::expand().await;
|
||||
|
||||
let node = doc.main_node()
|
||||
.expect("Main node was not found");
|
||||
|
||||
let previews: Vec<json_ld::Node> = {
|
||||
use acrate_astreams::activitystreams::StreamsEntity;
|
||||
|
||||
node.activitystreams_previews()
|
||||
.map(|v| v
|
||||
.expect("Property `preview` failed to process")
|
||||
)
|
||||
.collect()
|
||||
};
|
||||
|
||||
let mut previews = previews.into_iter();
|
||||
|
||||
let _preview = previews.next().expect("Expected `preview` [0] to be present");
|
||||
|
||||
// TODO: More tests when StreamsObject is implemented
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue