use std::{collections::HashMap, fs, fs::File}; use rust_util::{XResult, information, simple_error}; use serde::{Serialize, Deserialize}; use crate::tx::Transaction; #[derive(Debug, Serialize, Deserialize)] pub struct CreditContract { pub name: String, pub credit_limit: u32, pub issue_amount: u32, pub admin: String, pub credit: HashMap, } impl CreditContract { pub fn new(tx: &Transaction, name: &str, credit_limit: u32) -> XResult { let c = Self { name: name.into(), credit_limit, issue_amount: 0, admin: tx.sender.clone(), credit: HashMap::new(), }; save_credit_contract(&c, false)?; Ok(c) } pub fn load(name: &str) -> XResult { load_credit_contract(name) } pub fn issue(&mut self, tx: &Transaction, receiver: &str, credit: u32) -> XResult<()> { if &self.admin != &tx.sender { return simple_error!("Current user is not admin, {} vs {:?}", self.admin, tx.sender); } if credit == 0 { return simple_error!("Cannot issue 0 credit!"); } if self.issue_amount + credit > self.credit_limit { return simple_error!("Issue too much credit, current: {}, issue: {}, limit: {}, left: {}", self.issue_amount, credit, self.credit_limit, self.credit_limit - self.issue_amount); } match self.credit.get_mut(receiver) { None => { self.credit.insert(receiver.to_owned(), credit); }, Some(cr) => *cr += credit, } self.issue_amount += credit; save_credit_contract(self, true)?; Ok(()) } pub fn transfer(&mut self, tx: &Transaction, receiver: &str, credit: u32) -> XResult<()> { if credit == 0 { return simple_error!("Cannot transfer 0 credit!"); } match self.credit.get_mut(&tx.sender) { None => return simple_error!("Have not enough credit: {:?}", tx.sender), Some(cr) => { if *cr >= credit { *cr -= credit; match self.credit.get_mut(receiver) { None => { self.credit.insert(receiver.to_owned(), credit); }, Some(receiver_credit) => *receiver_credit += credit, } } else { return simple_error!("Have not enough credit: {:?}", tx.sender); } }, } save_credit_contract(self, true)?; Ok(()) } pub fn query(&self, _tx: &Transaction, account: &str) -> u32 { match self.credit.get(account) { None => 0, Some(receiver_credit) => *receiver_credit, } } pub fn query_all(&self, _tx: &Transaction) -> &HashMap { &self.credit } } fn load_credit_contract(name: &str) -> XResult { let json = fs::read_to_string(name)?; serde_json::from_str(&json).map_err(|e| e.into()) } fn save_credit_contract(c: &CreditContract, overwrite: bool) -> XResult<()> { let name = &c.name; if !overwrite { if let Ok(_) = File::open(name) { return simple_error!("File exists: {}", name); } } information!("Write file: {}", name); fs::write(name, serde_json::to_string(c)?.as_bytes())?; Ok(()) }