Skip to content

Commit

Permalink
command skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
d2ci8xc5 committed Nov 23, 2019
1 parent 5f56d6c commit 1c93988
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 110 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
**/*.rs.bk
**/*.json
8 changes: 4 additions & 4 deletions src/account.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use std::cmp;
use std::fmt;
use serde::{Serialize, Deserialize};

/// Accounts represent a pool of funds
#[derive(Serialize, Deserialize, Clone, Debug)]
Expand All @@ -12,7 +12,7 @@ pub struct Account {
impl Account {
pub fn new(id: i32, name: String, balance: i32) -> Result<Account, &'static str> {
return match balance {
b if b >= 0 => Ok(Account {id, name, balance}),
b if b >= 0 => Ok(Account { id, name, balance }),
b if b < 0 => Err("invalid balance ( less than zero)"),
_ => Err("unknown error"),
};
Expand Down Expand Up @@ -49,15 +49,15 @@ mod tests {
fn test_equality() {
let acc_0 = Account::new(0, String::from("acc_0"), 100);
let acc_eq_0 = Account::new(0, String::from("acc_0"), 100);
assert_eq!(acc_0, acc_eq_0);
assert_eq!(acc_0, acc_eq_0);
}

#[test]
fn test_negative_balance_init() {
match Account::new(0, String::from("acc_0"), -100) {
Ok(acc) => assert!(false),
Err(reason) => (),
_ => assert!(false),
_ => assert!(false),
}
}
}
39 changes: 18 additions & 21 deletions src/ledger.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::transaction::Transaction;
use crate::account::Account;
use serde::{Serialize, Deserialize};
use crate::transaction::Transaction;
use serde::{Deserialize, Serialize};
use serde_json;
use std::fs::File;
use std::io::{Write, Read, Seek, SeekFrom};
use std::io::{Read, Seek, SeekFrom, Write};

/// Ledger represents a collection of accounts and transactions.
#[derive(Serialize, Deserialize, Debug)]
Expand All @@ -15,12 +15,14 @@ pub struct Ledger {
impl Ledger {
/// Create a new ledger
pub fn new(accounts: Vec<Account>, transactions: Vec<Transaction>) -> Ledger {
Ledger { accounts, transactions }
Ledger {
accounts,
transactions,
}
}

/// Add an account to the ledger
fn add_account(&mut self, acc: Account) -> bool {

// Only allow addition of unique accounts to ledger
for account in self.accounts.iter() {
if acc.id == account.id {
Expand All @@ -34,7 +36,6 @@ impl Ledger {
// TODO: only allow added accounts to tx
/// Write a transaction to the ledger
fn add_transaction(&mut self, tx: Transaction) -> bool {

// Only allow addition of unique txids to ledger
for transaction in self.transactions.iter() {
if tx.id == transaction.id {
Expand All @@ -45,28 +46,26 @@ impl Ledger {
return true;
}

/// Serialize ledger to disk
/// Serialize ledger to disk
pub fn save(&self, file: &mut File) {
let serialized = serde_json::to_string(&self).unwrap();
println!("\n{}\n", serialized);
write!(file, "{}", serialized).unwrap();
let serialized = serde_json::to_string(&self).unwrap();
write!(file, "{}", serialized).unwrap();
}

/// Serialize ledger from disk
/// Serialize ledger from disk
pub fn load(&self, file: &mut File) -> Ledger {
file.seek(SeekFrom::Start(0)).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
let serde_ledger: Ledger = serde_json::from_str(&buf.to_string()).unwrap();
let serde_ledger: Ledger = serde_json::from_str(&buf.to_string()).unwrap();
return serde_ledger;
}

}

#[cfg(test)]
mod tests {
use super::*;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use serde_json;

#[test]
Expand All @@ -78,13 +77,12 @@ mod tests {

#[test]
fn test_serde_disk() {

// Setup ledger state
let acc_0 = Account::new(0, String::from("acc_0"), 100).unwrap();
let acc_1 = Account::new(1, String::from("acc_1"), 100).unwrap();

let tuple_0 = (acc_0.clone(), 100);
let tuple_1 = (acc_1.clone(), -100);
let tuple_0 = (acc_0.clone(), 100);
let tuple_1 = (acc_1.clone(), -100);
let mut vec: Vec<(Account, i32)> = Vec::new();
vec.push(tuple_0);
vec.push(tuple_1);
Expand All @@ -96,14 +94,13 @@ mod tests {

// Serialize ledger to disk
let mut tmp_file: File = tempfile::tempfile().unwrap();
ledger.save(&mut tmp_file);
ledger.save(&mut tmp_file);
tmp_file.seek(SeekFrom::Start(0)).unwrap();
let mut buf = String::new();
tmp_file.read_to_string(&mut buf).unwrap();
assert_eq!(serde_json::to_string(&ledger).unwrap(), buf);

// Deserialize ledger from disk
let serde_ledger: Ledger = serde_json::from_str(&buf.to_string()).unwrap();
// Deserialize ledger from disk
let serde_ledger: Ledger = serde_json::from_str(&buf.to_string()).unwrap();
}

}
128 changes: 67 additions & 61 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,88 @@ pub mod account;
pub mod ledger;
pub mod transaction;

use crate::ledger::Ledger;
use account::Account as acc;
use account::Account;
use crate::ledger::Ledger;
use shrust::{Shell, ShellIO};
use std::io;
use std::io::prelude::*;
use transaction::Transaction;

/// Initial greeting to the program
pub fn print_header() {
println!("ledger system, type \"help\" for commands");
}

pub fn run_loop() {
let mut next_txid = 0i32;
let mut next_accid = 0i32;

let main_accounts: Vec<Account> = Vec::new();
let main_transactions: Vec<Transaction> = Vec::new();
let main_ledger = Ledger::new(main_accounts, main_transactions);
/// Help command
pub fn print_help() {
println!(
"\nsl\t\t: Save ledger state to disk\n\
ll\t\t: Load ledger state from disk\n\
ca\t\t: Create account\n\
ct\t\t: Create transaction\n\
la\t\t: Print account\n\
lt\t\t: Print transaction\n\
quit\t\t: Exit ledger application saving ledger state to disk\n\
nsquit\t\t: Exit ledger application without saving ledger state to disk\n\
help\t\t: Print this help text\n\n\
NOTE: optional arguments are annotated with [optional] \
and required arguments with <required>
"
);
}

let v = Vec::new();
let mut shell = Shell::new(v);
/// Program input loop
pub fn run_loop() {
let mut next_txid = 0i32;
let mut next_accid = 0i32;

let main_ledger = Ledger::new(Vec::new(), Vec::new());
//let main_file;
print_header();
/// Create account
shell.new_command(
"ca <account name> <account balance>",
"Create account",
1,
|io, next_acc, s| {
// Parse CLI args
let name: String = s[0].to_string();
let balance: i32 = s[1].parse::<i32>().unwrap();
let account: Account = Account::new(0, name, balance).unwrap();

Ok(())
},
);
let input = io::stdin();

/// Create transaction
shell.new_command(
"ct <date> <account_name_0> <amount_0> ... <account_name_x> <amount_x>",
"Create account",
1,
|io, v, s| {
for i in 0..s.len() {}
Ok(())
},
);
loop {
let mut buffer = String::new();
print!("> ");
io::stdout().flush().unwrap();
if input.read_line(&mut buffer).is_err() {
break;
}
let args: Vec<&str> = buffer.trim_end().split(' ').collect();
match args[0] {
"help" => {
print_help();
}
"sl" => { // Save ledger

}
"ll" => { // Load ledger

/// List account balances
shell.new_command(
"la [account_name] | [account_id]",
"List account balances",
1,
|io, v, s| Ok(()),
);
}
"ca" => { // Create account

/// List transactions
shell.new_command(
"lt [account_name][account_id]",
"List transactions",
1,
|io, v, s| Ok(()),
);
}
"ct" => { // Create transaction

shell.new_command("push", "Add string to the list", 1, |io, v, s| {
writeln!(io, "Pushing {}", s[0])?;
v.push(s[0].to_string());
Ok(())
});
shell.new_command_noargs("list", "List strings", |io, v| {
for s in v {
writeln!(io, "{}", s)?;
}
Ok(())
});
}
"la" => { // List account balance

}
"lt" => { // List transactions

shell.run_loop(&mut ShellIO::default());
}
"quit" => { // Quit

}
"nsquit" => { // No save quit

}
"" => {

}
_ => {
println!("unknown command, type \"help\" for a list of availiable commands");
}
}
}
}
39 changes: 19 additions & 20 deletions src/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::account::Account;
use chrono::prelude::*;
use std::fmt;
use chrono::NaiveDate;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use std::fmt;

/// Transactions represent the transfer of funds between Accounts.
/// All transactions are required to have a net zero balance.
Expand All @@ -27,7 +27,7 @@ impl Transaction {
}
sumation += entry.1;
}

// Transaction must have net zero balance
if sumation != 0 {
return Err("Transaction is not balanced");
Expand All @@ -37,13 +37,9 @@ impl Transaction {
match NaiveDate::parse_from_str(&date.to_string(), "%Y/%m/%d") {
Ok(date) => (),
Err(chrono_except) => return Err("Invalid date"),
}
}

return Ok(Transaction {
id,
date,
entries,
});
return Ok(Transaction { id, date, entries });
}
}

Expand All @@ -60,25 +56,28 @@ mod tests {
#[test]
fn test_transaction_new() {
// format (Account, balance to move)
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), -100);
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), -100);
let mut vec: Vec<(Account, i32)> = Vec::new();
vec.push(tuple_0);
vec.push(tuple_1);
match Transaction::new(0, String::from("2019/03/20"), vec) {
Ok(tx) => (),
Err(reason) => {println!("{}", reason); assert!(false)},
Err(reason) => {
println!("{}", reason);
assert!(false)
}
_ => assert!(false),
}

// TODO: Check fields are valid
// TODO: Check fields are valid
}

#[test]
fn test_transaction_unbalanced() {
// format (Account, balance to move)
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), 100);
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), 100);
let mut vec: Vec<(Account, i32)> = Vec::new();
vec.push(tuple_0);
vec.push(tuple_1);
Expand All @@ -92,8 +91,8 @@ mod tests {
#[test]
fn test_invalid_date() {
// format (Account, balance to move)
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), 100);
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), 100);
let mut vec: Vec<(Account, i32)> = Vec::new();
vec.push(tuple_0);
vec.push(tuple_1);
Expand All @@ -109,12 +108,12 @@ mod tests {
#[test]
fn test_invalid_date_format() {
// format (Account, balance to move)
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), 100);
let tuple_0 = (Account::new(0, String::from("acc_0"), 100).unwrap(), 100);
let tuple_1 = (Account::new(1, String::from("acc_1"), 100).unwrap(), 100);
let mut vec: Vec<(Account, i32)> = Vec::new();
vec.push(tuple_0);
vec.push(tuple_1);

let invalid_date = String::from("2019-22-22");
match Transaction::new(0, invalid_date, vec) {
Ok(tx) => assert!(false),
Expand Down
Loading

0 comments on commit 1c93988

Please sign in to comment.