use iref::IriBuf;
use super::BindingRef;
use super::Context;
use super::Key;
use crate::{Container, Direction, LenientLangTag, LenientLangTagBuf, Nullable, Term, Type};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
#[derive(Clone, PartialEq, Eq)]
pub enum TypeSelection<T = IriBuf> {
Reverse,
Any,
Type(Type<T>),
}
impl<T: fmt::Debug> fmt::Debug for TypeSelection<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TypeSelection::Reverse => write!(f, "Reverse"),
TypeSelection::Any => write!(f, "Any"),
TypeSelection::Type(ty) => write!(f, "Type({ty:?})"),
}
}
}
struct InverseType<T> {
reverse: Option<Key>,
any: Option<Key>,
map: HashMap<Type<T>, Key>,
}
impl<T> InverseType<T> {
fn select(&self, selection: TypeSelection<T>) -> Option<&Key>
where
T: Hash + Eq,
{
match selection {
TypeSelection::Reverse => self.reverse.as_ref(),
TypeSelection::Any => self.any.as_ref(),
TypeSelection::Type(ty) => self.map.get(&ty),
}
}
fn set_any(&mut self, term: &Key) {
if self.any.is_none() {
self.any = Some(term.clone())
}
}
fn set_none(&mut self, term: &Key)
where
T: Clone + Hash + Eq,
{
self.set(&Type::None, term)
}
fn set(&mut self, ty: &Type<T>, term: &Key)
where
T: Clone + Hash + Eq,
{
if !self.map.contains_key(ty) {
self.map.insert(ty.clone(), term.clone());
}
}
}
type LangDir = Nullable<(Option<LenientLangTagBuf>, Option<Direction>)>;
struct InverseLang {
any: Option<Key>,
map: HashMap<LangDir, Key>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LangSelection<'a> {
Any,
Lang(Nullable<(Option<&'a LenientLangTag>, Option<Direction>)>),
}
impl InverseLang {
fn select(&self, selection: LangSelection) -> Option<&Key> {
match selection {
LangSelection::Any => self.any.as_ref(),
LangSelection::Lang(lang_dir) => {
let lang_dir = lang_dir.map(|(l, d)| (l.map(|l| l.to_owned()), d));
self.map.get(&lang_dir)
}
}
}
fn set_any(&mut self, term: &Key) {
if self.any.is_none() {
self.any = Some(term.clone())
}
}
fn set_none(&mut self, term: &Key) {
self.set(Nullable::Some((None, None)), term)
}
fn set(
&mut self,
lang_dir: Nullable<(Option<&LenientLangTag>, Option<Direction>)>,
term: &Key,
) {
let lang_dir = lang_dir.map(|(l, d)| (l.map(|l| l.to_owned()), d));
self.map.entry(lang_dir).or_insert_with(|| term.clone());
}
}
struct InverseContainer<T> {
language: InverseLang,
typ: InverseType<T>,
any: Any,
}
struct Any {
none: Key,
}
impl<T> InverseContainer<T> {
pub fn new(term: &Key) -> InverseContainer<T> {
InverseContainer {
language: InverseLang {
any: None,
map: HashMap::new(),
},
typ: InverseType {
reverse: None,
any: None,
map: HashMap::new(),
},
any: Any { none: term.clone() },
}
}
}
pub struct InverseDefinition<T> {
map: HashMap<Container, InverseContainer<T>>,
}
impl<T> InverseDefinition<T> {
fn new() -> InverseDefinition<T> {
InverseDefinition {
map: HashMap::new(),
}
}
fn get(&self, container: &Container) -> Option<&InverseContainer<T>> {
self.map.get(container)
}
fn contains(&self, container: &Container) -> bool {
self.map.contains_key(container)
}
fn reference_mut<F: FnOnce() -> InverseContainer<T>>(
&mut self,
container: &Container,
insert: F,
) -> &mut InverseContainer<T> {
if !self.contains(container) {
self.map.insert(*container, insert());
}
self.map.get_mut(container).unwrap()
}
pub fn select(&self, containers: &[Container], selection: &Selection<T>) -> Option<&Key>
where
T: Clone + Hash + Eq,
{
for container in containers {
if let Some(type_lang_map) = self.get(container) {
match selection {
Selection::Any => return Some(&type_lang_map.any.none),
Selection::Type(preferred_values) => {
for item in preferred_values {
if let Some(term) = type_lang_map.typ.select(item.clone()) {
return Some(term);
}
}
}
Selection::Lang(preferred_values) => {
for item in preferred_values {
if let Some(term) = type_lang_map.language.select(*item) {
return Some(term);
}
}
}
}
}
}
None
}
}
pub struct InverseContext<T, B> {
map: HashMap<Term<T, B>, InverseDefinition<T>>,
}
pub enum Selection<'a, T> {
Any,
Type(Vec<TypeSelection<T>>),
Lang(Vec<LangSelection<'a>>),
}
impl<'a, T: fmt::Debug> fmt::Debug for Selection<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Selection::Any => write!(f, "Any"),
Selection::Type(s) => write!(f, "Type({s:?})"),
Selection::Lang(s) => write!(f, "Lang({s:?})"),
}
}
}
impl<T, B> InverseContext<T, B> {
pub fn new() -> Self {
InverseContext {
map: HashMap::new(),
}
}
}
impl<T: Hash + Eq, B: Hash + Eq> InverseContext<T, B> {
pub fn contains(&self, term: &Term<T, B>) -> bool {
self.map.contains_key(term)
}
pub fn insert(&mut self, term: Term<T, B>, value: InverseDefinition<T>) {
self.map.insert(term, value);
}
pub fn get(&self, term: &Term<T, B>) -> Option<&InverseDefinition<T>> {
self.map.get(term)
}
pub fn get_mut(&mut self, term: &Term<T, B>) -> Option<&mut InverseDefinition<T>> {
self.map.get_mut(term)
}
fn reference_mut<F: FnOnce() -> InverseDefinition<T>>(
&mut self,
term: &Term<T, B>,
insert: F,
) -> &mut InverseDefinition<T>
where
T: Clone,
B: Clone,
{
if !self.contains(term) {
self.insert(term.clone(), insert());
}
self.map.get_mut(term).unwrap()
}
pub fn select(
&self,
var: &Term<T, B>,
containers: &[Container],
selection: &Selection<T>,
) -> Option<&Key>
where
T: Clone,
{
match self.get(var) {
Some(container_map) => container_map.select(containers, selection),
None => None,
}
}
}
impl<T, B> Default for InverseContext<T, B> {
fn default() -> Self {
Self::new()
}
}
impl<'a, T: Clone + Hash + Eq, B: Clone + Hash + Eq> From<&'a Context<T, B>>
for InverseContext<T, B>
{
fn from(context: &'a Context<T, B>) -> Self {
let mut result = InverseContext::new();
let mut definitions: Vec<_> = context.definitions().iter().collect();
definitions.sort_by(|a, b| {
let a = a.term().as_str();
let b = b.term().as_str();
let ord = a.len().cmp(&b.len());
if ord == Ordering::Equal {
a.cmp(b)
} else {
ord
}
});
for binding in definitions {
if let BindingRef::Normal(term, term_definition) = binding {
if let Some(var) = term_definition.value.as_ref() {
let container = &term_definition.container;
let container_map = result.reference_mut(var, InverseDefinition::new);
let type_lang_map =
container_map.reference_mut(container, || InverseContainer::new(term));
let type_map = &mut type_lang_map.typ;
let lang_map = &mut type_lang_map.language;
if term_definition.reverse_property {
if type_map.reverse.is_none() {
type_map.reverse = Some(term.clone())
}
} else {
match &term_definition.typ {
Some(Type::None) => {
type_map.set_any(term);
lang_map.set_any(term);
}
Some(typ) => {
type_map.set(typ, term)
}
None => {
match (&term_definition.language, &term_definition.direction) {
(Some(language), Some(direction)) => {
match (language, direction) {
(
Nullable::Some(language),
Nullable::Some(direction),
) => lang_map.set(
Nullable::Some((
Some(language.as_lenient_lang_tag_ref()),
Some(*direction),
)),
term,
),
(Nullable::Some(language), Nullable::Null) => lang_map
.set(
Nullable::Some((
Some(language.as_lenient_lang_tag_ref()),
None,
)),
term,
),
(Nullable::Null, Nullable::Some(direction)) => lang_map
.set(
Nullable::Some((None, Some(*direction))),
term,
),
(Nullable::Null, Nullable::Null) => {
lang_map.set(Nullable::Null, term)
}
}
}
(Some(language), None) => {
match language {
Nullable::Some(language) => lang_map.set(
Nullable::Some((
Some(language.as_lenient_lang_tag_ref()),
None,
)),
term,
),
Nullable::Null => lang_map.set(Nullable::Null, term),
}
}
(None, Some(direction)) => {
match direction {
Nullable::Some(direction) => lang_map.set(
Nullable::Some((None, Some(*direction))),
term,
),
Nullable::Null => {
lang_map.set(Nullable::Some((None, None)), term)
}
}
}
(None, None) => {
lang_map.set(
Nullable::Some((
context.default_language(),
context.default_base_direction(),
)),
term,
);
lang_map.set_none(term);
type_map.set_none(term);
}
}
}
}
}
}
}
}
result
}
}