use oxigraph::model::{Term, TermRef}; use oxilangtag::LanguageTag; use std::sync::LazyLock; pub const ENGLISH_PRIMARY: &str = "en"; pub static ENGLISH_OR_UNTAGGED: LazyLock = LazyLock::new(|| { LanguageCondition::ExactMatchOrUntagged( LanguageTag::parse(ENGLISH_PRIMARY.to_string()).unwrap(), ) }); 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 to_filter_expression(&self, var: &str) -> String { match self { LanguageCondition::ExactMatchOnly(language) => { format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}"))"#) } LanguageCondition::ExactMatchOrUntagged(language) => { format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}") || !hasLANG(?{var}))"#) } LanguageCondition::UntaggedOnly => format!("FILTER(!hasLANG(?{var}))"), LanguageCondition::AnyOrNone => "".to_string(), } } }