// #![deny(warnings)] #![crate_name = "windmill"] #![allow(non_snake_case)] extern crate rusty_money; use std::collections::HashMap; use std::convert::TryInto; use std::error; use std::fmt; use std::default::Default; use std::cmp::Ordering; use chrono::prelude::*; use rand::distributions::{Distribution, Uniform}; // use serde_derive::{Deserialize, Serialize}; use rusty_money::{Money, define_currency_set}; define_currency_set!( chain { INV:{ code: "INV", exponent: 2, locale: Locale::EnUs, minor_units: 100, name: "INV", symbol: "𝒾", symbol_first: false, } } ); /// Enum of built in Error types #[derive(Debug,PartialEq,Clone, Copy)] pub enum WindmillError { Incomplete, BadRequest, ClosedAuction, } impl fmt::Display for WindmillError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { WindmillError::Incomplete => write!(f,"User profile is incomplete."), WindmillError::BadRequest => write!(f,"Bad Request"), WindmillError::ClosedAuction => write!(f,"Closed Auction"), } } } impl error::Error for WindmillError { fn description(&self) -> &str { match *self { WindmillError::Incomplete => "Incomplete user profile.", WindmillError::BadRequest => "Bad Request.", WindmillError::ClosedAuction => "Auction is closed.", } } fn source(&self) -> Option<&(dyn error::Error + 'static)> { // Generic error, underlying cause isn't tracked. None } } #[derive(Clone)] struct RanIDs { radix: u32, pub ids: Vec, pub dist: Uniform, } impl RanIDs { fn new() -> Self { RanIDs { radix: 36, ids: Vec::new(), dist: Uniform::from(1..36_u32.pow(5)-1), } } fn get(&mut self) -> Result { let mut rng = rand::thread_rng(); let mut x: u32 = self.dist.sample(&mut rng); while self.ids.contains(&x) { x = self.dist.sample(&mut rng); } self.ids.push(x); let mut result: [char;5] = [std::char::from_digit(0,self.radix).unwrap(); 5]; let mut i = 5; while i > 0 { let m = x % self.radix; x = x / self.radix; result[i-1] = std::char::from_digit(m, self.radix).unwrap(); if x == 0 { return Ok(result.iter().collect()); } i -= 1; } Err(WindmillError::Incomplete) } } impl Default for RanIDs { fn default() -> RanIDs { RanIDs::new() } } #[derive(Debug,PartialEq,Clone)] struct Asset { name: String, address: String, } #[derive(Debug,Eq,Clone)] pub struct Bid { pub id: String, pub quantity: u64, pub price: Money<'static, chain::Currency>, pub timestamp: DateTime, } impl Ord for Bid { fn cmp(&self, other: &Self) -> Ordering { let ord = (self.price.clone()/self.quantity).cmp(&(other.price.clone()/other.quantity)); match ord { Ordering::Equal => return other.timestamp.cmp(&self.timestamp), _ => return ord, } } } impl PartialOrd for Bid { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl PartialEq for Bid { fn eq(&self, other: &Self) -> bool { (self.price.clone()/self.quantity) == (other.price.clone()/other.quantity) && other.timestamp == self.timestamp } } #[derive(Debug,PartialEq,Clone)] pub enum AuctionResult { Success {quantity: u64, price: Money<'static, chain::Currency>}, InProgress, Failure, } #[derive(Debug,PartialEq,Clone,Copy)] pub enum BidResult { PastTime, Submitted, } #[derive(Clone)] pub struct Auction { time: DateTime, shares: u64, bids: HashMap, pub results: HashMap, ids: RanIDs, } impl Auction { pub fn new(time: DateTime, shares: u64) -> Self { Auction { time, shares, bids: HashMap::default(), results: HashMap::default(), ids: RanIDs::default(), } } pub fn join(&mut self) -> Result { let nt = self.ids.get()?; self.results.insert(nt.clone(),AuctionResult::InProgress); Ok(nt) } pub fn bid(&mut self,bid: Bid) -> Result { let Bid{id, quantity: _, price: _, timestamp} = bid.clone(); if let Some(res) = self.results.get(&id.clone()){ let local_res = res.clone(); match local_res { AuctionResult::InProgress => { if timestamp < self.time { self.bids.insert(id.clone(),bid); return Ok(BidResult::Submitted); } else { self.tabulate(); return Ok(BidResult::PastTime); } } _ => return Ok(BidResult::PastTime), } } else { return Err(WindmillError::BadRequest); } } pub fn tabulate(&mut self) { let mut final_results: Vec = self.bids.values().cloned().collect(); final_results.sort_unstable(); final_results.reverse(); let mut price = Money::from_minor(0,chain::INV); let mut counter = 0usize; let mut counted = 0i64; let mut winners: HashMap = HashMap::new(); while counted < self.shares.try_into().unwrap() && counter < final_results.len() { let mut bb = final_results[counter].clone(); price = bb.price.clone()/bb.quantity; counted += bb.quantity as i64; if counted > self.shares.try_into().unwrap() { bb.quantity = ((self.shares as i64) - counted + (bb.quantity as i64)) as u64; } winners.insert(bb.id.clone(),bb); counter += 1; } if counted >= self.shares.try_into().unwrap() { for (key, val) in self.results.iter_mut() { if let Some(bb) = winners.get(&key.clone()){ *val = AuctionResult::Success{quantity: bb.quantity, price: bb.quantity*price.clone()}; } else { *val = AuctionResult::Failure; } } } else { for (_, val) in self.results.iter_mut() { *val = AuctionResult::Failure; } } } }