use language_tags::LanguageTag;
use std::collections::HashMap;
#[cfg(feature = "taganak")]
use async_stream::stream;
#[cfg(feature = "taganak")]
use futures_util::stream::{Stream, TryStreamExt};
#[cfg(feature = "taganak")]
use std::sync::Arc;
#[cfg(feature = "taganak")]
use taganak_core::prelude::{GraphError, GraphView, Literal, Term, Triple};
#[cfg(feature = "taganak")]
use taganak_orm::{GraphORMError, GraphORMField};
#[derive(Debug, Default, Clone)]
pub struct LangString {
langs: HashMap<LanguageTag, String>,
default: Option<String>,
}
impl LangString {
pub fn set(&mut self, language: LanguageTag, string: String) {
self.langs.insert(language, string);
}
pub fn set_default(&mut self, string: String) {
let _ = self.default.insert(string);
}
pub fn unset(&mut self, language: &LanguageTag) {
self.langs.remove(language);
}
pub fn unset_default(&mut self) {
self.default.take();
}
pub fn get(&self, language: Option<&LanguageTag>) -> Option<&str> {
if language.is_none() {
return self.default.as_deref();
}
let language = language.expect("we just checked");
if let Some(string) = self.langs.get(language) {
return Some(string);
}
for known_lang in self.langs.keys() {
if !known_lang.is_language_range() {
continue;
}
if known_lang.matches(language) {
return self.langs.get(known_lang).map(|s| s.as_str());
}
}
self.default.as_deref()
}
#[cfg(feature = "taganak")]
pub async fn from_graph<G>(
graph: G,
subject: &Term,
predicate: &Term,
) -> Result<Self, GraphError>
where
G: GraphView + Clone,
{
use std::sync::Arc;
let mut lang_string = Self::default();
static LIMIT: usize = 256;
let mut stream = graph
.objects(Some(subject), Some(predicate), Some(LIMIT))
.await?;
while let Some(object) = stream.try_next().await? {
let object: Arc<Term> = object;
if !object.is_literal() {
continue;
}
let literal = object.to_literal().expect("we just checked");
match literal.datatype() {
"http://www.w3.org/2001/XMLSchema#string" => {
lang_string.set_default(literal.lexical().to_string())
}
"http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"
| "http://www.w3.org/1999/02/22-rdf-syntax-ns#dirLangString" => lang_string.set(
literal.language().expect("we just checked").clone(),
literal.lexical().to_string(),
),
_ => continue,
}
}
Ok(lang_string)
}
}
#[cfg(feature = "taganak")]
impl GraphORMField for LangString {
fn orm_triples(
&self,
subject: Option<&Term>,
predicate: Option<&Term>,
) -> Result<impl Stream<Item = Arc<Triple>>, GraphORMError> {
let subject = Arc::new(subject.cloned().unwrap());
let predicate = Arc::new(predicate.cloned().unwrap());
Ok(Box::pin(stream! {
if let Some(string) = &self.default {
yield Triple::new(
subject.clone(),
predicate.clone(),
Arc::new(Term::Literal(Literal::new(string.clone(), None, None, None).unwrap()))
).unwrap();
}
for (lang, string) in self.langs.iter() {
yield Triple::new(
subject.clone(),
predicate.clone(),
Arc::new(
Term::Literal(
Literal::new(string.clone(), None, Some(lang.clone()), None).unwrap()
)
)
).unwrap();
}
}))
}
async fn orm_field_from_graph(
graph: impl GraphView,
subject: &Term,
predicate: &Term,
_object: Option<&Term>,
) -> Result<Self, GraphORMError>
where
Self: Sized,
{
Self::from_graph(graph, subject, predicate)
.await
.map_err(|e| GraphORMError::FailedDeserialize(e.to_string()))
}
}