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 for MtxVariant { type Error = Error; fn try_from(v: String) -> Result { 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, 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 = 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 { 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, 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 = row.get(0); // let id: Uuid = row.get(1); match from_slice::(&bytes) { Ok(i) => Some(i), Err(e) => { warn!("{:?}", e); None } } }).collect::>(); 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); // } }