use super::{Options, TypeLangValue};
use crate::{
context::inverse::{Inversible, LangSelection, Selection, TypeSelection},
object,
syntax::{Container, Term, Type},
Context, Error, ErrorCode, Id, Indexed, Nullable, Object, ProcessingMode, Value,
};
use generic_json::{JsonClone, JsonHash};
pub(crate) fn compact_iri<'a, J: JsonHash, T: 'a + Id, C: Context<T>>(
active_context: Inversible<T, &C>,
var: &Term<T>,
vocab: bool,
reverse: bool,
options: Options,
) -> Result<Option<String>, Error> {
compact_iri_full::<J, T, C, Object<J, T>>(active_context, var, None, vocab, reverse, options)
}
pub(crate) fn compact_iri_with<'a, J: JsonHash, T: 'a + Id, C: Context<T>, N: object::Any<J, T>>(
active_context: Inversible<T, &C>,
var: &Term<T>,
value: &Indexed<N>,
vocab: bool,
reverse: bool,
options: Options,
) -> Result<Option<String>, Error> {
compact_iri_full(active_context, var, Some(value), vocab, reverse, options)
}
pub(crate) fn compact_iri_full<'a, J: JsonHash, T: 'a + Id, C: Context<T>, N: object::Any<J, T>>(
active_context: Inversible<T, &C>,
var: &Term<T>,
value: Option<&Indexed<N>>,
vocab: bool,
reverse: bool,
options: Options,
) -> Result<Option<String>, Error> {
if var.is_null() {
return Ok(None);
}
if vocab {
if let Some(entry) = active_context.inverse().get(var) {
let mut containers = Vec::new();
let mut type_lang_value = None;
if let Some(value) = value {
if value.index().is_some() && !value.is_graph() {
containers.push(Container::Index);
containers.push(Container::IndexSet);
}
}
let mut has_index = false;
let mut is_simple_value = false;
if reverse {
type_lang_value = Some(TypeLangValue::Type(TypeSelection::Reverse));
containers.push(Container::Set);
} else {
let value_ref = value.map(|v| {
has_index = v.index().is_some();
v.inner().as_ref()
});
match value_ref {
Some(object::Ref::List(list)) => {
if !has_index {
containers.push(Container::List);
}
let mut common_type = None;
let mut common_lang_dir = None;
if list.is_empty() {
common_lang_dir = Some(Nullable::Some((
active_context.default_language(),
active_context.default_base_direction(),
)))
} else {
for item in list {
let mut item_type = None;
let mut item_lang_dir = None;
let mut is_value = false;
match item.inner() {
Object::Value(value) => {
is_value = true;
match value {
Value::LangString(lang_str) => {
item_lang_dir = Some(Nullable::Some((
lang_str.language(),
lang_str.direction(),
)))
}
Value::Literal(_, Some(ty)) => {
item_type = Some(Type::Ref(ty.clone()))
}
Value::Literal(_, None) => {
item_lang_dir = Some(Nullable::Null)
}
Value::Json(_) => item_type = Some(Type::Json),
}
}
_ => item_type = Some(Type::Id),
}
if common_lang_dir.is_none() {
common_lang_dir = item_lang_dir
} else if is_value && common_lang_dir != item_lang_dir {
common_lang_dir = Some(Nullable::Some((None, None)))
}
if common_type.is_none() {
common_type = Some(item_type)
} else if *common_type.as_ref().unwrap() != item_type {
common_type = Some(None)
}
if common_lang_dir == Some(Nullable::Some((None, None)))
&& common_type == Some(None)
{
break;
}
}
}
if common_lang_dir.is_none() {
common_lang_dir = Some(Nullable::Some((None, None)))
}
let common_lang_dir = common_lang_dir.unwrap();
if common_type.is_none() {
common_type = Some(None)
}
let common_type = common_type.unwrap();
if let Some(common_type) = common_type {
type_lang_value =
Some(TypeLangValue::Type(TypeSelection::Type(common_type)))
} else {
type_lang_value =
Some(TypeLangValue::Lang(LangSelection::Lang(common_lang_dir)))
}
}
Some(object::Ref::Node(node)) if node.is_graph() => {
if has_index {
containers.push(Container::GraphIndex);
containers.push(Container::GraphIndexSet);
}
if node.id().is_some() {
containers.push(Container::GraphId);
containers.push(Container::GraphIdSet);
}
containers.push(Container::Graph);
containers.push(Container::GraphSet);
containers.push(Container::Set);
if !has_index {
containers.push(Container::GraphIndex);
containers.push(Container::GraphIndexSet);
}
if node.id().is_none() {
containers.push(Container::GraphId);
containers.push(Container::GraphIdSet);
}
containers.push(Container::Index);
containers.push(Container::IndexSet);
type_lang_value = Some(TypeLangValue::Type(TypeSelection::Type(Type::Id)))
}
Some(object::Ref::Value(v)) => {
if (v.direction().is_some() || v.language().is_some()) && !has_index {
type_lang_value = Some(TypeLangValue::Lang(LangSelection::Lang(
Nullable::Some((v.language(), v.direction())),
)));
containers.push(Container::Language);
containers.push(Container::LanguageSet)
} else if let Some(ty) = v.typ() {
type_lang_value = Some(TypeLangValue::Type(TypeSelection::Type(
ty.map(|ty| (*ty).clone()),
)))
} else {
is_simple_value =
v.direction().is_none() && v.language().is_none() && !has_index
}
containers.push(Container::Set)
}
_ => {
type_lang_value = Some(TypeLangValue::Type(TypeSelection::Type(Type::Id)));
containers.push(Container::Id);
containers.push(Container::IdSet);
containers.push(Container::Type);
containers.push(Container::SetType);
containers.push(Container::Set)
}
}
}
containers.push(Container::None);
if options.processing_mode != ProcessingMode::JsonLd1_0 && !has_index {
containers.push(Container::Index);
containers.push(Container::IndexSet)
}
if options.processing_mode != ProcessingMode::JsonLd1_0 && is_simple_value {
containers.push(Container::Language);
containers.push(Container::LanguageSet)
}
let mut is_empty_list = false;
if let Some(value) = value {
if let object::Ref::List(list) = value.inner().as_ref() {
if list.is_empty() {
is_empty_list = true;
}
}
}
let selection = if is_empty_list {
Selection::Any
} else {
match type_lang_value {
Some(TypeLangValue::Type(type_value)) => {
let mut selection: Vec<TypeSelection<T>> = Vec::new();
if type_value == TypeSelection::Reverse {
selection.push(TypeSelection::Reverse);
}
let mut has_id_type = false;
if let Some(value) = value {
if let Some(id) = value.id() {
if type_value == TypeSelection::Type(Type::Id)
|| type_value == TypeSelection::Reverse
{
has_id_type = true;
let mut vocab = false;
let compacted_iri = compact_iri::<J, _, _>(
active_context.clone(),
&id.clone().into_term(),
true,
false,
options,
)?;
if let Some(def) =
active_context.get(compacted_iri.as_ref().unwrap())
{
if let Some(iri_mapping) = &def.value {
vocab = iri_mapping == id;
}
}
if vocab {
selection.push(TypeSelection::Type(Type::Vocab));
selection.push(TypeSelection::Type(Type::Id));
} else {
selection.push(TypeSelection::Type(Type::Id));
selection.push(TypeSelection::Type(Type::Vocab));
}
selection.push(TypeSelection::Type(Type::None));
}
}
}
if !has_id_type {
selection.push(type_value);
selection.push(TypeSelection::Type(Type::None));
}
selection.push(TypeSelection::Any);
Selection::Type(selection)
}
Some(TypeLangValue::Lang(lang_value)) => {
let mut selection = vec![
lang_value,
LangSelection::Lang(Nullable::Some((None, None))),
LangSelection::Any,
];
if let LangSelection::Lang(Nullable::Some((Some(_), Some(dir)))) =
lang_value
{
selection.push(LangSelection::Lang(Nullable::Some((None, Some(dir)))));
}
Selection::Lang(selection)
}
None => Selection::Lang(vec![
LangSelection::Lang(Nullable::Null),
LangSelection::Lang(Nullable::Some((None, None))),
LangSelection::Any,
]),
}
};
if let Some(term) = entry.select(&containers, &selection) {
return Ok(Some(term.into()));
}
}
if let Some(vocab_mapping) = active_context.vocabulary() {
if let Some(suffix) = var.as_str().strip_prefix(vocab_mapping.as_str()) {
if !suffix.is_empty() && active_context.get(suffix).is_none() {
return Ok(Some(suffix.into()));
}
}
}
}
let mut compact_iri = String::new();
for (key, definition) in active_context.definitions() {
match definition.value.as_ref() {
Some(iri_mapping) if definition.prefix => {
if let Some(suffix) = var.as_str().strip_prefix(iri_mapping.as_str()) {
if !suffix.is_empty() {
let candidate = key.clone() + ":" + suffix;
let candidate_def = active_context.get(&candidate);
if (compact_iri.is_empty()
|| (candidate.len() <= compact_iri.len() && candidate < compact_iri))
&& (candidate_def.is_none()
|| (candidate_def.is_some()
&& candidate_def
.and_then(|def| def.value.as_ref())
.map_or(false, |v| v.as_str() == var.as_str())
&& value.is_none()))
{
compact_iri = candidate
}
}
}
}
_ => (),
}
}
if !compact_iri.is_empty() {
return Ok(Some(compact_iri.as_str().into()));
}
if let Some(iri) = var.as_iri() {
if active_context.contains(iri.scheme().as_str()) {
return Err(ErrorCode::IriConfusedWithPrefix.into());
}
}
if !vocab {
if let Some(base_iri) = active_context.base_iri() {
if let Some(iri) = var.as_iri() {
return Ok(Some(iri.relative_to(base_iri).as_str().into()));
}
}
}
Ok(Some(var.as_str().into()))
}