tx triples

main
Ziyang Hu 2 years ago
parent 51a27163c6
commit a9c1676e24

@ -29,6 +29,7 @@ ordered-float = { version = "3.0", features = ["serde"] }
actix-web = "4.1.0" actix-web = "4.1.0"
clap = { version = "3.2.8", features = ["derive"] } clap = { version = "3.2.8", features = ["derive"] }
itertools = "0.10.3" itertools = "0.10.3"
actix-cors = "0.6.1"
cozorocks = { path = "cozorocks" } cozorocks = { path = "cozorocks" }
[target.'cfg(not(target_env = "msvc"))'.dependencies] [target.'cfg(not(target_env = "msvc"))'.dependencies]

@ -1,3 +1,4 @@
use actix_cors::Cors;
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder}; use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use clap::Parser; use clap::Parser;
use cozo::{AttrTxItem, Db}; use cozo::{AttrTxItem, Db};
@ -107,8 +108,11 @@ async fn main() -> std::io::Result<()> {
eprintln!("Serving database at {}:{}", addr.0, addr.1); eprintln!("Serving database at {}:{}", addr.0, addr.1);
HttpServer::new(move || { HttpServer::new(move || {
let cors = Cors::permissive();
App::new() App::new()
.app_data(app_state.clone()) .app_data(app_state.clone())
.wrap(cors)
.service(query) .service(query)
.service(transact) .service(transact)
.service(transact_attr) .service(transact_attr)

@ -27,6 +27,44 @@ impl Validity {
} }
} }
impl From<i64> for Validity {
fn from(i: i64) -> Self {
Validity(i)
}
}
impl TryFrom<&str> for Validity {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let dt =
DateTime::parse_from_rfc2822(value).or_else(|_| DateTime::parse_from_rfc3339(value))?;
let sysdt: SystemTime = dt.into();
let timestamp = sysdt.duration_since(UNIX_EPOCH).unwrap().as_micros() as i64;
Ok(Self(timestamp))
}
}
#[derive(Debug, thiserror::Error)]
pub enum IdError {
#[error("Cannot convert to validity: {0}")]
JsonValidityError(serde_json::Value),
}
impl TryFrom<&serde_json::Value> for Validity {
type Error = anyhow::Error;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
if let Some(v) = value.as_i64() {
return Ok(v.into());
}
if let Some(s) = value.as_str() {
return s.try_into();
}
Err(IdError::JsonValidityError(value.clone()).into())
}
}
impl Debug for Validity { impl Debug for Validity {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let dt = Utc.timestamp(self.0 / 1_000_000, (self.0 % 1_000_000) as u32 * 1000); let dt = Utc.timestamp(self.0 / 1_000_000, (self.0 % 1_000_000) as u32 * 1000);

@ -1,7 +1,6 @@
use lazy_static::lazy_static;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use serde_json::Value;
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use std::collections::BTreeSet;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::str::Utf8Error; use std::str::Utf8Error;
@ -15,6 +14,9 @@ pub enum KeywordError {
#[error(transparent)] #[error(transparent)]
Utf8(#[from] Utf8Error), Utf8(#[from] Utf8Error),
#[error("unexpected json {0}")]
UnexpectedJson(serde_json::Value),
} }
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Deserialize, Serialize)] #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Deserialize, Serialize)]
@ -45,6 +47,7 @@ impl TryFrom<&str> for Keyword {
type Error = KeywordError; type Error = KeywordError;
fn try_from(value: &str) -> Result<Self, Self::Error> { fn try_from(value: &str) -> Result<Self, Self::Error> {
let make_err = || KeywordError::InvalidKeyword(value.to_string()); let make_err = || KeywordError::InvalidKeyword(value.to_string());
let value = value.strip_prefix(':').unwrap_or(value);
let mut kw_iter = value.split('/'); let mut kw_iter = value.split('/');
let ns = kw_iter.next().ok_or_else(make_err)?; let ns = kw_iter.next().ok_or_else(make_err)?;
let ident = match kw_iter.next() { let ident = match kw_iter.next() {

@ -5,7 +5,7 @@ pub(crate) mod id;
pub(crate) mod json; pub(crate) mod json;
pub(crate) mod keyword; pub(crate) mod keyword;
pub(crate) mod triple; pub(crate) mod triple;
pub(crate) mod tx;
pub(crate) mod value; pub(crate) mod value;
pub(crate) mod tx_attr; pub(crate) mod tx_attr;
pub(crate) mod tx_triple;

@ -0,0 +1,122 @@
use crate::data::id::{AttrId, EntityId, Validity};
use crate::data::keyword::Keyword;
use crate::data::triple::StoreOp;
use crate::data::value::Value;
use crate::runtime::transact::SessionTx;
use anyhow::Result;
use serde_json::Map;
pub(crate) struct Triple<'a> {
id: EntityId,
attr: AttrId,
value: Value<'a>,
}
pub struct Quintuple<'a> {
triple: Triple<'a>,
op: StoreOp,
validity: Validity,
}
#[derive(Debug, thiserror::Error)]
pub enum TxError {
#[error("Error decoding {0}: {1}")]
Decoding(serde_json::Value, String),
#[error("triple length error")]
TripleLength,
#[error("attribute not found: {0}")]
AttrNotFound(Keyword),
}
impl SessionTx {
/// Requests are like these
/// ```json
/// {"tx": [...], "comment": "a comment", "since": timestamp}
/// ```
/// each line in `tx` is `{"put: ...}`, `{"retract": ...}`, `{"erase": ...}` or `{"ensure": ...}`
/// these can also have a `from` field, overriding the timestamp
/// the dots can be triples
/// ```json
/// [12345, ":x/y", 12345]
/// ```
/// triples with tempid
/// ```json
/// ["tempid1", ":x/y", 12345]
/// ```
/// objects format
/// ```json
/// {
/// "_id": 12345,
/// "_tempid": "xyzwf",
/// "ns/fieldname": 111
/// }
/// ```
/// nesting is allowed for values of type `ref` and `component`
pub fn parse_tx_requests<'a>(
&mut self,
req: &'a serde_json::Value,
) -> Result<(Vec<Quintuple<'a>>, String)> {
let map = req
.as_object()
.ok_or_else(|| TxError::Decoding(req.clone(), "expected object".to_string()))?;
let items = map
.get("tx")
.ok_or_else(|| TxError::Decoding(req.clone(), "expected field 'tx'".to_string()))?
.as_array()
.ok_or_else(|| {
TxError::Decoding(
req.clone(),
"expected field 'tx' to be an array".to_string(),
)
})?;
let default_since = match map.get("since") {
None => Validity::current(),
Some(v) => v.try_into()?,
};
let comment = match map.get("comment") {
None => "".to_string(),
Some(v) => v.to_string(),
};
let mut collected = Vec::with_capacity(items.len());
for item in items {
collected.push(self.parse_tx_request_item(item, default_since)?)
}
Ok((collected, comment))
}
fn parse_tx_request_item<'a>(
&mut self,
item: &'a serde_json::Value,
default_since: Validity,
) -> Result<Quintuple<'a>> {
if let Some(arr) = item.as_array() {
return self.parse_tx_request_arr(arr, default_since);
}
if let Some(obj) = item.as_object() {
return self.parse_tx_request_obj(obj, default_since);
}
Err(TxError::Decoding(item.clone(), "expected object or array".to_string()).into())
}
fn parse_tx_request_arr<'a>(
&mut self,
item: &'a [serde_json::Value],
default_since: Validity,
) -> Result<Quintuple<'a>> {
match item {
[eid, attr_kw, val] => {
let kw: Keyword = attr_kw.try_into()?;
let attr = self.attr_by_kw(&kw)?.ok_or(TxError::AttrNotFound(kw))?;
todo!()
}
vs => Err(TxError::TripleLength.into()),
}
}
fn parse_tx_request_obj<'a>(
&mut self,
item: &'a Map<String, serde_json::Value>,
default_since: Validity,
) -> Result<Quintuple<'a>> {
todo!()
}
}
Loading…
Cancel
Save