Simplify issuer, fix serialization
Depends on change to rdf-types, PR incoming
This commit is contained in:
parent
980fd91acb
commit
f646d759ca
|
@ -20,11 +20,16 @@ itertools = "0.10.5"
|
|||
json-ld = "0.9.1"
|
||||
locspan = "0.7.9"
|
||||
rdf-types = "0.12.4"
|
||||
sha2 = { version = "0.10.6", optional = true }
|
||||
|
||||
hex = { version = "0.4.3", optional = true }
|
||||
sha2 = { version = "0.10.6", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
iref = "2.2.0"
|
||||
reqwest = "0.11.13"
|
||||
static-iref = "2.0.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
[patch.crates-io]
|
||||
# json-ld = { path = "../json-ld/json-ld" }
|
||||
rdf-types = { path = "../rdf-types" }
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use contextual::WithContext;
|
||||
use iref::{Iri, IriBuf};
|
||||
use json_ld::{
|
||||
syntax::{parse::MetaError, Parse, Value},
|
||||
Flatten, JsonLdProcessor, Loader, RemoteDocument,
|
||||
};
|
||||
use json_ld_normalization::quad_to_string;
|
||||
use locspan::{Location, Meta, Span};
|
||||
use rdf_types::{generator::Blank, vocabulary::Index, IriVocabulary, IriVocabularyMut};
|
||||
use reqwest::Client;
|
||||
|
@ -32,8 +32,8 @@ struct Cache {
|
|||
|
||||
struct ReqwestLoader<I = Index, M = Location<I>, T = Value<M>, E = MetaError<M>> {
|
||||
cache: Cache,
|
||||
parser: Box<DynParser<I, M, T, E>>,
|
||||
client: Client,
|
||||
parser: Box<DynParser<I, M, T, E>>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
|
@ -55,6 +55,7 @@ impl Cache {
|
|||
impl<I, M, T, E> ReqwestLoader<I, M, T, E> {
|
||||
fn new(
|
||||
cache: Cache,
|
||||
client: Client,
|
||||
parser: impl 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
|
@ -62,18 +63,15 @@ impl<I, M, T, E> ReqwestLoader<I, M, T, E> {
|
|||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
client,
|
||||
parser: Box::new(parser),
|
||||
client: Client::builder()
|
||||
.user_agent("json-ld-playground")
|
||||
.build()
|
||||
.expect("Successful client"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Clone> ReqwestLoader<I, Location<I>, Value<Location<I>>, MetaError<Location<I>>> {
|
||||
fn default_with_cache(cache: Cache) -> Self {
|
||||
Self::new(cache, |_, file: &I, s| {
|
||||
fn with_default_parser(cache: Cache, client: Client) -> Self {
|
||||
Self::new(cache, client, |_, file: &I, s| {
|
||||
Value::parse_str(s, |span| Location::new(file.clone(), span))
|
||||
})
|
||||
}
|
||||
|
@ -83,7 +81,12 @@ impl<I: Clone> Default
|
|||
for ReqwestLoader<I, Location<I>, Value<Location<I>>, MetaError<Location<I>>>
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::default_with_cache(Cache::default())
|
||||
let client = Client::builder()
|
||||
.user_agent("json-ld-playground")
|
||||
.build()
|
||||
.expect("Successful client");
|
||||
|
||||
Self::with_default_parser(Cache::default(), client)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,17 +99,13 @@ impl<I, M, T, E> ReqwestLoader<I, M, T, E> {
|
|||
let url = vocabulary.iri(url).unwrap().to_owned();
|
||||
|
||||
if !PERMITTED_CONTEXTS.contains(&url.as_str()) {
|
||||
println!("Fetching {url} is not permitted");
|
||||
return Err(Error::NotPermitted(url));
|
||||
}
|
||||
|
||||
if let Some(cached) = self.cache.get(&url) {
|
||||
println!("Got {url} from cache");
|
||||
return Ok(cached);
|
||||
}
|
||||
|
||||
println!("Fetching {url}");
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.get(url.as_str())
|
||||
|
@ -166,22 +165,27 @@ impl<I: Send + Sync, T: Send, M: Send, E> Loader<I, M> for ReqwestLoader<I, M, T
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<(), AnyError> {
|
||||
let cache = Cache::new();
|
||||
let client = Client::builder()
|
||||
.user_agent("json-ld-playground")
|
||||
.build()
|
||||
.expect("Successful client");
|
||||
|
||||
for (iri, document) in [
|
||||
(
|
||||
iri!("https://masto.asonix.dog/actor"),
|
||||
MASTO_ASONIX_DOG_ACTOR,
|
||||
),
|
||||
(
|
||||
iri!("https://relay.asonix.dog/actor"),
|
||||
RELAY_ASONIX_DOG_ACTOR,
|
||||
),
|
||||
(
|
||||
iri!("https://masto.asonix.dog/users/asonix"),
|
||||
MASTO_ASONIX_DOG_ASONIX_ACTOR,
|
||||
),
|
||||
] {
|
||||
do_the_thing(cache.clone(), iri, document).await?;
|
||||
let iris = [
|
||||
iri!("https://relay.asonix.dog/actor"),
|
||||
iri!("https://masto.asonix.dog/actor"),
|
||||
iri!("https://masto.asonix.dog/users/asonix"),
|
||||
];
|
||||
|
||||
for iri in iris {
|
||||
let document = client
|
||||
.get(iri.as_str())
|
||||
.header("accept", "application/activity+json")
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
do_the_thing(cache.clone(), client.clone(), iri, &document).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -189,8 +193,9 @@ async fn main() -> Result<(), AnyError> {
|
|||
|
||||
async fn do_the_thing(
|
||||
cache: Cache,
|
||||
client: Client,
|
||||
iri: Iri<'static>,
|
||||
document: &'static str,
|
||||
document: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut vocabulary: rdf_types::IndexVocabulary = rdf_types::IndexVocabulary::new();
|
||||
|
||||
|
@ -199,17 +204,18 @@ async fn do_the_thing(
|
|||
let input = RemoteDocument::new(
|
||||
Some(iri_index.clone()),
|
||||
Some("application/activity+json".parse()?),
|
||||
Value::parse_str(document, |span| Location::new(iri_index, span)).expect("Failed to parse"),
|
||||
Value::parse_str(document, |span| Location::new(iri_index.clone(), span))
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
|
||||
let mut loader = ReqwestLoader::default_with_cache(cache);
|
||||
let mut loader = ReqwestLoader::with_default_parser(cache, client);
|
||||
|
||||
let expanded = input
|
||||
.expand_with(&mut vocabulary, &mut loader)
|
||||
.await
|
||||
.expect("Failed to expand");
|
||||
|
||||
let mut pre_gen = Blank::new().with_metadata(Location::new(iri_index, Span::default()));
|
||||
let mut pre_gen = Blank::new().with_metadata(Location::new(iri_index.clone(), Span::default()));
|
||||
|
||||
let flattened = expanded
|
||||
.flatten_with(&mut vocabulary, &mut pre_gen, true)
|
||||
|
@ -222,243 +228,15 @@ async fn do_the_thing(
|
|||
true,
|
||||
)?;
|
||||
|
||||
for quad in output_document.quads {
|
||||
let (subject, predicate, object, graph) = quad.into_parts();
|
||||
let mut strings = output_document
|
||||
.quads
|
||||
.iter()
|
||||
.map(|quad| quad_to_string(quad, &vocabulary))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let subject = subject.with(&vocabulary);
|
||||
let predicate = predicate.with(&vocabulary);
|
||||
let object = object.with(&vocabulary);
|
||||
strings.sort();
|
||||
|
||||
if let Some(graph) = graph {
|
||||
let graph = graph.with(&vocabulary);
|
||||
println!("{subject} {predicate} {object} {graph}");
|
||||
} else {
|
||||
println!("{subject} {predicate} {object}");
|
||||
}
|
||||
}
|
||||
println!("{}", strings.join(""));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const MASTO_ASONIX_DOG_ACTOR: &'static str = r#"
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"featuredTags": {
|
||||
"@id": "toot:featuredTags",
|
||||
"@type": "@id"
|
||||
},
|
||||
"alsoKnownAs": {
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
},
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value",
|
||||
"discoverable": "toot:discoverable",
|
||||
"Device": "toot:Device",
|
||||
"Ed25519Signature": "toot:Ed25519Signature",
|
||||
"Ed25519Key": "toot:Ed25519Key",
|
||||
"Curve25519Key": "toot:Curve25519Key",
|
||||
"EncryptedMessage": "toot:EncryptedMessage",
|
||||
"publicKeyBase64": "toot:publicKeyBase64",
|
||||
"deviceId": "toot:deviceId",
|
||||
"claim": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:claim"
|
||||
},
|
||||
"fingerprintKey": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:fingerprintKey"
|
||||
},
|
||||
"identityKey": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:identityKey"
|
||||
},
|
||||
"devices": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:devices"
|
||||
},
|
||||
"messageFranking": "toot:messageFranking",
|
||||
"messageType": "toot:messageType",
|
||||
"cipherText": "toot:cipherText",
|
||||
"suspended": "toot:suspended"
|
||||
}
|
||||
],
|
||||
"id": "https://masto.asonix.dog/actor",
|
||||
"type": "Application",
|
||||
"inbox": "https://masto.asonix.dog/actor/inbox",
|
||||
"outbox": "https://masto.asonix.dog/actor/outbox",
|
||||
"preferredUsername": "masto.asonix.dog",
|
||||
"url": "https://masto.asonix.dog/about/more?instance_actor=true",
|
||||
"manuallyApprovesFollowers": true,
|
||||
"publicKey": {
|
||||
"id": "https://masto.asonix.dog/actor#main-key",
|
||||
"owner": "https://masto.asonix.dog/actor",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx8zXS0QNg9YGUBsxAOBH\nJaxIn7i6t+Z4UOpSFDVa2kP0NvQgIJsq3wzRqvaiuncRWpkyFk1fTakiRGD32xnY\nt+juuAaIBlU8eswKyANFqhcLAvFHmT3rA1848M4/YM19djvlL/PR9T53tPNHU+el\nS9MlsG3o6Zsj8YaUJtCI8RgEuJoROLHUb/V9a3oMQ7CfuIoSvF3VEz3/dRT09RW6\n0wQX7yhka9WlKuayWLWmTcB9lAIX6neBk+qKc8VSEsO7mHkzB8mRgVcS2uYZl1eA\nD8/jTT+SlpeFNDZk0Oh35GNFoOxh9qjRw3NGxu7jJCVBehDODzasOv4xDxKAhONa\njQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
},
|
||||
"endpoints": {
|
||||
"sharedInbox": "https://masto.asonix.dog/inbox"
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
const RELAY_ASONIX_DOG_ACTOR: &'static str = r#"
|
||||
{
|
||||
"publicKey": {
|
||||
"id": "https://relay.asonix.dog/actor#main-key",
|
||||
"owner": "https://relay.asonix.dog/actor",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw8UKc+men4z4Ex3Nx1be\ngti6DXAiRvCMp5pgOld7jjvSv2OqfdT71c1ZmAhNh6fMCSgusrGA+JGysExGODb4\nQw5NFa1jDxQkufMawEPQkvmB24vFi2C0PjVeSzu0oHZWfw4zlt+Lg1pOtlFOf41U\npFg282T+kDqIy968v8bH1wY3XzQ/jyb3jZD58jsXKtNUUzHs2K2IqJSuhuCJSkGd\nMkrCCXgLZ8Tw+dgYF+jLv1YNCPS2zBgM0v3IujNuszyzCobWUMfsGFfIvMORyvNP\nr5oxM1r3JV3CxMOOmskeYyh5DrtVSJ/ZVNabmj+Cy1WMadO8Rpd7e5j12pbY6ORu\nNu6mLOQ0qxX101bKYzVxVCP8KDN3j7RSkPB/K4gKPXZcHAcdRIjnlthDRicwZqW6\n1OsdP45tmfHxSIVNPmn1Wc4xWMsGU+EubmsxfXxkGNSaNpD1sjBf9vcf/c7q3mj8\nqcp74dOAmmO/O1AQnoQKCVqZaT26Db6YRn1P/lBHefdCetapmsyTkJ39Htxp3aAK\n+FHTxxX/hM6PlQwvteIvFHnaYBQgvp37L+zdXh87E2MQN4azj7LoUfmJG+7UVatn\nS61VCwLvKbDvlS8zg4RZo2hCBXG4rnyW8DpD2YdCMTKXvSySEzUF64Jomsxq7vqC\nNLWSay9Y9rhvQswxPe2XYEUCAwEAAQ==\n-----END PUBLIC KEY-----\n"
|
||||
},
|
||||
"inbox": "https://relay.asonix.dog/inbox",
|
||||
"outbox": "https://relay.asonix.dog/outbox",
|
||||
"following": "https://relay.asonix.dog/following",
|
||||
"followers": "https://relay.asonix.dog/followers",
|
||||
"preferredUsername": "relay",
|
||||
"endpoints": {
|
||||
"sharedInbox": "https://relay.asonix.dog/inbox"
|
||||
},
|
||||
"summary": "AodeRelay bot",
|
||||
"url": "https://relay.asonix.dog/actor",
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1"
|
||||
],
|
||||
"id": "https://relay.asonix.dog/actor",
|
||||
"type": "Application",
|
||||
"name": "AodeRelay"
|
||||
}
|
||||
"#;
|
||||
|
||||
const MASTO_ASONIX_DOG_ASONIX_ACTOR: &'static str = r#"
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"featuredTags": {
|
||||
"@id": "toot:featuredTags",
|
||||
"@type": "@id"
|
||||
},
|
||||
"alsoKnownAs": {
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
},
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value",
|
||||
"discoverable": "toot:discoverable",
|
||||
"Device": "toot:Device",
|
||||
"Ed25519Signature": "toot:Ed25519Signature",
|
||||
"Ed25519Key": "toot:Ed25519Key",
|
||||
"Curve25519Key": "toot:Curve25519Key",
|
||||
"EncryptedMessage": "toot:EncryptedMessage",
|
||||
"publicKeyBase64": "toot:publicKeyBase64",
|
||||
"deviceId": "toot:deviceId",
|
||||
"claim": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:claim"
|
||||
},
|
||||
"fingerprintKey": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:fingerprintKey"
|
||||
},
|
||||
"identityKey": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:identityKey"
|
||||
},
|
||||
"devices": {
|
||||
"@type": "@id",
|
||||
"@id": "toot:devices"
|
||||
},
|
||||
"messageFranking": "toot:messageFranking",
|
||||
"messageType": "toot:messageType",
|
||||
"cipherText": "toot:cipherText",
|
||||
"suspended": "toot:suspended",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "https://masto.asonix.dog/users/asonix",
|
||||
"type": "Person",
|
||||
"following": "https://masto.asonix.dog/users/asonix/following",
|
||||
"followers": "https://masto.asonix.dog/users/asonix/followers",
|
||||
"inbox": "https://masto.asonix.dog/users/asonix/inbox",
|
||||
"outbox": "https://masto.asonix.dog/users/asonix/outbox",
|
||||
"featured": "https://masto.asonix.dog/users/asonix/collections/featured",
|
||||
"featuredTags": "https://masto.asonix.dog/users/asonix/collections/tags",
|
||||
"preferredUsername": "asonix",
|
||||
"name": "Liom on Mane -> ANE",
|
||||
"summary": "<p>26, local liom, friend, rust (lang) stan, bi </p><p>icon by <span class=\"h-card\"><a href=\"https://furaffinity.net/user/lalupine\" target=\"blank\" rel=\"noopener noreferrer\" class=\"u-url mention\">@<span>lalupine@furaffinity.net</span></a></span><br />header by <span class=\"h-card\"><a href=\"https://furaffinity.net/user/tronixx\" target=\"blank\" rel=\"noopener noreferrer\" class=\"u-url mention\">@<span>tronixx@furaffinity.net</span></a></span></p><p>Testimonials:</p><p>Stand: LIONS<br />Stand User: AODE<br />- Keris (not on here)</p>",
|
||||
"url": "https://masto.asonix.dog/@asonix",
|
||||
"manuallyApprovesFollowers": true,
|
||||
"discoverable": true,
|
||||
"published": "2021-02-09T00:00:00Z",
|
||||
"devices": "https://masto.asonix.dog/users/asonix/collections/devices",
|
||||
"publicKey": {
|
||||
"id": "https://masto.asonix.dog/users/asonix#main-key",
|
||||
"owner": "https://masto.asonix.dog/users/asonix",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm+YpyXb3bUp5EyryHqRA\npKSvl4RamJh6CLlngYYPFU8lcx92oQR8nFlqOwInAczGPoCoIfojQpZfqV4hFq1I\nlETy6jHHeoO/YkUsH2dYtz6gjEqiZFCFpoWuGxUQO3lwfmPYpxl2/GFEDR4MrUNp\n9fPn9jHUlKydiDkFQqluajqSJgv0BCwnUGBanTEfeQKahnc3OqPTi4xNbsd2cbAW\nZtJ6VYepphQCRHElvkzefe1ra5qm5i8YBdan3Z3oo5wN1vo3u41tqjVGhDptKZkv\nwBevdL0tedoLp5Lj1l/HLTSBP0D0ZT/HUFuo6Zq27PCq/4ZgJaZkMi7YCVVtpjim\nmQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
},
|
||||
"tag": [],
|
||||
"attachment": [
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "pronouns",
|
||||
"value": "he/they"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "software",
|
||||
"value": "bad"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "gitea",
|
||||
"value": "<a href=\"https://git.asonix.dog\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"><span class=\"invisible\">https://</span><span class=\"\">git.asonix.dog</span><span class=\"invisible\"></span></a>"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "join my",
|
||||
"value": "relay"
|
||||
}
|
||||
],
|
||||
"endpoints": {
|
||||
"sharedInbox": "https://masto.asonix.dog/inbox"
|
||||
},
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://masto.asonix.dog/system/accounts/avatars/000/000/001/original/00852df0e6fee7e0.png"
|
||||
},
|
||||
"image": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://masto.asonix.dog/system/accounts/headers/000/000/001/original/8122ce3e5a745385.png"
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
|
286
src/lib.rs
286
src/lib.rs
|
@ -3,7 +3,7 @@ use indexmap::IndexMap;
|
|||
use itertools::Itertools;
|
||||
use json_ld::{rdf::Value, RdfQuads, ValidId as Subject};
|
||||
use locspan::{Location, Span};
|
||||
use rdf_types::{generator::Blank, BlankIdVocabularyMut, Vocabulary, VocabularyMut};
|
||||
use rdf_types::{generator::Blank, BlankIdVocabularyMut, RdfDisplay, Vocabulary, VocabularyMut};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
|
@ -11,26 +11,11 @@ use std::{
|
|||
};
|
||||
|
||||
mod input_dataset;
|
||||
#[cfg(feature = "rustcrypto")]
|
||||
mod sha2_impls;
|
||||
|
||||
use input_dataset::{InputDataset, NormalizingQuad, Position, QuadSubject, QuadValue};
|
||||
|
||||
#[cfg(feature = "rustcrypto")]
|
||||
mod sha2_impls {
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
impl super::Sha256 for Sha256 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Digest::update(self, bytes)
|
||||
}
|
||||
|
||||
fn finalize_hex_and_reset(&mut self) -> crate::HexHash {
|
||||
let output = self.finalize_reset();
|
||||
|
||||
crate::HexHash(hex::encode(&output))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Security;
|
||||
|
||||
|
@ -50,18 +35,22 @@ where
|
|||
pub quads: Vec<NormalizingQuad<N>>,
|
||||
}
|
||||
|
||||
pub struct Issuer<B> {
|
||||
// Identifier Prefix and Identifier Counter
|
||||
blank_node_generator: Blank,
|
||||
// Issued Identifier List
|
||||
issued_identifier_list: IndexMap<B, B>,
|
||||
}
|
||||
|
||||
pub struct CanonicalizationState<'a, N, S>
|
||||
where
|
||||
N: Vocabulary,
|
||||
{
|
||||
sha256: S,
|
||||
|
||||
// Identifier Prefix and Identifier Counter
|
||||
blank_node_generator: Blank,
|
||||
vocabulary: &'a mut N,
|
||||
|
||||
// Issued Identifier List
|
||||
issued_identifier_list: IndexMap<N::BlankId, N::BlankId>,
|
||||
canonical_issuer: Issuer<N::BlankId>,
|
||||
|
||||
blank_node_to_quads: HashMap<N::BlankId, HashSet<Position>>,
|
||||
hash_to_blank_nodes: BTreeMap<HexHash, HashSet<N::BlankId>>,
|
||||
|
@ -88,6 +77,66 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
impl<B> Issuer<B> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
blank_node_generator: canonicalization_node_generator(),
|
||||
issued_identifier_list: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_with_prefix(prefix: &str) -> Self {
|
||||
Self {
|
||||
blank_node_generator: make_issuer(prefix),
|
||||
issued_identifier_list: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = (&B, &B)> {
|
||||
self.issued_identifier_list.iter()
|
||||
}
|
||||
|
||||
fn get(&self, identifier: &B) -> Option<&B>
|
||||
where
|
||||
B: Eq + Hash,
|
||||
{
|
||||
self.issued_identifier_list.get(identifier)
|
||||
}
|
||||
|
||||
fn contains(&self, identifier: &B) -> bool
|
||||
where
|
||||
B: Eq + Hash,
|
||||
{
|
||||
self.issued_identifier_list.contains_key(identifier)
|
||||
}
|
||||
|
||||
fn issue_identifier<N>(&mut self, identifier: B, vocabulary: &mut N) -> N::BlankId
|
||||
where
|
||||
N: Vocabulary<BlankId = B> + BlankIdVocabularyMut,
|
||||
B: Eq + Hash + Clone,
|
||||
{
|
||||
use rdf_types::Generator;
|
||||
|
||||
// step 1
|
||||
if let Some(blank) = self.get(&identifier) {
|
||||
return blank.clone();
|
||||
}
|
||||
|
||||
// step 2 and 4
|
||||
let blank = match self.blank_node_generator.next(vocabulary) {
|
||||
Subject::Blank(blank) => blank,
|
||||
Subject::Iri(_) => unreachable!("Blank ID generators should only generate blank IDs"),
|
||||
};
|
||||
|
||||
// step 3
|
||||
self.issued_identifier_list
|
||||
.insert(identifier, blank.clone());
|
||||
|
||||
// step 5
|
||||
blank
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, N, S> CanonicalizationState<'a, N, S>
|
||||
where
|
||||
N: Vocabulary,
|
||||
|
@ -99,9 +148,8 @@ where
|
|||
{
|
||||
Self {
|
||||
sha256: S::default(),
|
||||
blank_node_generator: canonicalization_node_generator(),
|
||||
vocabulary,
|
||||
issued_identifier_list: Default::default(),
|
||||
canonical_issuer: Issuer::default(),
|
||||
blank_node_to_quads: Default::default(),
|
||||
hash_to_blank_nodes: Default::default(),
|
||||
}
|
||||
|
@ -228,12 +276,8 @@ where
|
|||
non_normalized_identifiers.remove(&identifier);
|
||||
|
||||
// step 5.4.2
|
||||
issue_identifier_algorithm(
|
||||
identifier,
|
||||
&mut self.blank_node_generator,
|
||||
self.vocabulary,
|
||||
&mut self.issued_identifier_list,
|
||||
);
|
||||
self.canonical_issuer
|
||||
.issue_identifier(identifier, self.vocabulary);
|
||||
}
|
||||
|
||||
// step 5.4.5
|
||||
|
@ -266,7 +310,7 @@ where
|
|||
// step 6.2
|
||||
for identifier in identifier_list {
|
||||
// step 6.2.1
|
||||
if self.issued_identifier_list.contains_key(&identifier) {
|
||||
if self.canonical_issuer.contains(&identifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -275,18 +319,12 @@ where
|
|||
}
|
||||
|
||||
// step 6.2.2
|
||||
let mut temporary_issuer = make_issuer("b");
|
||||
let mut temporary_issuer = Issuer::new_with_prefix("b");
|
||||
|
||||
// step 6.2.3
|
||||
let mut issued_identifier_list = Default::default();
|
||||
let mut temporary_vocabulary = N::default();
|
||||
|
||||
issue_identifier_algorithm(
|
||||
identifier.clone(),
|
||||
&mut temporary_issuer,
|
||||
&mut temporary_vocabulary,
|
||||
&mut issued_identifier_list,
|
||||
);
|
||||
temporary_issuer.issue_identifier(identifier.clone(), &mut temporary_vocabulary);
|
||||
|
||||
// step 6.2.4
|
||||
let hash = hash_n_degree_quads(
|
||||
|
@ -294,26 +332,21 @@ where
|
|||
&mut temporary_vocabulary,
|
||||
&self.vocabulary,
|
||||
&mut temporary_issuer,
|
||||
&mut issued_identifier_list,
|
||||
&self.issued_identifier_list,
|
||||
&self.canonical_issuer,
|
||||
identifier,
|
||||
input_dataset,
|
||||
&mut self.sha256,
|
||||
);
|
||||
|
||||
hash_path_list.push((hash, issued_identifier_list));
|
||||
hash_path_list.push((hash, temporary_issuer));
|
||||
}
|
||||
|
||||
// step 6.3
|
||||
for (_, issued_identifier_list) in hash_path_list {
|
||||
for (_, temporary_issuer) in hash_path_list {
|
||||
// step 6.3.1
|
||||
for (existing_identifier, _) in issued_identifier_list {
|
||||
issue_identifier_algorithm(
|
||||
existing_identifier,
|
||||
&mut self.blank_node_generator,
|
||||
self.vocabulary,
|
||||
&mut self.issued_identifier_list,
|
||||
);
|
||||
for (existing_identifier, _) in temporary_issuer.iter() {
|
||||
self.canonical_issuer
|
||||
.issue_identifier(existing_identifier.clone(), self.vocabulary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,9 +401,9 @@ where
|
|||
{
|
||||
match subject {
|
||||
Subject::Iri(iri) => Some(Subject::Iri(iri.clone())),
|
||||
Subject::Blank(blank) => Some(Subject::Blank(
|
||||
self.issued_identifier_list.get(blank)?.clone(),
|
||||
)),
|
||||
Subject::Blank(blank) => {
|
||||
Some(Subject::Blank(self.canonical_issuer.get(blank)?.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -379,9 +412,8 @@ fn hash_n_degree_quads<N, S>(
|
|||
blank_node_to_quads: &HashMap<N::BlankId, HashSet<Position>>,
|
||||
vocabulary: &mut N,
|
||||
canon_vocabulary: &N,
|
||||
issuer: &mut Blank,
|
||||
issued_identifier_list: &mut IndexMap<N::BlankId, N::BlankId>,
|
||||
canon_issued_identifier_list: &IndexMap<N::BlankId, N::BlankId>,
|
||||
issuer: &mut Issuer<N::BlankId>,
|
||||
canon_issuer: &Issuer<N::BlankId>,
|
||||
identifier: N::BlankId,
|
||||
input_dataset: &InputDataset<N>,
|
||||
sha256: &mut S,
|
||||
|
@ -416,9 +448,9 @@ where
|
|||
// step 3.1.1
|
||||
let hash = hash_related_blank_node(
|
||||
blank_node_to_quads,
|
||||
canon_issued_identifier_list,
|
||||
canon_issuer,
|
||||
canon_vocabulary,
|
||||
issued_identifier_list,
|
||||
issuer,
|
||||
vocabulary,
|
||||
related,
|
||||
quad,
|
||||
|
@ -451,12 +483,10 @@ where
|
|||
|
||||
// step 5.3
|
||||
let mut chosen_issuer = Default::default();
|
||||
let mut chosen_issued_identifier_list = Default::default();
|
||||
|
||||
'permute: for permutation in permute(blank_node_list) {
|
||||
// step 5.4.1
|
||||
let mut issuer_copy = Blank::new_full(issuer.prefix().to_string(), issuer.count());
|
||||
let mut issued_identifier_list_copy = issued_identifier_list.clone();
|
||||
let mut issuer_copy = issuer.clone();
|
||||
// step 5.4.2
|
||||
let mut path = String::new();
|
||||
// step 5.4.3
|
||||
|
@ -464,7 +494,7 @@ where
|
|||
|
||||
// step 5.4.4
|
||||
for related in permutation {
|
||||
if let Some(blank) = canon_issued_identifier_list.get(&related) {
|
||||
if let Some(blank) = canon_issuer.get(&related) {
|
||||
// step 5.4.4.1
|
||||
if let Some(blank_id) = canon_vocabulary.blank_id(blank) {
|
||||
path += &blank_id.to_string();
|
||||
|
@ -476,12 +506,7 @@ where
|
|||
// step 5.4.4.2.1
|
||||
recursion_list.insert(related.clone());
|
||||
// step 5.4.4.2.2
|
||||
issue_identifier_algorithm(
|
||||
related,
|
||||
&mut issuer_copy,
|
||||
vocabulary,
|
||||
&mut issued_identifier_list_copy,
|
||||
);
|
||||
issuer_copy.issue_identifier(related, vocabulary);
|
||||
}
|
||||
|
||||
// step 5.4.4.3
|
||||
|
@ -499,19 +524,13 @@ where
|
|||
vocabulary,
|
||||
canon_vocabulary,
|
||||
&mut issuer_copy,
|
||||
&mut issued_identifier_list_copy,
|
||||
canon_issued_identifier_list,
|
||||
canon_issuer,
|
||||
related.clone(),
|
||||
input_dataset,
|
||||
sha256,
|
||||
);
|
||||
// step 5.4.5.2
|
||||
let new_blank = issue_identifier_algorithm(
|
||||
related,
|
||||
&mut issuer_copy,
|
||||
vocabulary,
|
||||
&mut issued_identifier_list_copy,
|
||||
);
|
||||
let new_blank = issuer_copy.issue_identifier(related, vocabulary);
|
||||
|
||||
if let Some(blank_id) = vocabulary.blank_id(&new_blank) {
|
||||
path += &blank_id.to_string();
|
||||
|
@ -536,7 +555,6 @@ where
|
|||
if chosen_path.is_empty() || path < chosen_path {
|
||||
chosen_path = path;
|
||||
chosen_issuer = issuer_copy;
|
||||
chosen_issued_identifier_list = issued_identifier_list_copy;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,7 +563,6 @@ where
|
|||
|
||||
// step 5.6
|
||||
std::mem::swap(issuer, &mut chosen_issuer);
|
||||
std::mem::swap(issued_identifier_list, &mut chosen_issued_identifier_list);
|
||||
}
|
||||
|
||||
// step 6
|
||||
|
@ -564,9 +581,9 @@ where
|
|||
|
||||
fn hash_related_blank_node<N, S>(
|
||||
blank_node_to_quads: &HashMap<N::BlankId, HashSet<Position>>,
|
||||
canon_issued_identifier_list: &IndexMap<N::BlankId, N::BlankId>,
|
||||
canon_issuer: &Issuer<N::BlankId>,
|
||||
canon_vocabulary: &N,
|
||||
issued_identifier_list: &IndexMap<N::BlankId, N::BlankId>,
|
||||
issuer: &Issuer<N::BlankId>,
|
||||
vocabulary: &N,
|
||||
related: &N::BlankId,
|
||||
quad: &NormalizingQuad<N>,
|
||||
|
@ -580,12 +597,12 @@ where
|
|||
S: Sha256,
|
||||
{
|
||||
// step 1
|
||||
let identifier = if let Some(blank_id) = canon_issued_identifier_list.get(related) {
|
||||
let identifier = if let Some(blank_id) = canon_issuer.get(related) {
|
||||
let blank = canon_vocabulary
|
||||
.blank_id(blank_id)
|
||||
.expect("No blank in vocabulary");
|
||||
blank.to_string()
|
||||
} else if let Some(blank_id) = issued_identifier_list.get(related) {
|
||||
} else if let Some(blank_id) = issuer.get(related) {
|
||||
let blank = vocabulary
|
||||
.blank_id(blank_id)
|
||||
.expect("No blank in vocabulary");
|
||||
|
@ -666,24 +683,24 @@ where
|
|||
N::BlankId: Clone + Eq,
|
||||
{
|
||||
let subject = serialize_subject(identifier, quad.subject(), vocabulary);
|
||||
let predicate = quad.predicate().with(vocabulary);
|
||||
let predicate = quad.predicate().with(vocabulary).rdf_display().to_string();
|
||||
let object = serialize_object(identifier, quad.object(), vocabulary);
|
||||
let graph = quad
|
||||
.graph()
|
||||
.map(|graph| serialize_subject(identifier, graph, vocabulary));
|
||||
|
||||
if let Some(graph) = graph {
|
||||
format!("{subject} {predicate} {object} {graph}")
|
||||
format!("{subject} {predicate} {object} {graph} .\n")
|
||||
} else {
|
||||
format!("{subject} {predicate} {object}")
|
||||
format!("{subject} {predicate} {object} .\n")
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_subject<N>(
|
||||
fn serialize_subject<'a, N>(
|
||||
identifier: &N::BlankId,
|
||||
subject: &QuadSubject<N>,
|
||||
vocabulary: &N,
|
||||
) -> Cow<'static, str>
|
||||
subject: &'a QuadSubject<N>,
|
||||
vocabulary: &'a N,
|
||||
) -> Cow<'a, str>
|
||||
where
|
||||
N: Vocabulary,
|
||||
N::BlankId: Eq,
|
||||
|
@ -693,25 +710,53 @@ where
|
|||
} else if subject.is_blank() {
|
||||
Cow::Borrowed("_:z")
|
||||
} else {
|
||||
Cow::Owned(subject.with(vocabulary).to_string())
|
||||
Cow::Owned(subject.with(vocabulary).rdf_display().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_object<N>(
|
||||
fn serialize_object<'a, N>(
|
||||
identifier: &N::BlankId,
|
||||
object: &QuadValue<N>,
|
||||
vocabulary: &N,
|
||||
) -> Cow<'static, str>
|
||||
object: &'a QuadValue<N>,
|
||||
vocabulary: &'a N,
|
||||
) -> Cow<'a, str>
|
||||
where
|
||||
N: Vocabulary,
|
||||
N::BlankId: Eq,
|
||||
{
|
||||
match object {
|
||||
Value::Literal(lit) => Cow::Owned(lit.with(vocabulary).to_string()),
|
||||
Value::Literal(lit) => Cow::Owned(lit.with(vocabulary).rdf_display().to_string()),
|
||||
Value::Reference(subject) => serialize_subject(identifier, subject, vocabulary),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn quad_to_string<N>(quad: &NormalizingQuad<N>, vocabulary: &N) -> String
|
||||
where
|
||||
N: Vocabulary,
|
||||
{
|
||||
let subject = quad.subject().with(vocabulary).rdf_display().to_string();
|
||||
let predicate = quad.predicate().with(vocabulary).rdf_display().to_string();
|
||||
let object = object_to_string(quad.object(), vocabulary);
|
||||
let graph = quad
|
||||
.graph()
|
||||
.map(|graph| graph.with(vocabulary).rdf_display().to_string());
|
||||
|
||||
if let Some(graph) = graph {
|
||||
format!("{subject} {predicate} {object} {graph} .\n")
|
||||
} else {
|
||||
format!("{subject} {predicate} {object} .\n")
|
||||
}
|
||||
}
|
||||
|
||||
fn object_to_string<'a, N>(object: &'a QuadValue<N>, vocabulary: &'a N) -> String
|
||||
where
|
||||
N: Vocabulary,
|
||||
{
|
||||
match object {
|
||||
Value::Literal(lit) => lit.with(vocabulary).rdf_display().to_string(),
|
||||
Value::Reference(subject) => subject.with(vocabulary).rdf_display().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_identifier<N>(identifier: &N::BlankId, subject: &QuadSubject<N>) -> bool
|
||||
where
|
||||
N: Vocabulary,
|
||||
|
@ -743,36 +788,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn issue_identifier_algorithm<N>(
|
||||
identifier: N::BlankId,
|
||||
generator: &mut Blank,
|
||||
vocabulary: &mut N,
|
||||
issued_identifier_list: &mut IndexMap<N::BlankId, N::BlankId>,
|
||||
) -> N::BlankId
|
||||
where
|
||||
N: VocabularyMut + BlankIdVocabularyMut,
|
||||
N::BlankId: Eq + Hash + Clone,
|
||||
{
|
||||
use rdf_types::Generator;
|
||||
|
||||
// step 1
|
||||
if let Some(blank) = issued_identifier_list.get(&identifier) {
|
||||
return blank.clone();
|
||||
}
|
||||
|
||||
// step 2 and 4
|
||||
let blank = match generator.next(vocabulary) {
|
||||
Subject::Blank(blank) => blank,
|
||||
Subject::Iri(_) => unreachable!("Blank ID generators should only generate blank IDs"),
|
||||
};
|
||||
|
||||
// step 3
|
||||
issued_identifier_list.insert(identifier, blank.clone());
|
||||
|
||||
// step 5
|
||||
blank
|
||||
}
|
||||
|
||||
fn canonicalization_node_generator() -> Blank {
|
||||
make_issuer("c14n")
|
||||
}
|
||||
|
@ -788,3 +803,24 @@ impl std::fmt::Display for Security {
|
|||
}
|
||||
|
||||
impl std::error::Error for Security {}
|
||||
|
||||
impl<B> Clone for Issuer<B>
|
||||
where
|
||||
B: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
blank_node_generator: Blank::new_full(
|
||||
self.blank_node_generator.prefix().to_string(),
|
||||
self.blank_node_generator.count(),
|
||||
),
|
||||
issued_identifier_list: self.issued_identifier_list.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Default for Issuer<B> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
14
src/sha2_impls.rs
Normal file
14
src/sha2_impls.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
impl super::Sha256 for Sha256 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Digest::update(self, bytes)
|
||||
}
|
||||
|
||||
fn finalize_hex_and_reset(&mut self) -> crate::HexHash {
|
||||
let output = self.finalize_reset();
|
||||
|
||||
crate::HexHash(hex::encode(&output))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue