mnml/server/src/mtx.rs
2019-07-10 18:39:32 +10:00

196 lines
5.1 KiB
Rust

use std::convert::TryFrom;
use uuid::Uuid;
// use rand::prelude::*;
use serde_cbor::{from_slice};
use postgres::transaction::Transaction;
use failure::Error;
use failure::err_msg;
use account;
use account::Account;
use construct::{Construct, construct_select, construct_write};
use img::{img_molecular_write};
pub const FREE_MTX: [MtxVariant; 2] = [
MtxVariant::Rename,
MtxVariant::Reimage,
];
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
pub enum MtxVariant {
Rename,
Reimage,
ArchitectureMolecular,
ArchitectureInvader,
}
impl MtxVariant {
fn to_sql(&self) -> String {
format!("{:?}", *self)
}
}
// could use postgres-derive
// but enums in pg are a bit of a pain
impl TryFrom<String> for MtxVariant {
type Error = Error;
fn try_from(v: String) -> Result<MtxVariant, Error> {
match v.as_ref() {
"Rename" => Ok(MtxVariant::Rename),
"Reimage" => Ok(MtxVariant::Reimage),
"ArchitectureMolecular" => Ok(MtxVariant::ArchitectureMolecular),
"ArchitectureInvader" => Ok(MtxVariant::ArchitectureInvader),
_ => Err(format_err!("mtx variant not found variant={:?}", v)),
}
}
}
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
pub struct Mtx {
id: Uuid,
account: Uuid,
variant: MtxVariant,
}
pub fn apply(tx: &mut Transaction, account: &Account, variant: MtxVariant, construct_id: Uuid) -> Result<Vec<Construct>, Error> {
let mtx = select(tx, variant, account.id)?;
let construct = construct_select(tx, construct_id, account.id)?;
match mtx.variant {
MtxVariant::Reimage => reimage(tx, construct)?,
_ => unimplemented!(),
};
account::debit(tx, account.id, 1)?;
account::account_constructs(tx, account)
}
pub fn reimage(tx: &mut Transaction, mut construct: Construct) -> Result<Construct, Error> {
construct = construct.new_img();
img_molecular_write(construct.img)?;
construct = construct_write(tx, construct)?;
Ok(construct)
}
pub fn select(tx: &mut Transaction, variant: MtxVariant, account: Uuid) -> Result<Mtx, Error> {
let query = "
SELECT id, account, variant
FROM mtx
WHERE account = $1
AND variant = $2
FOR UPDATE;
";
let result = tx
.query(query, &[&account, &variant.to_sql()])?;
if let Some(row) = result.iter().next() {
let id: Uuid = row.get(0);
let account: Uuid = row.get(1);
let v_str: String = row.get(2);
let variant = MtxVariant::try_from(v_str)?;
Ok(Mtx { id, account, variant })
} else {
Err(format_err!("mtx not found account={:?} variant={:?}", account, variant))
}
}
impl Mtx {
pub fn new(variant: MtxVariant, account: Uuid) -> Mtx {
match variant {
_ => Mtx { id: Uuid::new_v4(), account, variant },
// MtxVariant::ArchitectureInvader => Mtx { id: Uuid::new_v4(), account, variant: self },
// MtxVariant::ArchitectureMolecular => Mtx { id: Uuid::new_v4(), account, variant: self },
}
}
pub fn account_list(tx: &mut Transaction, account: Uuid) -> Result<Vec<Mtx>, Error> {
let query = "
SELECT data, id
FROM mtx
WHERE account = $1;
";
let result = tx
.query(query, &[&account])?;
let values = result.into_iter().filter_map(|row| {
let bytes: Vec<u8> = row.get(0);
// let id: Uuid = row.get(1);
match from_slice::<Mtx>(&bytes) {
Ok(i) => Some(i),
Err(e) => {
warn!("{:?}", e);
None
}
}
}).collect::<Vec<Mtx>>();
return Ok(values);
}
pub fn delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> {
let query = "
DELETE
FROM mtx
WHERE id = $1;
";
let result = tx
.execute(query, &[&id])?;
if result != 1 {
return Err(format_err!("unable to delete mtx {:?}", id));
}
info!("mtx deleted {:?}", id);
return Ok(());
}
pub fn insert(&self, tx: &mut Transaction) -> Result<&Mtx, Error> {
let query = "
INSERT INTO mtx (id, account, variant)
VALUES ($1, $2, $3)
RETURNING id, account;
";
let result = tx
.query(query, &[&self.id, &self.account, &self.variant.to_sql()])?;
result.iter().next().ok_or(err_msg("mtx not written"))?;
info!("wrote mtx {:?}", self);
return Ok(self);
}
// pub fn update(&self, tx: &mut Transaction) -> Result<&Mtx, Error> {
// let query = "
// UPDATE mtx
// SET data = $1, updated_at = now()
// WHERE id = $2
// RETURNING id, data;
// ";
// let result = tx
// .query(query, &[&self.id, &to_vec(self)?])?;
// if let None = result.iter().next() {
// return Err(err_msg("mtx not written"));
// }
// info!("wrote mtx {:?}", self);
// return Ok(self);
// }
}