use std::borrow::Cow;
use std::slice;
use std::vec;
use ciborium::value::Value;
use crate::codec::{decode_chain, Codec, CodecError};
pub const XSD_STRING: &str = "http://www.w3.org/2001/XMLSchema#string";
pub const RDF_LANG_STRING: &str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString";
pub const RDF_DIR_LANG_STRING: &str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#dirLangString";
pub fn is_literal_direction(direction: &str) -> bool {
matches!(direction, "ltr" | "rtl")
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TermKind {
Iri = 0,
Literal = 1,
Bnode = 2,
Triple = 3,
}
impl TermKind {
pub fn from_wire(k: Option<i128>) -> TermKind {
match k {
Some(1) => TermKind::Literal,
Some(2) => TermKind::Bnode,
Some(3) => TermKind::Triple,
_ => TermKind::Iri,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Term {
pub kind: TermKind,
pub value: Option<String>,
pub datatype: Option<usize>,
pub lang: Option<String>,
pub direction: Option<String>,
pub reifier: Option<usize>,
}
pub type Quad = (usize, usize, usize, Option<usize>);
pub type Triple3 = (usize, usize, usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct QuadTerms<'a> {
pub subject: &'a Term,
pub predicate: &'a Term,
pub object: &'a Term,
pub graph_name: Option<&'a Term>,
}
pub struct QuadTermsIter<'a> {
graph: &'a Graph,
inner: slice::Iter<'a, Quad>,
}
impl<'a> Iterator for QuadTermsIter<'a> {
type Item = QuadTerms<'a>;
fn next(&mut self) -> Option<Self::Item> {
let &(subject, predicate, object, graph_name) = self.inner.next()?;
Some(QuadTerms {
subject: &self.graph.terms[subject],
predicate: &self.graph.terms[predicate],
object: &self.graph.terms[object],
graph_name: graph_name.map(|tid| &self.graph.terms[tid]),
})
}
}
#[derive(Clone, Debug)]
pub struct OpaqueNode {
pub id: Vec<u8>,
pub frame_type: String,
pub reason: String,
pub sigstat: String,
pub pub_meta: Option<Value>,
pub recipients: Option<Vec<Value>>,
}
#[derive(Clone, Debug)]
pub struct Suppression {
pub targets: Vec<Value>,
pub reason: Option<String>,
pub by: Option<usize>,
}
#[derive(Clone, Debug)]
pub struct Diagnostic {
pub code: String,
pub detail: String,
pub frame_index: Option<usize>,
}
#[derive(Clone, Debug)]
pub struct Signature {
pub frame_id: Vec<u8>,
pub kid: Option<String>,
pub status: String,
pub cose: Option<Vec<u8>>,
}
#[derive(Clone, Debug, Default)]
pub struct StreamableInfo {
pub claimed: bool,
pub covered: usize,
pub tail: usize,
pub head: Option<Vec<u8>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BlobEntry {
Bytes(Vec<u8>),
Lazy { raw: Vec<u8>, chain: Vec<Codec> },
}
impl BlobEntry {
pub fn bytes(data: Vec<u8>) -> Self {
Self::Bytes(data)
}
pub fn lazy(raw: Vec<u8>, chain: Vec<Codec>) -> Self {
Self::Lazy { raw, chain }
}
pub fn is_lazy(&self) -> bool {
matches!(self, Self::Lazy { .. })
}
pub fn cached_bytes(&self) -> Option<&[u8]> {
match self {
Self::Bytes(bytes) => Some(bytes),
Self::Lazy { .. } => None,
}
}
pub fn decode(&mut self) -> Result<&[u8], CodecError> {
if let Self::Lazy { raw, chain } = self {
let decoded = decode_chain(chain, raw)?;
*self = Self::Bytes(decoded);
}
match self {
Self::Bytes(bytes) => Ok(bytes),
Self::Lazy { .. } => unreachable!("lazy entry was decoded above"),
}
}
pub fn decoded_bytes(&self) -> Result<Cow<'_, [u8]>, CodecError> {
match self {
Self::Bytes(bytes) => Ok(Cow::Borrowed(bytes)),
Self::Lazy { raw, chain } => decode_chain(chain, raw).map(Cow::Owned),
}
}
pub fn decoded_vec(&self) -> Result<Vec<u8>, CodecError> {
match self.decoded_bytes()? {
Cow::Borrowed(bytes) => Ok(bytes.to_vec()),
Cow::Owned(bytes) => Ok(bytes),
}
}
pub fn decoded_len(&self) -> Result<usize, CodecError> {
Ok(self.decoded_bytes()?.len())
}
}
#[derive(Default, Debug)]
pub struct Graph {
pub terms: Vec<Term>,
pub quads: Vec<Quad>,
pub reifiers: Vec<(usize, Triple3)>,
pub annotations: Vec<Triple3>,
pub blobs: Vec<(String, BlobEntry)>,
pub blob_meta: Vec<(String, Value)>,
pub meta: Vec<(String, Value)>,
pub suppressions: Vec<Suppression>,
pub opaque: Vec<OpaqueNode>,
pub signatures: Vec<Signature>,
pub diagnostics: Vec<Diagnostic>,
pub segment_heads: Vec<Vec<u8>>,
pub segment_profiles: Vec<String>,
pub segment_meta: Vec<Vec<(String, Value)>>,
pub segment_streamable: Vec<StreamableInfo>,
}
impl Graph {
pub fn into_quads(self) -> vec::IntoIter<Quad> {
self.quads.into_iter()
}
pub fn quad_terms(&self) -> QuadTermsIter<'_> {
QuadTermsIter {
graph: self,
inner: self.quads.iter(),
}
}
pub fn reifier(&self, rid: usize) -> Option<Triple3> {
self.reifiers
.iter()
.find(|(r, _)| *r == rid)
.map(|(_, spo)| *spo)
}
pub fn set_reifier(&mut self, rid: usize, spo: Triple3) {
if let Some(slot) = self.reifiers.iter_mut().find(|(r, _)| *r == rid) {
slot.1 = spo;
} else {
self.reifiers.push((rid, spo));
}
}
pub fn set_meta(&mut self, key: String, value: Value) {
if let Some(slot) = self.meta.iter_mut().find(|(k, _)| *k == key) {
slot.1 = value;
} else {
self.meta.push((key, value));
}
}
pub fn set_blob_meta(&mut self, digest: String, meta: Value) {
if let Some(slot) = self.blob_meta.iter_mut().find(|(d, _)| *d == digest) {
slot.1 = meta;
} else {
self.blob_meta.push((digest, meta));
}
}
pub fn set_blob_entry(&mut self, digest: String, entry: BlobEntry) {
if let Some(slot) = self.blobs.iter_mut().find(|(d, _)| *d == digest) {
slot.1 = entry;
} else {
self.blobs.push((digest, entry));
}
}
pub fn set_blob(&mut self, digest: String, data: Vec<u8>) {
self.set_blob_entry(digest, BlobEntry::bytes(data));
}
pub fn set_lazy_blob(&mut self, digest: String, raw: Vec<u8>, chain: Vec<Codec>) {
self.set_blob_entry(digest, BlobEntry::lazy(raw, chain));
}
pub fn blob_entry(&self, digest: &str) -> Option<&BlobEntry> {
self.blobs
.iter()
.find(|(d, _)| d == digest)
.map(|(_, entry)| entry)
}
pub fn blob_bytes(&mut self, digest: &str) -> Result<Option<&[u8]>, CodecError> {
match self.blobs.iter_mut().find(|(d, _)| d == digest) {
Some((_, entry)) => entry.decode().map(Some),
None => Ok(None),
}
}
pub fn blob_bytes_cloned(&mut self, digest: &str) -> Result<Option<Vec<u8>>, CodecError> {
Ok(self.blob_bytes(digest)?.map(|bytes| bytes.to_vec()))
}
pub fn decoded_blobs(&mut self) -> Result<Vec<(String, Vec<u8>)>, CodecError> {
let mut out = Vec::with_capacity(self.blobs.len());
for (digest, entry) in &mut self.blobs {
out.push((digest.clone(), entry.decode()?.to_vec()));
}
Ok(out)
}
pub fn datatype_iri(&self, t: &Term) -> String {
if let Some(dt) = t.datatype {
return self
.terms
.get(dt)
.and_then(|term| term.value.clone())
.unwrap_or_else(|| XSD_STRING.to_string());
}
if t.lang.is_some()
&& matches!(t.direction.as_deref(), Some(direction) if is_literal_direction(direction))
{
RDF_DIR_LANG_STRING.to_string()
} else if t.lang.is_some() {
RDF_LANG_STRING.to_string()
} else {
XSD_STRING.to_string()
}
}
}
impl IntoIterator for Graph {
type Item = Quad;
type IntoIter = vec::IntoIter<Quad>;
fn into_iter(self) -> Self::IntoIter {
self.into_quads()
}
}