Browse Source

Initial Commit

master
Sarah Jamie Lewis 1 month ago
commit
05f01c5887
6 changed files with 520 additions and 0 deletions
  1. +6
    -0
      .gitignore
  2. +174
    -0
      Cargo.lock
  3. +9
    -0
      Cargo.toml
  4. +23
    -0
      src/account.rs
  5. +221
    -0
      src/main.rs
  6. +87
    -0
      src/transaction.rs

+ 6
- 0
.gitignore View File

@@ -0,0 +1,6 @@
/target
**/*.rs.bk
bisq
ledger.dat
.idea/
openpriv

+ 174
- 0
Cargo.lock View File

@@ -0,0 +1,174 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "bstr"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "chrono"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "coblynau"
version = "0.1.0"
dependencies = [
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "csv"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "csv-core"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "libc"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "memchr"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "num-integer"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "regex-automata"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "ryu"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "time"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[metadata]
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9"
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

+ 9
- 0
Cargo.toml View File

@@ -0,0 +1,9 @@
[package]
name = "coblynau"
version = "0.1.0"
authors = ["Sarah Jamie Lewis <sarah@openprivacy.ca>"]
edition = "2018"

[dependencies]
csv = "1.1"
chrono = "0.4.10"

+ 23
- 0
src/account.rs View File

@@ -0,0 +1,23 @@
use crate::transaction::Transaction;

pub struct Account {
pub transactions: Vec<Transaction>
}

impl Account {
pub fn new() -> Account {
Account {
transactions: vec!{}
}
}

pub fn add_transaction(&mut self, transaction: Transaction) {
self.transactions.push(transaction);
}

pub fn print(&self) {
for transaction in self.transactions.iter() {
transaction.print();
}
}
}

+ 221
- 0
src/main.rs View File

@@ -0,0 +1,221 @@
use std::error::Error;
use std::fs::File;
use chrono::{NaiveDateTime};
use crate::transaction::{Transaction, Input, Output};

pub mod account;
pub mod transaction;
use std::env;
use std::borrow::Borrow;

fn parse_bisq_transactions(account:&String) -> Result<Vec<Transaction>, Box<dyn Error>>{
// Build the CSV reader and iterate over each record.
let file = File::open(String::from(account) + "/bisq/transactions.csv")?;
let mut rdr = csv::Reader::from_reader(file);
let mut trade_transactions: Vec<Transaction>= vec!();
for result in rdr.records() {
// The iterator yields Result<StringRecord, Error>, so we check the
// error here..
let record = result?;
let details = &record[1];
let date = &record[0];
let date_time = NaiveDateTime::parse_from_str(date,"%b %e, %Y %I:%M:%S %p")?;
let amount:f64 = record[4].parse().unwrap();

if details.contains("Maker and tx fee") {
let mut transaction = Transaction::new(date_time, format!("Maker Fee"));
transaction.add_input(Input{
source: String::from("Assets:Trading"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Expenses:MakerFee"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_comment(record[2].to_string());
trade_transactions.push(transaction);
}

if details.contains("Taker and tx fee") {
let mut transaction = Transaction::new(date_time, format!("Taker Fee"));
transaction.add_input(Input{
source: String::from("Assets:Trading"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Expenses:TakerFee"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_comment(record[2].to_string());
trade_transactions.push(transaction);
}

if details.contains("Multisig deposit") {
let mut transaction = Transaction::new(date_time, format!("Multisig Deposit"));
transaction.add_input(Input{
source: String::from("Assets:Trading"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Expenses:TradingDeposit"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_comment(record[2].to_string());
trade_transactions.push(transaction);
}

if details.contains("Multisig payout") {
let mut transaction = Transaction::new(date_time, format!("Multisig Payout"));
transaction.add_input(Input{
source: String::from("Expenses:TradingDeposit"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Assets:Trading"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_comment(record[2].to_string());
trade_transactions.push(transaction);
}

if details.contains("Withdrawn from wallet") {
let mut transaction = Transaction::new(date_time, format!("Widthdrawal from Bisq Trading Wallet"));
transaction.add_input(Input{
source: String::from("Assets:Trading"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Assets:General"),
amount: ((amount.abs() * 100.0).round() / 100.0),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Expenses:TxFee"),
amount: ((amount.abs() - ((amount.abs() * 100.0).round() / 100.0)) * 100000000.0).round() / 100000000.0,
value: String::from("BTC")
});
transaction.add_comment(record[2].to_string());
trade_transactions.push(transaction);
}


if details.contains("Received funds") {
let mut transaction = Transaction::new(date_time, format!("Deposit into Bisq Trading Wallet"));
transaction.add_input(Input{
source: String::from("Assets:General"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_output(Output{
destination: String::from("Assets:Trading"),
amount: amount.abs(),
value: String::from("BTC")
});
transaction.add_comment(record[2].to_string());
trade_transactions.push(transaction);
}

}
return Ok(trade_transactions);
}


fn parse_bisq_trades(account:&String) -> Result<Vec<Transaction>, Box<dyn Error>>{
// Build the CSV reader and iterate over each record.
let file = File::open(String::from(account) + "/bisq/tradeHistory.csv")?;
let mut rdr = csv::Reader::from_reader(file);
let mut trade_transactions: Vec<Transaction>= vec!();
for result in rdr.records() {
// The iterator yields Result<StringRecord, Error>, so we check the
// error here..
let record = result?;
let id = &record[0];
let date = &record[1];
let date_time = NaiveDateTime::parse_from_str(date,"%b %e, %Y %I:%M:%S %p")?;
let bought = &record[4];
let sold = &record[5];

let mut transaction = Transaction::new(date_time, format!("Bisq Trade {}", id));
transaction.add_input(Input{
source: String::from("Assets:Trading"),
amount: sold.replace(" ZEC","").parse().unwrap(),
value: String::from("ZEC")
});
transaction.add_output(Output{
destination: String::from("Expenses:TradingDeposit"),
amount: bought.parse().unwrap(),
value: String::from("BTC")
});
trade_transactions.push(transaction);

}
return Ok(trade_transactions);
}


fn parse_donations(account:&String) -> Result<Vec<Transaction>, Box<dyn Error>>{
// Build the CSV reader and iterate over each record.
let file = File::open(String::from(account) + "/donations.csv")?;
let mut rdr = csv::Reader::from_reader(file);
let mut trade_transactions: Vec<Transaction>= vec!();
for result in rdr.records() {
// The iterator yields Result<StringRecord, Error>, so we check the
// error here..
let record = result?;
let date = &record[0];
let date_time = NaiveDateTime::parse_from_str(date,"%b %e, %Y %I:%M:%S %p")?;
let details = &record[1];
let amount = &record[2];
let denomination = &record[3];
let account = &record[4];


let mut transaction = Transaction::new(date_time, format!("{}", details));
transaction.add_input(Input{
source: String::from("Income:Donations"),
amount: amount.parse().unwrap(),
value: String::from(denomination),
});
transaction.add_output(Output{
destination: String::from(account),
amount: amount.parse().unwrap(),
value: String::from(denomination),
});
trade_transactions.push(transaction);

}
return Ok(trade_transactions);
}

fn main() {

let args: Vec<String> = env::args().collect();
let mut ledger = account::Account::new();

let donations = parse_donations(args[1].borrow()).unwrap();
for transaction in donations.iter() {
ledger.add_transaction(transaction.clone());
}

let trade_transactions = parse_bisq_trades(args[1].borrow()).unwrap();
for transaction in trade_transactions.iter() {
ledger.add_transaction(transaction.clone());
}

let bisq_transactions = parse_bisq_transactions(args[1].borrow()).unwrap();
for transaction in bisq_transactions.iter() {
ledger.add_transaction(transaction.clone());
}

ledger.transactions.sort_by_key(|transaction|transaction.date);
ledger.print();
}

+ 87
- 0
src/transaction.rs View File

@@ -0,0 +1,87 @@
use chrono::{NaiveDateTime, Datelike};

#[derive(Clone)]
pub struct Input {
pub source: String,
pub amount:f64,
pub value: String
}

#[derive(Clone)]
pub struct Output {
pub destination: String,
pub amount:f64,
pub value: String
}

impl Output {
pub fn clone(&self) -> Self {
Output {
destination: self.destination.clone(),
amount: self.amount,
value: self.value.clone()
}
}
}

pub struct Transaction {
pub date: NaiveDateTime,
description: String,
inputs: Vec<Input>,
outputs:Vec<Output>,
comments:Vec<String>
}

impl Transaction {
pub fn new(date:NaiveDateTime, description:String) -> Transaction {
Transaction {
date,
description,
inputs:vec!(),
outputs:vec!(),
comments:vec!()
}
}

pub fn add_input(&mut self, input: Input) {
self.inputs.push(input);
}

pub fn add_output(&mut self, output: Output) {
self.outputs.push(output);
}

pub fn add_comment(&mut self, comment: String) {
self.comments.push(comment);
}

pub fn clone(&self) -> Self {
Transaction {
date: self.date,
description: self.description.clone(),
inputs: self.inputs.to_vec(),
outputs: self.outputs.to_vec(),
comments: self.comments.to_vec(),
}
}


pub fn print(&self) {
for comment in self.comments.iter() {
println!(";{}", comment);
}
println!("{}/{}/{} {} * {}", self.date.date().year(),self.date.date().month(),self.date.date().day(), self.date.time(), self.description);
for input in self.inputs.iter() {
println!("\t {} \t\t -{} {}", input.source, input.amount, input.value);
}
for output in self.outputs.iter() {
println!("\t {} \t\t {} {}", output.destination, output.amount, output.value);
}
println!("");
println!("");
}


}



Loading…
Cancel
Save