implement Serialize/Deserialize for Entry, Address, remove as_json

+ cosmetics (to_str -> to_string)
feat/vaults
Tomáš Mládek 2021-02-19 21:45:33 +01:00
parent 99f6c6c052
commit dc71bec67a
4 changed files with 62 additions and 52 deletions

View File

@ -1,14 +1,15 @@
use crate::hash::{decode, encode, Hash};
use anyhow::{anyhow, Result};
use serde::de::Visitor;
use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::Cursor; use std::io::Cursor;
use anyhow::{anyhow, Result};
use unsigned_varint::encode;
use uuid::Uuid;
use crate::hash::{encode, Hash};
use std::str::FromStr; use std::str::FromStr;
use thiserror::private::DisplayAsDisplay; use thiserror::private::DisplayAsDisplay;
use unsigned_varint::encode;
use uuid::Uuid;
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Address { pub enum Address {
@ -57,6 +58,42 @@ impl Address {
} }
} }
impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_str(encode(self.encode().map_err(ser::Error::custom)?).as_str())
}
}
struct AddressVisitor;
impl<'de> Visitor<'de> for AddressVisitor {
type Value = Address;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid UpEnd address (hash/UUID) as a multi-hashed string")
}
fn visit_str<E>(self, str: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = decode(str).map_err(de::Error::custom)?;
Ok(Address::decode(bytes.as_ref()).map_err(de::Error::custom)?)
}
}
impl<'de> Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Address, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(AddressVisitor)
}
}
impl FromStr for Address { impl FromStr for Address {
type Err = anyhow::Error; type Err = anyhow::Error;

View File

@ -14,6 +14,7 @@ use lexpr::value::Value::Symbol;
use lexpr::Value::Cons; use lexpr::Value::Cons;
use log::{debug, trace}; use log::{debug, trace};
use nonempty::NonEmpty; use nonempty::NonEmpty;
use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -23,34 +24,20 @@ use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entry { pub struct Entry {
pub target: Address, pub target: Address,
pub key: String, pub key: String,
pub value: EntryValue, pub value: EntryValue,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum EntryValue { pub enum EntryValue {
Value(serde_json::Value), Value(serde_json::Value),
Address(Address), Address(Address),
Invalid, Invalid,
} }
impl Entry {
pub fn as_json(&self) -> serde_json::Value {
json!({
"target": self.target.to_string(),
"key": self.key,
"value": match &self.value {
EntryValue::Value(value) => ("VALUE", value.clone()),
EntryValue::Address(address) => ("ADDR", json!(address.to_string())),
EntryValue::Invalid => ("INVALID", json!("INVALID")),
}
})
}
}
impl TryFrom<models::Entry> for Entry { impl TryFrom<models::Entry> for Entry {
type Error = anyhow::Error; type Error = anyhow::Error;
@ -74,13 +61,13 @@ impl Hashable for Entry {
let mut result = Cursor::new(vec![0u8; 0]); let mut result = Cursor::new(vec![0u8; 0]);
result.write_all(self.target.encode()?.as_slice())?; result.write_all(self.target.encode()?.as_slice())?;
result.write_all(self.key.as_bytes())?; result.write_all(self.key.as_bytes())?;
result.write_all(self.value.to_str()?.as_bytes())?; result.write_all(self.value.to_string()?.as_bytes())?;
Ok(hash(result.get_ref())) Ok(hash(result.get_ref()))
} }
} }
impl EntryValue { impl EntryValue {
pub fn to_str(&self) -> Result<String> { pub fn to_string(&self) -> Result<String> {
let (type_char, content) = match self { let (type_char, content) = match self {
EntryValue::Value(value) => ('J', serde_json::to_string(value)?), EntryValue::Value(value) => ('J', serde_json::to_string(value)?),
EntryValue::Address(address) => ('O', address.to_string()), EntryValue::Address(address) => ('O', address.to_string()),
@ -193,7 +180,7 @@ pub fn retrieve_object<C: Connection<Backend = Sqlite>>(
let matches = data let matches = data
.filter(target.eq(object_address.encode()?)) .filter(target.eq(object_address.encode()?))
.or_filter(value.eq(EntryValue::Address(object_address).to_str()?)) .or_filter(value.eq(EntryValue::Address(object_address).to_string()?))
.load::<models::Entry>(connection)?; .load::<models::Entry>(connection)?;
let entries = matches let entries = matches
.into_iter() .into_iter()
@ -239,7 +226,7 @@ pub fn remove_object<C: Connection<Backend = Sqlite>>(
let matches = data let matches = data
.filter(target.eq(object_address.encode()?)) .filter(target.eq(object_address.encode()?))
.or_filter(value.eq(EntryValue::Address(object_address).to_str()?)); .or_filter(value.eq(EntryValue::Address(object_address).to_string()?));
Ok(diesel::delete(matches).execute(connection)?) Ok(diesel::delete(matches).execute(connection)?)
} }
@ -499,11 +486,11 @@ fn query_to_sqlite(query: &Query) -> Result<Box<Predicate>> {
match &eq.value { match &eq.value {
QueryComponent::Exact(q_value) => { QueryComponent::Exact(q_value) => {
subqueries.push(Box::new(data::value.eq(q_value.to_str()?))) subqueries.push(Box::new(data::value.eq(q_value.to_string()?)))
} }
QueryComponent::In(q_values) => { QueryComponent::In(q_values) => {
let values: Result<Vec<_>, _> = let values: Result<Vec<_>, _> =
q_values.iter().map(|v| v.to_str()).collect(); q_values.iter().map(|v| v.to_string()).collect();
subqueries.push(Box::new(data::value.eq_any(values?))) subqueries.push(Box::new(data::value.eq_any(values?)))
} }
QueryComponent::Contains(q_value) => { QueryComponent::Contains(q_value) => {
@ -583,9 +570,9 @@ pub fn query_entries<C: Connection<Backend = Sqlite>>(
}; };
query = match entry_query.value { query = match entry_query.value {
QueryComponent::Exact(q_value) => query.filter(value.eq(q_value.to_str()?)), QueryComponent::Exact(q_value) => query.filter(value.eq(q_value.to_string()?)),
QueryComponent::In(q_values) => { QueryComponent::In(q_values) => {
let values: Result<Vec<_>, _> = q_values.into_iter().map(|v| v.to_str()).collect(); let values: Result<Vec<_>, _> = q_values.into_iter().map(|v| v.to_string()).collect();
query.filter(value.eq_any(values?)) query.filter(value.eq_any(values?))
} }
QueryComponent::Contains(q_value_string) => { QueryComponent::Contains(q_value_string) => {
@ -617,7 +604,7 @@ pub fn insert_entry<C: Connection<Backend = Sqlite>>(
identity: entry.hash()?.0, identity: entry.hash()?.0,
target: entry.target.encode()?, target: entry.target.encode()?,
key: entry.key, key: entry.key,
value: entry.value.to_str()?, value: entry.value.to_string()?,
}; };
Ok(diesel::insert_into(data::table) Ok(diesel::insert_into(data::table)

View File

@ -1,10 +1,11 @@
use actix::prelude::*; use actix::prelude::*;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use filebuffer::FileBuffer; use filebuffer::FileBuffer;
use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
use tiny_keccak::{Hasher, KangarooTwelve}; use tiny_keccak::{Hasher, KangarooTwelve};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Hash(pub Vec<u8>); pub struct Hash(pub Vec<u8>);
impl AsRef<[u8]> for Hash { impl AsRef<[u8]> for Hash {

View File

@ -53,11 +53,11 @@ pub async fn get_object(
debug!("{:?}", response); debug!("{:?}", response);
let mut result: HashMap<String, serde_json::Value> = HashMap::new(); let mut result: HashMap<String, Entry> = HashMap::new();
for entry in response.map_err(error::ErrorInternalServerError)? { for entry in response.map_err(error::ErrorInternalServerError)? {
result.insert( result.insert(
encode(entry.hash().map_err(ErrorInternalServerError)?), encode(entry.hash().map_err(ErrorInternalServerError)?),
entry.as_json(), entry,
); );
} }
Ok(HttpResponse::Ok().json(result)) Ok(HttpResponse::Ok().json(result))
@ -90,12 +90,7 @@ pub async fn list_hier(
.await .await
.map_err(ErrorNotFound)?; // todo: 500 if actual error occurs .map_err(ErrorNotFound)?; // todo: 500 if actual error occurs
Ok(HttpResponse::Ok().json( Ok(HttpResponse::Ok().json(entries))
entries
.iter()
.map(Entry::as_json)
.collect::<serde_json::Value>(),
))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -111,12 +106,7 @@ pub async fn get_lookup(
let connection = state.db_pool.get().map_err(ErrorInternalServerError)?; let connection = state.db_pool.get().map_err(ErrorInternalServerError)?;
let response = lookup_by_filename(&connection, info.query).map_err(ErrorInternalServerError)?; let response = lookup_by_filename(&connection, info.query).map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json( Ok(HttpResponse::Ok().json(response))
response
.iter()
.map(Entry::as_json)
.collect::<serde_json::Value>(),
))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -135,12 +125,7 @@ pub async fn get_query(
let in_query = Query::from_sexp(&sexp).map_err(ErrorInternalServerError)?; let in_query = Query::from_sexp(&sexp).map_err(ErrorInternalServerError)?;
let result = query(&connection, in_query).map_err(ErrorInternalServerError)?; let result = query(&connection, in_query).map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json( Ok(HttpResponse::Ok().json(result))
result
.iter()
.map(Entry::as_json)
.collect::<serde_json::Value>(),
))
} }
#[post("/api/refresh")] #[post("/api/refresh")]