use crate::{add_value, compact_iri, compact_property, Error, Options};
use contextual::WithContext;
use json_ld_context_processing::{Options as ProcessingOptions, Process, ProcessingMode};
use json_ld_core::{Container, ContainerKind, Context, Id, Loader, Node, Term, Type};
use json_ld_syntax::Keyword;
use mown::Mown;
use rdf_types::VocabularyMut;
use std::hash::Hash;
fn optional_string(s: Option<String>) -> json_syntax::Value {
s.map(Into::into)
.unwrap_or_else(|| json_syntax::Value::Null)
}
#[allow(clippy::too_many_arguments)]
pub async fn compact_indexed_node_with<N, L>(
vocabulary: &mut N,
node: &Node<N::Iri, N::BlankId>,
index: Option<&str>,
mut active_context: &Context<N::Iri, N::BlankId>,
type_scoped_context: &Context<N::Iri, N::BlankId>,
active_property: Option<&str>,
loader: &L,
options: Options,
) -> Result<json_syntax::Value, Error>
where
N: VocabularyMut,
N::Iri: Clone + Hash + Eq,
N::BlankId: Clone + Hash + Eq,
L: Loader,
{
if !(node.is_empty() && node.id.is_some()) {
if let Some(previous_context) = active_context.previous_context() {
active_context = previous_context
}
}
let mut active_context = Mown::Borrowed(active_context);
if let Some(active_property) = active_property {
if let Some(active_property_definition) = type_scoped_context.get(active_property) {
if let Some(local_context) = active_property_definition.context() {
active_context = Mown::Owned(
local_context
.process_with(
vocabulary,
active_context.as_ref(),
loader,
active_property_definition.base_url().cloned(),
ProcessingOptions::from(options).with_override(),
)
.await?
.into_processed(),
)
}
}
}
let mut result = json_syntax::Object::default();
if !node.types().is_empty() {
let mut compacted_types = Vec::new();
for ty in node.types() {
let compacted_ty = compact_iri(
vocabulary,
type_scoped_context,
&ty.clone().into_term(),
true,
false,
options,
)?;
compacted_types.push(compacted_ty)
}
compacted_types.sort_by(|a, b| a.as_ref().unwrap().cmp(b.as_ref().unwrap()));
for term in &compacted_types {
if let Some(term_definition) = type_scoped_context.get(term.as_ref().unwrap().as_str())
{
if let Some(local_context) = term_definition.context() {
let processing_options = ProcessingOptions::from(options).without_propagation();
active_context = Mown::Owned(
local_context
.process_with(
vocabulary,
active_context.as_ref(),
loader,
term_definition.base_url().cloned(),
processing_options,
)
.await?
.into_processed(),
)
}
}
}
}
let mut expanded_entries: Vec<_> = node.properties().iter().collect();
if options.ordered {
let vocabulary: &N = vocabulary;
expanded_entries.sort_by(|(a, _), (b, _)| {
(**a)
.with(vocabulary)
.as_str()
.cmp((**b).with(vocabulary).as_str())
})
}
if let Some(id_entry) = &node.id {
let id = id_entry.clone().into_term();
if node.is_empty() {
let type_mapping = match active_property {
Some(prop) => match active_context.get(prop) {
Some(def) => def.typ(),
None => None,
},
None => None,
};
if type_mapping == Some(&Type::Id) {
let compacted_value = compact_iri(
vocabulary,
active_context.as_ref(),
&id,
false,
false,
options,
)?;
return Ok(optional_string(compacted_value));
}
if type_mapping == Some(&Type::Vocab) {
let compacted_value = compact_iri(
vocabulary,
active_context.as_ref(),
&id,
true,
false,
options,
)?;
return Ok(optional_string(compacted_value));
}
}
let compacted_value = compact_iri(
vocabulary,
active_context.as_ref(),
&id,
false,
false,
options,
)?;
let alias = compact_iri(
vocabulary,
active_context.as_ref(),
&Term::Keyword(Keyword::Id),
true,
false,
options,
)?;
if let Some(key) = alias {
result.insert(key.into(), optional_string(compacted_value));
}
}
compact_types(
vocabulary,
&mut result,
node.types.as_deref(),
active_context.as_ref(),
type_scoped_context,
options,
)?;
if let Some(reverse_properties) = node.reverse_properties_entry() {
if !reverse_properties.is_empty() {
let active_property = "@reverse";
if let Some(active_property_definition) = active_context.get(active_property) {
if let Some(local_context) = active_property_definition.context() {
active_context = Mown::Owned(
local_context
.process_with(
vocabulary,
active_context.as_ref(),
loader,
active_property_definition.base_url().cloned(),
ProcessingOptions::from(options).with_override(),
)
.await?
.into_processed(),
)
}
}
let mut reverse_result = json_syntax::Object::default();
for (expanded_property, expanded_value) in reverse_properties.iter() {
compact_property(
vocabulary,
&mut reverse_result,
expanded_property.clone().into(),
expanded_value.iter(),
active_context.as_ref(),
loader,
true,
options,
)
.await?;
}
let mut reverse_map = json_syntax::Object::default();
for (property, mapped_value) in reverse_result.iter_mut() {
let mut value = json_syntax::Value::Null;
std::mem::swap(&mut value, &mut *mapped_value);
if let Some(term_definition) = active_context.get(property.as_str()) {
if term_definition.reverse_property() {
let as_array = term_definition.container().contains(ContainerKind::Set)
|| !options.compact_arrays;
add_value(&mut result, property, value, as_array);
continue;
}
}
reverse_map.insert(property.clone(), value);
}
if !reverse_map.is_empty() {
let alias = compact_iri(
vocabulary,
active_context.as_ref(),
&Term::Keyword(Keyword::Reverse),
true,
false,
options,
)?;
result.insert(alias.unwrap().into(), reverse_map.into());
}
}
}
if let Some(index_entry) = index {
let mut index_container = false;
if let Some(active_property) = active_property {
if let Some(active_property_definition) = active_context.get(active_property) {
if active_property_definition
.container()
.contains(ContainerKind::Index)
{
index_container = true;
}
}
}
if !index_container {
let alias = compact_iri(
vocabulary,
active_context.as_ref(),
&Term::Keyword(Keyword::Index),
true,
false,
options,
)?;
result.insert(alias.unwrap().into(), index_entry.into());
}
}
if let Some(graph_entry) = node.graph_entry() {
compact_property(
vocabulary,
&mut result,
Term::Keyword(Keyword::Graph),
graph_entry.iter(),
active_context.as_ref(),
loader,
false,
options,
)
.await?
}
for (expanded_property, expanded_value) in expanded_entries {
compact_property(
vocabulary,
&mut result,
expanded_property.clone().into(),
expanded_value.iter(),
active_context.as_ref(),
loader,
false,
options,
)
.await?
}
if let Some(included_entry) = node.included_entry() {
compact_property(
vocabulary,
&mut result,
Term::Keyword(Keyword::Included),
included_entry.iter(),
active_context.as_ref(),
loader,
false,
options,
)
.await?
}
Ok(result.into())
}
fn compact_types<N>(
vocabulary: &mut N,
result: &mut json_syntax::Object,
types: Option<&[Id<N::Iri, N::BlankId>]>,
active_context: &Context<N::Iri, N::BlankId>,
type_scoped_context: &Context<N::Iri, N::BlankId>,
options: Options,
) -> Result<(), Error>
where
N: VocabularyMut,
N::Iri: Clone + Hash + Eq,
N::BlankId: Clone + Hash + Eq,
{
if let Some(types) = types {
if !types.is_empty() {
let compacted_value = if types.len() == 1 {
optional_string(compact_iri(
vocabulary,
type_scoped_context,
&types[0].clone().into_term(),
true,
false,
options,
)?)
} else {
let mut compacted_value = Vec::with_capacity(types.len());
for ty in types.iter() {
let ty = ty.clone().into_term();
let compacted_ty =
compact_iri(vocabulary, type_scoped_context, &ty, true, false, options)?;
compacted_value.push(optional_string(compacted_ty))
}
json_syntax::Value::Array(compacted_value.into_iter().collect())
};
let alias = compact_iri(
vocabulary,
active_context,
&Term::Keyword(Keyword::Type),
true,
false,
options,
)?
.unwrap();
let container_mapping = match active_context.get(alias.as_str()) {
Some(def) => def.container(),
None => Container::None,
};
let as_array = (options.processing_mode == ProcessingMode::JsonLd1_1
&& container_mapping.contains(ContainerKind::Set))
|| !options.compact_arrays;
add_value(result, &alias, compacted_value, as_array)
}
}
Ok(())
}