use langtag::LangTagBuf;
use rdf_syntax::IriRef;
use crate::{
JsonLdDirection, JsonLdLenient, JsonLdNullable,
syntax::{
JsonLdContext, JsonLdContextEntry,
context::{
JsonLdContextBindingsIter, JsonLdContextDefinition, JsonLdContextEntryValueRef,
JsonLdContextTerm, JsonLdContextType, JsonLdKeyOrKeyword, JsonLdTermDefinition,
JsonLdVocab,
},
},
};
pub struct Merged<'a> {
base: &'a JsonLdContextDefinition,
imported: Option<JsonLdContext>,
}
impl<'a> Merged<'a> {
pub fn new(base: &'a JsonLdContextDefinition, imported: Option<JsonLdContext>) -> Self {
Self { base, imported }
}
pub fn imported(&self) -> Option<&JsonLdContextDefinition> {
self.imported.as_ref().and_then(|imported| match imported {
JsonLdContext::One(JsonLdContextEntry::Definition(import_context)) => {
Some(import_context)
}
_ => None,
})
}
pub fn base(&self) -> Option<JsonLdNullable<&IriRef>> {
self.base
.base
.as_ref()
.or_else(|| self.imported().and_then(|i| i.base.as_ref()))
.map(JsonLdNullable::as_deref)
}
pub fn vocab(&self) -> Option<JsonLdNullable<&JsonLdVocab>> {
self.base
.vocab
.as_ref()
.or_else(|| self.imported().and_then(|i| i.vocab.as_ref()))
.map(JsonLdNullable::as_ref)
}
pub fn language(&self) -> Option<JsonLdNullable<&JsonLdLenient<LangTagBuf>>> {
self.base
.language
.as_ref()
.or_else(|| self.imported().and_then(|i| i.language.as_ref()))
.map(JsonLdNullable::as_ref)
}
pub fn direction(&self) -> Option<JsonLdNullable<JsonLdDirection>> {
self.base
.direction
.or_else(|| self.imported().and_then(|i| i.direction))
}
pub fn protected(&self) -> Option<bool> {
self.base
.protected
.or_else(|| self.imported().and_then(|i| i.protected))
}
pub fn type_(&self) -> Option<JsonLdContextType> {
self.base
.type_
.or_else(|| self.imported().and_then(|i| i.type_))
}
pub fn bindings(&self) -> MergedBindings<'_> {
MergedBindings {
base: self.base,
base_bindings: self.base.bindings.iter(),
imported_bindings: self.imported().map(|i| i.bindings.iter()),
}
}
pub fn get(&self, key: &JsonLdKeyOrKeyword) -> Option<JsonLdContextEntryValueRef<'_>> {
self.base
.get(key)
.or_else(|| self.imported().and_then(|i| i.get(key)))
}
pub fn key_is_imported(&self, key: &JsonLdContextTerm) -> bool {
self.imported.is_some() && self.base.get_binding(key).is_none()
}
}
impl<'a> From<&'a JsonLdContextDefinition> for Merged<'a> {
fn from(base: &'a JsonLdContextDefinition) -> Self {
Self {
base,
imported: None,
}
}
}
type BindingRef<'a> = (
&'a JsonLdContextTerm,
JsonLdNullable<&'a JsonLdTermDefinition>,
);
pub struct MergedBindings<'a> {
base: &'a JsonLdContextDefinition,
base_bindings: JsonLdContextBindingsIter<'a>,
imported_bindings: Option<JsonLdContextBindingsIter<'a>>,
}
impl<'a> Iterator for MergedBindings<'a> {
type Item = BindingRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.imported_bindings {
Some(imported_bindings) => {
for (key_ref, def) in imported_bindings {
let key = key_ref.to_owned();
if self.base.get_binding(&key).is_none() {
return Some((key_ref, def));
}
}
self.base_bindings.next()
}
None => self.base_bindings.next(),
}
}
}