json-ld 0.22.0

JSON-LD implementation
Documentation
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)))
		// self.imported()
		// 	.and_then(|i| i.get(key))
		// 	.or_else(|| self.base.get(key))
	}

	/// Returns `true` if `key`'s binding comes from the imported context
	/// rather than from `base` (i.e. it would be shadowed by a `base`
	/// binding of the same key, if one existed).
	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,
		}
	}
}

// #[derive(Default)]
// pub struct StaticMergedBindings {
// 	base_offset: usize,
// 	imported_offset: usize
// }

// impl StaticMergedBindings {
// 	pub fn next<'a>(
// 		&mut self,
// 		context: &Merged<'a>
// 	) -> Option<BindingRef<'a>> {
// 		match context.base.bindings.get_entry(self.base_offset) {
// 			Some(entry) => {
// 				self.base_offset += 1;
// 				Some(entry)
// 			},
// 			None => {
// 				match context.imported() {
// 					Some(imported) => {
// 						while let Some(entry) = imported.bindings.get_entry(self.imported_offset) {
// 							self.imported_offset += 1;
// 							if context.base.get_binding(entry.0).is_none() {
// 								return Some(entry)
// 							}
// 						}

// 						None
// 					},
// 					None => 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(),
		}
	}
}