use super::{RDF_DIRECTION, RDF_FIRST, RDF_JSON, RDF_NIL, RDF_REST, RDF_VALUE, RdfDirection, XSD_BOOLEAN, XSD_DOUBLE, XSD_INTEGER, XSD_STRING};
use crate::{Direction, Id, Indexed, IndexedObject, Node, Object, ValidId, object::value};
use iri_rs::IriBuf;
use jstrict::Print;
use langtag::LangTagBuf;
use rdfx::{
Literal,
LocalGenerator,
vocabulary::{IriVocabularyMut, Vocabulary, VocabularyMut},
};
use smallvec::SmallVec;
pub type Triple<T, B, L> = rdfx::GeneralizedTriple<ValidId<T, B>, ValidId<T, B>, Value<T, B, L>>;
fn static_datatype(iri: iri_rs::Iri<&'static str>) -> rdfx::Datatype {
rdfx::Datatype::new(IriBuf::from(iri)).unwrap_or_else(|_| rdfx::Datatype::xsd_string())
}
fn datatype_of<V: rdfx::vocabulary::IriVocabulary>(vocabulary: &V, iri: &V::Iri) -> Option<rdfx::Datatype> {
let buf = IriBuf::from(vocabulary.iri(iri)?);
rdfx::Datatype::new(buf).ok()
}
impl<T: Clone, B: Clone> Id<T, B> {
fn rdf_value<L>(&self) -> Option<Value<T, B, L>> {
match self {
Id::Valid(id) => Some(Value::Id(id.clone())),
Id::Invalid(_) => None,
}
}
}
pub struct CompoundLiteralTriples<T, B, L> {
id: ValidId<T, B>,
value: Option<Value<T, B, L>>,
direction: Option<Value<T, B, L>>,
}
impl<T: Clone, B: Clone, L: Clone> CompoundLiteralTriples<T, B, L> {
fn next(&mut self, vocabulary: &mut impl IriVocabularyMut<Iri = T>) -> Option<Triple<T, B, L>> {
if let Some(value) = self.value.take() {
return Some(rdfx::GeneralizedTriple(self.id.clone(), ValidId::Iri(vocabulary.insert(RDF_VALUE)), value));
}
if let Some(direction) = self.direction.take() {
return Some(rdfx::GeneralizedTriple(
self.id.clone(),
ValidId::Iri(vocabulary.insert(RDF_DIRECTION)),
direction,
));
}
None
}
}
pub struct CompoundLiteral<T, B, L> {
value: Value<T, B, L>,
triples: Option<CompoundLiteralTriples<T, B, L>>,
}
impl<T: Clone> crate::object::Value<T> {
fn rdf_value_with<V, G: LocalGenerator>(
&self,
vocabulary: &mut V,
generator: &mut G,
rdf_direction: Option<RdfDirection>,
) -> Option<CompoundLiteral<T, V::BlankId, V::Literal>>
where
V: Vocabulary<Iri = T> + VocabularyMut,
{
match self {
Self::Json(json) => Some(CompoundLiteral {
value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(
json.compact_print().to_string(),
rdfx::LiteralType::Any(static_datatype(RDF_JSON)),
))),
triples: None,
}),
Self::LangString(lang_string) => {
let (string, language, direction) = lang_string.parts();
let language = match language {
Some(language) => Some(language.as_well_formed()?.to_owned()),
None => None,
};
match direction {
Some(direction) => match rdf_direction {
Some(RdfDirection::I18nDatatype) => {
let ty = rdfx::Datatype::new(i18n(language.as_ref(), *direction)).unwrap_or_else(|_| rdfx::Datatype::xsd_string());
Some(CompoundLiteral {
value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(string.to_string(), rdfx::LiteralType::Any(ty)))),
triples: None,
})
}
Some(RdfDirection::CompoundLiteral) => {
let id = crate::id::generator_next_id(vocabulary, generator).ok()?;
Some(CompoundLiteral {
value: Value::from_id(id),
triples: None,
})
}
None => match language {
Some(tag) => Some(CompoundLiteral {
value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(string.to_string(), rdfx::LiteralType::LangString(tag)))),
triples: None,
}),
None => Some(CompoundLiteral {
value: Value::Literal(
vocabulary.insert_owned_literal(Literal::new(string.to_string(), rdfx::LiteralType::Any(static_datatype(XSD_STRING)))),
),
triples: None,
}),
},
},
None => match language {
Some(tag) => Some(CompoundLiteral {
value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(string.to_string(), rdfx::LiteralType::LangString(tag)))),
triples: None,
}),
None => Some(CompoundLiteral {
value: Value::Literal(
vocabulary.insert_owned_literal(Literal::new(string.to_string(), rdfx::LiteralType::Any(static_datatype(XSD_STRING)))),
),
triples: None,
}),
},
}
}
Self::Literal(lit, ty) => {
let (rdf_lit, preferred_rdf_ty) = match lit {
value::Literal::Boolean(b) => {
let lit = if *b { "true".to_string() } else { "false".to_string() };
(lit, Some(static_datatype(XSD_BOOLEAN)))
}
value::Literal::Null => ("null".to_string(), None),
value::Literal::Number(n) => {
if n.is_i64() && ty.as_ref().and_then(|t| vocabulary.iri(t)).is_none_or(|i| i != XSD_DOUBLE) {
(n.to_string(), Some(static_datatype(XSD_INTEGER)))
} else {
(pretty_dtoa::dtoa(n.as_f64_lossy(), XSD_CANONICAL_FLOAT), Some(static_datatype(XSD_DOUBLE)))
}
}
value::Literal::String(s) => (s.to_string(), None),
};
let rdf_ty = match ty {
Some(id) => Some(datatype_of(vocabulary, id)?),
None => preferred_rdf_ty,
};
Some(CompoundLiteral {
value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(
rdf_lit,
rdfx::LiteralType::Any(rdf_ty.unwrap_or_else(|| static_datatype(XSD_STRING))),
))),
triples: None,
})
}
}
}
}
pub(crate) const XSD_CANONICAL_FLOAT: pretty_dtoa::FmtFloatConfig = pretty_dtoa::FmtFloatConfig::default().force_e_notation().capitalize_e(true);
impl<T: Clone, B: Clone> Node<T, B> {
fn rdf_value<L>(&self) -> Option<Value<T, B, L>> {
self.id.as_ref().and_then(Id::rdf_value)
}
}
impl<T: Clone, B: Clone> Object<T, B> {
fn rdf_value_with<V, G: LocalGenerator>(
&self,
vocabulary: &mut V,
generator: &mut G,
rdf_direction: Option<RdfDirection>,
) -> Option<CompoundValue<'_, T, B, V::Literal>>
where
V: Vocabulary<Iri = T, BlankId = B> + VocabularyMut,
{
match self {
Self::Value(value) => value.rdf_value_with(vocabulary, generator, rdf_direction).map(|compound_value| CompoundValue {
value: compound_value.value,
triples: compound_value.triples.map(CompoundValueTriples::literal),
}),
Self::Node(node) => node.rdf_value().map(|value| CompoundValue { value, triples: None }),
Self::List(list) => {
if list.is_empty() {
Some(CompoundValue {
value: Value::Id(ValidId::Iri(vocabulary.insert(RDF_NIL))),
triples: None,
})
} else {
let id = crate::id::generator_next_id(vocabulary, generator).ok()?;
Some(CompoundValue {
value: Value::from_id(id.clone()),
triples: Some(CompoundValueTriples::List(ListTriples::new(list.as_slice(), id))),
})
}
}
}
}
}
pub struct CompoundValue<'a, T, B, L> {
pub value: Value<T, B, L>,
pub triples: Option<CompoundValueTriples<'a, T, B, L>>,
}
impl<'a, T: Clone, B: Clone> crate::quad::ObjectRef<'a, T, B> {
pub fn rdf_value_with<V, G: LocalGenerator>(
&self,
vocabulary: &mut V,
generator: &mut G,
rdf_direction: Option<RdfDirection>,
) -> Option<CompoundValue<'a, T, B, V::Literal>>
where
V: Vocabulary<Iri = T, BlankId = B> + VocabularyMut,
{
match self {
Self::Object(object) => object.rdf_value_with(vocabulary, generator, rdf_direction),
Self::Node(node) => node.rdf_value().map(|value| CompoundValue { value, triples: None }),
Self::Ref(r) => r.rdf_value().map(|value| CompoundValue { value, triples: None }),
}
}
}
enum ListItemTriples<'a, T, B, L> {
NestedList(NestedListTriples<'a, T, B>),
CompoundLiteral(Box<CompoundLiteralTriples<T, B, L>>),
}
struct NestedListTriples<'a, T, B> {
head_ref: Option<ValidId<T, B>>,
previous: Option<ValidId<T, B>>,
iter: std::slice::Iter<'a, IndexedObject<T, B>>,
}
struct ListNode<'a, 'i, T, B> {
id: &'i ValidId<T, B>,
object: &'a Indexed<Object<T, B>>,
}
impl<'a, T, B> NestedListTriples<'a, T, B> {
fn new(list: &'a [IndexedObject<T, B>], head_ref: ValidId<T, B>) -> Self {
Self {
head_ref: Some(head_ref),
previous: None,
iter: list.iter(),
}
}
fn previous(&self) -> Option<&ValidId<T, B>> {
self.previous.as_ref()
}
fn next<V: VocabularyMut<Iri = T, BlankId = B>, G: LocalGenerator>(&mut self, vocabulary: &mut V, generator: &mut G) -> Option<ListNode<'a, '_, T, B>> {
if let Some(next) = self.iter.next() {
let id = match self.head_ref.take() {
Some(id) => id,
None => crate::id::generator_next_id(vocabulary, generator).ok()?,
};
Some(ListNode {
object: next,
id: self.previous.insert(id),
})
} else {
None
}
}
}
pub enum CompoundValueTriples<'a, T, B, L> {
Literal(Box<CompoundLiteralTriples<T, B, L>>),
List(ListTriples<'a, T, B, L>),
}
impl<'a, T, B, L> CompoundValueTriples<'a, T, B, L> {
pub fn literal(l: CompoundLiteralTriples<T, B, L>) -> Self {
Self::Literal(Box::new(l))
}
pub fn with<'n, V: Vocabulary<Iri = T, BlankId = B, Literal = L>, G: LocalGenerator>(
self,
vocabulary: &'n mut V,
generator: G,
rdf_direction: Option<RdfDirection>,
) -> CompoundValueTriplesWith<'a, 'n, V, G> {
CompoundValueTriplesWith {
vocabulary,
generator,
rdf_direction,
inner: self,
}
}
pub fn next<V, G: LocalGenerator>(&mut self, vocabulary: &mut V, generator: &mut G, rdf_direction: Option<RdfDirection>) -> Option<Triple<T, B, L>>
where
T: Clone,
B: Clone,
L: Clone,
V: Vocabulary<Iri = T, BlankId = B, Literal = L> + VocabularyMut,
{
match self {
Self::Literal(l) => l.next(vocabulary),
Self::List(l) => l.next(vocabulary, generator, rdf_direction),
}
}
}
pub struct CompoundValueTriplesWith<'a, 'n, N: Vocabulary, G: LocalGenerator> {
vocabulary: &'n mut N,
generator: G,
rdf_direction: Option<RdfDirection>,
inner: CompoundValueTriples<'a, N::Iri, N::BlankId, N::Literal>,
}
impl<N: Vocabulary + VocabularyMut, G: LocalGenerator> Iterator for CompoundValueTriplesWith<'_, '_, N, G>
where
N::Iri: Clone,
N::BlankId: Clone,
N::Literal: Clone,
{
type Item = Triple<N::Iri, N::BlankId, N::Literal>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next(self.vocabulary, &mut self.generator, self.rdf_direction)
}
}
pub struct ListTriples<'a, T, B, L> {
stack: SmallVec<[ListItemTriples<'a, T, B, L>; 2]>,
pending: Option<Triple<T, B, L>>,
}
impl<'a, T, B, L> ListTriples<'a, T, B, L> {
pub fn new(list: &'a [IndexedObject<T, B>], head_ref: ValidId<T, B>) -> Self {
let mut stack = SmallVec::new();
stack.push(ListItemTriples::NestedList(NestedListTriples::new(list, head_ref)));
Self { stack, pending: None }
}
pub fn with<'n, V: Vocabulary<Iri = T, BlankId = B, Literal = L>, G: LocalGenerator>(
self,
vocabulary: &'n mut V,
generator: G,
rdf_direction: Option<RdfDirection>,
) -> ListTriplesWith<'a, 'n, V, G> {
ListTriplesWith {
vocabulary,
generator,
rdf_direction,
inner: self,
}
}
pub fn next<V, G: LocalGenerator>(&mut self, vocabulary: &mut V, generator: &mut G, rdf_direction: Option<RdfDirection>) -> Option<Triple<T, B, L>>
where
T: Clone,
B: Clone,
L: Clone,
V: Vocabulary<Iri = T, BlankId = B, Literal = L> + VocabularyMut,
{
loop {
if let Some(pending) = self.pending.take() {
break Some(pending);
}
match self.stack.last_mut() {
Some(ListItemTriples::CompoundLiteral(lit)) => match lit.next(vocabulary) {
Some(triple) => break Some(triple),
None => {
self.stack.pop();
}
},
Some(ListItemTriples::NestedList(list)) => {
let previous = list.previous().cloned();
if let Some(node) = list.next(vocabulary, generator) {
if let Some(compound_value) = node.object.rdf_value_with(vocabulary, generator, rdf_direction) {
let id = node.id.clone();
if let Some(compound_triples) = compound_value.triples {
match compound_triples {
CompoundValueTriples::List(nested) => self.stack.extend(nested.stack),
CompoundValueTriples::Literal(literal) => self.stack.push(ListItemTriples::CompoundLiteral(literal)),
}
}
self.pending = Some(rdfx::GeneralizedTriple(
id.clone(),
ValidId::Iri(vocabulary.insert(RDF_FIRST)),
compound_value.value,
));
if let Some(previous_id) = previous {
break Some(rdfx::GeneralizedTriple(
previous_id,
ValidId::Iri(vocabulary.insert(RDF_REST)),
Value::from_id(id),
));
}
}
} else {
self.stack.pop();
if let Some(previous_id) = previous {
break Some(rdfx::GeneralizedTriple(
previous_id,
ValidId::Iri(vocabulary.insert(RDF_REST)),
Value::Id(ValidId::Iri(vocabulary.insert(RDF_NIL))),
));
}
}
}
None => break None,
}
}
}
}
pub struct ListTriplesWith<'a, 'n, V: Vocabulary, G: LocalGenerator> {
vocabulary: &'n mut V,
generator: G,
rdf_direction: Option<RdfDirection>,
inner: ListTriples<'a, V::Iri, V::BlankId, V::Literal>,
}
impl<N: Vocabulary + VocabularyMut, G: LocalGenerator> Iterator for ListTriplesWith<'_, '_, N, G>
where
N::Iri: Clone,
N::BlankId: Clone,
N::Literal: Clone,
{
type Item = Triple<N::Iri, N::BlankId, N::Literal>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next(self.vocabulary, &mut self.generator, self.rdf_direction)
}
}
fn i18n(language: Option<&LangTagBuf>, direction: Direction) -> IriBuf {
let iri = match language {
Some(language) => format!("https://www.w3.org/ns/i18n#{language}_{direction}"),
None => format!("https://www.w3.org/ns/i18n#{direction}"),
};
IriBuf::new(iri).unwrap_or_else(|_| IriBuf::from(iri_rs::iri!("https://www.w3.org/ns/i18n#")))
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value<T, B, L> {
Id(ValidId<T, B>),
Literal(L),
}
impl<T, B, L> Value<T, B, L> {
pub fn from_id(id: ValidId<T, B>) -> Self {
Self::Id(id)
}
}