#![deny(
bad_style,
bare_trait_objects,
const_err,
dead_code,
improper_ctypes,
legacy_directory_ownership,
missing_debug_implementations,
missing_docs,
no_mangle_generic_items,
non_shorthand_field_patterns,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
plugin_as_library,
private_in_public,
safe_extern_statics,
trivial_numeric_casts,
unconditional_recursion,
unions_with_drop_fields,
unsafe_code,
unused,
unused_allocation,
unused_comparisons,
unused_extern_crates,
unused_import_braces,
unused_parens,
unused_qualifications,
unused_results,
while_true
)]
pub mod command;
mod lexer;
pub mod naive_solve;
pub mod nameless;
#[allow(unused_parens)]
mod parser {
pub use self::parser::*;
use lalrpop_util::lalrpop_mod;
lalrpop_mod!(parser);
}
pub mod query;
#[cfg(test)]
mod strategies;
#[cfg(test)]
mod tests;
pub mod utils;
mod validate;
use crate::nameless::NamelessQuery;
pub use bytes::Bytes;
use derive_more::{Constructor, Display, From, FromStr, Into};
use futures::prelude::*;
pub use mime::Mime;
use serde_derive::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter, Result as FmtResult},
pin::Pin,
str::FromStr,
sync::Arc,
};
use uuid::Uuid;
#[derive(
Clone,
Copy,
Debug,
Deserialize,
Eq,
Display,
FromStr,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
)]
#[serde(transparent)]
pub struct Atom(#[serde(with = "utils::string")] Uuid);
impl Atom {
pub fn new() -> Atom {
Atom(Uuid::new_v4())
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(transparent)]
pub struct Hash([u8; 32]);
impl Hash {
pub fn from_bytes(bytes: &[u8]) -> Hash {
assert_eq!(bytes.len(), 32);
let mut hash = [0; 32];
for i in 0..32 {
hash[i] = bytes[i];
}
Hash(hash)
}
}
impl Display for Hash {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
for i in 0..32 {
write!(fmt, "{:02x}", self.0[i])?;
}
Ok(())
}
}
impl FromStr for Hash {
type Err = HashParseError;
fn from_str(s: &str) -> Result<Hash, HashParseError> {
fn hex(ch: char, i: usize) -> Result<u8, HashParseError> {
Ok(match ch {
'0' => 0,
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'a' | 'A' => 10,
'b' | 'B' => 11,
'c' | 'C' => 12,
'd' | 'D' => 13,
'e' | 'E' => 14,
'f' | 'F' => 15,
ch => return Err(HashParseError::BadChar(i, ch)),
})
}
if s.len() != 64 {
return Err(HashParseError::BadLength(s.len()));
}
let mut hash = [0; 32];
for (i, ch) in s.chars().enumerate() {
let x = hex(ch, i)?;
let j = i / 2;
let off = if (i % 2) == 0 { 4 } else { 0 };
hash[j] |= x << off;
}
Ok(Hash(hash))
}
}
#[derive(Clone, Copy, Debug)]
pub enum HashParseError {
BadChar(usize, char),
BadLength(usize),
}
impl Display for HashParseError {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
match self {
HashParseError::BadChar(i, c) => write!(
fmt,
"the character {:?} at index {} wasn't a hex character",
c, i
),
HashParseError::BadLength(l) => {
write!(fmt, "the string should be of length 64, not {}", l)
}
}
}
}
impl std::error::Error for HashParseError {}
#[async_trait::async_trait]
pub trait Connection: Send + Sync {
type Error: Error;
async fn create_atom(&self) -> Result<Atom, Self::Error>;
async fn delete_atom(&self, atom: Atom) -> Result<(), Self::Error>;
async fn create_name(
&self,
atom: Atom,
ns: &str,
title: &str,
upsert: bool,
) -> Result<(), Self::Error>;
async fn delete_name(&self, ns: &str, title: &str) -> Result<bool, Self::Error>;
async fn create_edge(&self, from: Atom, to: Atom, label: &str) -> Result<bool, Self::Error>;
async fn delete_edge(&self, from: Atom, to: Atom, label: &str) -> Result<bool, Self::Error>;
async fn create_tag(
&self,
atom: Atom,
key: &str,
value: &str,
upsert: bool,
) -> Result<(), Self::Error>;
async fn delete_tag(&self, atom: Atom, key: &str) -> Result<bool, Self::Error>;
async fn create_blob(
&self,
atom: Atom,
kind: &str,
mime: Mime,
hash: Hash,
upsert: bool,
) -> Result<(), Self::Error>;
async fn delete_blob(&self, atom: Atom, kind: &str, mime: Mime) -> Result<bool, Self::Error>;
async fn fetch_blob(
&self,
hash: Hash,
) -> Result<Pin<Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Send>>, Self::Error>;
async fn store_blob(
&self,
data: Pin<Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Send + 'static>>,
) -> Result<Hash, Self::Error>;
async fn query(
&self,
limit: Option<usize>,
query: &NamelessQuery,
) -> Result<Vec<Vec<Arc<str>>>, Self::Error>;
async fn query_all(&self, query: &NamelessQuery) -> Result<Vec<Vec<Arc<str>>>, Self::Error> {
self.query(None, query).await
}
async fn query_first(
&self,
query: &NamelessQuery,
) -> Result<Option<Vec<Arc<str>>>, Self::Error> {
let mut v = self.query(Some(1), query).await?;
debug_assert!(v.len() < 2);
Ok(v.pop())
}
async fn query_has_results(&self, query: &NamelessQuery) -> Result<bool, Self::Error> {
Ok(self.query_first(query).await?.is_some())
}
}
static_assertions::assert_obj_safe!(Connection<Error = SimpleError>);
pub trait Error: std::error::Error + Send + Sync + 'static {
fn invalid_query(msg: String) -> Self;
}
#[derive(Clone, Constructor, Debug, Display, Eq, From, Hash, Into, Ord, PartialEq, PartialOrd)]
pub struct SimpleError(pub String);
impl std::error::Error for SimpleError {}
impl Error for SimpleError {
fn invalid_query(msg: String) -> SimpleError {
SimpleError(msg)
}
}