use oxigraph::model::{Literal, TermRef}; use oxilangtag::LanguageTag; use spargebra::algebra::{Expression, Function, GraphPattern}; use spargebra::term::Variable; use std::sync::LazyLock; pub static ENGLISH_TAG: LazyLock> = LazyLock::new(|| LanguageTag::parse("en".to_string()).unwrap()); pub static ENGLISH_OR_UNTAGGED: LazyLock = LazyLock::new(|| LanguageCondition::ExactMatchOrUntagged(ENGLISH_TAG.clone())); #[derive(Clone, Debug)] pub enum LanguageCondition { ExactMatchOnly(LanguageTag), ExactMatchOrUntagged(LanguageTag), UntaggedOnly, AnyOrNone, } impl LanguageCondition { pub fn primary_matches_term<'a>(&self, term: impl Into>) -> bool { if let TermRef::Literal(literal) = term.into() { let tag = literal .language() .map(LanguageTag::parse_and_normalize) .and_then(Result::ok); match (tag, self) { (Some(language), LanguageCondition::ExactMatchOnly(expectation)) | (Some(language), LanguageCondition::ExactMatchOrUntagged(expectation)) => { language.primary_language() == expectation.primary_language() } (None, LanguageCondition::ExactMatchOrUntagged(_)) => true, (None, LanguageCondition::UntaggedOnly) => true, (_, LanguageCondition::AnyOrNone) => true, _ => false, } } else { false } } pub fn primary_language(&self) -> Option<&str> { match self { LanguageCondition::ExactMatchOnly(tag) => Some(tag.primary_language()), LanguageCondition::ExactMatchOrUntagged(tag) => Some(tag.primary_language()), _ => None, } } pub fn filter(&self, variable: impl Into) -> String { let variable = variable.into(); let exact = |language: &LanguageTag| { Expression::FunctionCall( Function::LangMatches, vec![ Expression::FunctionCall( Function::Lang, vec![Expression::Variable(Variable::new_unchecked(&variable))], ), Expression::Literal(Literal::new_simple_literal(language.to_string())), ], ) }; let untagged = Expression::Not(Box::new(Expression::FunctionCall( Function::HasLang, vec![Expression::Variable(Variable::new_unchecked(&variable))], ))); match self { Self::ExactMatchOnly(language) => Some(exact(language)), Self::ExactMatchOrUntagged(language) => Some(Expression::Or( Box::new(exact(language)), Box::new(untagged), )), Self::UntaggedOnly => Some(untagged), Self::AnyOrNone => None, } .map(|expr| { GraphPattern::Filter { expr, inner: Box::new(GraphPattern::Bgp { patterns: vec![] }), } .to_string() }) .unwrap_or_default() } }