Files
tools/graph/src/language.rs
T

92 lines
3.2 KiB
Rust
Raw Normal View History

2026-08-20 20:43:18 -04:00
use oxigraph::model::{Literal, TermRef};
2026-07-01 17:42:23 -04:00
use oxilangtag::LanguageTag;
2026-08-20 20:43:18 -04:00
use spargebra::algebra::{Expression, Function, GraphPattern};
2026-08-28 15:57:59 -04:00
use spargebra::term::Variable;
2026-09-02 23:05:29 -04:00
use std::sync::LazyLock;
2026-07-01 17:42:23 -04:00
2026-09-02 23:05:29 -04:00
pub static ENGLISH_TAG: LazyLock<LanguageTag<String>> =
LazyLock::new(|| LanguageTag::parse("en".to_string()).unwrap());
2026-07-01 17:42:23 -04:00
2026-09-02 23:05:29 -04:00
pub static ENGLISH_OR_UNTAGGED: LazyLock<LanguageCondition> =
LazyLock::new(|| LanguageCondition::ExactMatchOrUntagged(ENGLISH_TAG.clone()));
2026-07-01 17:42:23 -04:00
2026-08-20 20:43:18 -04:00
#[derive(Clone, Debug)]
2026-07-01 17:42:23 -04:00
pub enum LanguageCondition {
ExactMatchOnly(LanguageTag<String>),
ExactMatchOrUntagged(LanguageTag<String>),
UntaggedOnly,
AnyOrNone,
}
impl LanguageCondition {
2026-08-05 11:21:25 -04:00
pub fn primary_matches_term<'a>(&self, term: impl Into<TermRef<'a>>) -> bool {
if let TermRef::Literal(literal) = term.into() {
2026-08-08 23:50:04 -04:00
let tag = literal
.language()
2026-07-01 17:42:23 -04:00
.map(LanguageTag::parse_and_normalize)
.and_then(Result::ok);
match (tag, self) {
2026-08-08 23:50:04 -04:00
(Some(language), LanguageCondition::ExactMatchOnly(expectation))
| (Some(language), LanguageCondition::ExactMatchOrUntagged(expectation)) => {
language.primary_language() == expectation.primary_language()
}
2026-07-01 17:42:23 -04:00
(None, LanguageCondition::ExactMatchOrUntagged(_)) => true,
(None, LanguageCondition::UntaggedOnly) => true,
(_, LanguageCondition::AnyOrNone) => true,
_ => false,
}
} else {
false
}
}
2026-08-05 11:21:25 -04:00
pub fn primary_language(&self) -> Option<&str> {
match self {
LanguageCondition::ExactMatchOnly(tag) => Some(tag.primary_language()),
LanguageCondition::ExactMatchOrUntagged(tag) => Some(tag.primary_language()),
_ => None,
}
}
2026-08-22 23:31:14 -04:00
pub fn filter(&self, variable: impl Into<String>) -> String {
let variable = variable.into();
2026-09-02 23:05:29 -04:00
let exact = |language: &LanguageTag<String>| {
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))],
)));
2026-08-20 20:43:18 -04:00
2026-08-22 23:31:14 -04:00
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,
2026-09-02 23:05:29 -04:00
}
.map(|expr| {
GraphPattern::Filter {
expr,
inner: Box::new(GraphPattern::Bgp { patterns: vec![] }),
}
.to_string()
})
.unwrap_or_default()
2026-07-01 17:42:23 -04:00
}
2026-08-08 23:50:04 -04:00
}