json-ld 0.22.0

JSON-LD implementation
Documentation
use std::sync::Arc;

use json_syntax::{JsonValue, tracing::JsonErrorAt};
use rdf_syntax::Iri;

use super::{
	JsonLdCompactResult, JsonLdCompareResult, JsonLdExpandResult, JsonLdFlattenResult,
	JsonLdOptions, JsonLdProcessor,
};
use crate::{
	JsonLdBacktraceBuilder, JsonLdDocument, JsonLdLocatedError, JsonLdSourceFileRef,
	RemoteJsonLdContext,
	algorithms::{AsyncJsonLdEnvironment, JsonLdCompact, JsonLdExpand},
	context::RawJsonLdProcessedContext,
	syntax::JsonLdCompare,
};

impl JsonLdProcessor for JsonLdDocument {
	async fn async_compare_with(
		&self,
		other: &Self,
		env: impl AsyncJsonLdEnvironment,
		options: JsonLdOptions,
	) -> JsonLdCompareResult {
		if self.document.compare_json_ld(&other.document) {
			return Ok(true);
		}

		let a = JsonLdProcessor::async_expand_with(self, env.as_ref(), options.clone()).await?;
		let b = JsonLdProcessor::async_expand_with(other, env, options).await?;

		Ok(a == b)
	}

	async fn async_expand_with(
		&self,
		env: impl AsyncJsonLdEnvironment,
		mut options: JsonLdOptions,
	) -> JsonLdExpandResult {
		// Initialize the active context.
		let mut active_context =
			RawJsonLdProcessedContext::new(options.base.clone().or_else(|| self.url.clone()));

		// Process expand context if provided.
		if let Some(expand_context) = options.expand_context.take() {
			let context_document = expand_context
				.load(env.loader())
				.await
				.map_err(Into::into)
				.json_err_at(JsonLdBacktraceBuilder::Root)?;

			active_context = context_document
				.document
				.context
				.process_with(
					env.as_ref(),
					active_context.original_base_url(),
					&active_context,
					options.context_processing_options(),
					JsonLdBacktraceBuilder::new()
						.file(JsonLdSourceFileRef::original(context_document.url())),
				)
				.await?
				.into_raw();
		}

		// Process context URL from the loaded document, if any.
		if let Some(context_url) = self.context_url() {
			active_context = RemoteJsonLdContext::iri(context_url.to_owned())
				.load(env.loader())
				.await
				.map_err(Into::into)
				.json_err_at(JsonLdBacktraceBuilder::Root)?
				.document
				.context
				.process_with(
					env.as_ref(),
					Some(context_url),
					&active_context,
					options.context_processing_options(),
					JsonLdBacktraceBuilder::new()
						.file(JsonLdSourceFileRef::original(Some(context_url))),
				)
				.await?
				.into_raw()
		}

		// Expand the document.
		JsonLdExpand::expand_with(self, env, &active_context, options.expansion_options()).await
	}

	async fn async_compact_with(
		&self,
		context: RemoteJsonLdContext,
		env: impl AsyncJsonLdEnvironment,
		options: JsonLdOptions,
	) -> JsonLdCompactResult {
		let expanded_document = Arc::new(
			JsonLdProcessor::async_expand_with(self, env.as_ref(), options.clone().unordered())
				.await?,
		);

		compact_expanded(
			&expanded_document,
			self.url(),
			env,
			context,
			options,
			JsonLdBacktraceBuilder::new().file(JsonLdSourceFileRef::expanded(
				self.url(),
				&expanded_document,
			)),
		)
		.await
	}

	async fn async_flatten_with(
		&self,
		context: Option<RemoteJsonLdContext>,
		env: impl AsyncJsonLdEnvironment,
		options: JsonLdOptions,
	) -> JsonLdFlattenResult {
		let expanded_input = Arc::new(
			JsonLdProcessor::async_expand_with(self, env.as_ref(), options.clone().unordered())
				.await?,
		);

		let generator = rdf_syntax::generator::BlankIdGenerator::new_with_prefix("b".to_string());
		let flattened_output = expanded_input
			.flatten(
				generator,
				options.ordered,
				JsonLdBacktraceBuilder::new()
					.file(JsonLdSourceFileRef::expanded(self.url(), &expanded_input)),
			)
			.map_err(|e| (*e).cast())?;

		match context {
			Some(context) => {
				let flattened_output = Arc::new(flattened_output);

				compact_expanded(
					&flattened_output,
					self.url(),
					env,
					context,
					options,
					JsonLdBacktraceBuilder::new().file(JsonLdSourceFileRef::flattened(
						self.url(),
						&flattened_output,
					)),
				)
				.await
			}
			None => Ok(json_syntax::to_value(flattened_output).unwrap()),
		}
	}
}

async fn compact_expanded(
	expanded_input: &impl JsonLdCompact,
	url: Option<&Iri>,
	env: impl AsyncJsonLdEnvironment,
	context: RemoteJsonLdContext,
	options: JsonLdOptions,
	location: JsonLdBacktraceBuilder<'_>,
) -> Result<JsonValue, JsonLdLocatedError> {
	let context_base = url.or(options.base.as_deref());

	let context = context
		.load(env.loader())
		.await
		.map_err(Into::into)
		.json_err_at(JsonLdBacktraceBuilder::Root)?;

	let mut active_context = context
		.document
		.context
		.process_with(
			env.as_ref(),
			context_base,
			&RawJsonLdProcessedContext::new(None),
			options.context_processing_options(),
			json_syntax::tracing::JsonBacktraceBuilder::new()
				.file(JsonLdSourceFileRef::original(context.url())),
		)
		.await?;

	match options.base.as_ref() {
		Some(base) => active_context.set_base_iri(Some(base.clone())),
		None => {
			if options.compact_to_relative && active_context.base_iri().is_none() {
				active_context.set_base_iri(url.map(ToOwned::to_owned));
			}
		}
	}

	expanded_input
		.compact_with(env, &active_context, options.compaction_options(), location)
		.await
}