1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
// import custom types, so they can be used.
use crate::transaction::Transaction;
// import serde, for easy serialization and deserialization
use serde::{ Serialize, Deserialize };
// import uuid crate, to easily generate ids
use uuid::Uuid;
// import File and io stuff, so that data can be loaded from a file.
use std::{fs::File, fmt, io::{ self, Read } };
// import serde_json crate, to facilitate deserialization from JSON.
use serde_json;
/// Represents an entry in a check register
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialOrd, Ord)]
pub struct Record {
/// record identifier.
#[serde(default = "default_id")]
pub id: String,
/// the record's transaction
pub transaction: Transaction,
}
impl Record {
/// create a new empty Record object.
pub fn new() -> Record {
Record {
id: default_id(),
transaction: Transaction::new()
}
}
/**
* Create a record object with given values.
* If id is an empty String, an id will be generated for you.
* # Example
* ```let record = Record::from("FF04C3DC-F0FE-472E-8737-0F4034C049F0", Transaction::from(Some("2021-7-8"), Some(1260 as u32), String::from("Sam Hill Credit Union"), String::from("Open Account"), OrderedFloat::<f64>(500 as f64), TransactionType::DEPOSIT, false).unwrap());```
*/
pub fn from(id: &str, transaction: Transaction) -> Record {
Record {
id: {
if id.is_empty() {
default_id()
} else {
String::from(id)
}
},
transaction
}
}
/**load vector containing Records from a given file path.
* This method attempts to read a file containing record data, returning a vector if successful, but will give out an error if something goes wrong, either with loading the file or parsing it.
*/
pub fn from_file(f: &str) -> Result<Vec<Record>, String> {
match file_contents_from(f) {
Ok(content) => {
match serde_json::from_str::<Vec<Record>>(&content) {
Ok(decoded_records) => Ok(decoded_records),
Err(error) => Err(format!("{}", error))
}
},
Err(error) => Err(format!("{}", error))
}
}
/// create a record directly from a string.
pub fn from_string(s: &str) -> Record {
let components: Vec<&str> = s.split('\t').collect();
let mut transaction_components = components.clone();
transaction_components.remove(0);
let transaction_string: String = transaction_components.iter().map(|e| e.to_string() + "\t").collect();
Record {
id: if components[0].to_string().is_empty() {
default_id()
} else {
components[0].to_string()
},
transaction: Transaction::from_string(&transaction_string)
}
}
/** this method does the same thing as from_file() and operates like it,
but is for use with tsv files, while the former assumes json. */
pub fn from_tsv_file(f: &str) -> Result<Vec<Record>, String> {
match file_contents_from(f) {
Ok(content) => {
let lines: Vec<&str> = content.lines().map(|l| l).collect();
let records: Vec<Record> = lines.iter().map(|line| Record::from_string(line)).collect();
Ok(records)
},
Err(error) => Err(format!("{}", error))
}
}
/// presents a string version of the record.
pub fn to_string(&self) -> String {
format!("{}\t{}", self.id, self.transaction)
}
}
// apply trait to get item to be representable by string
impl fmt::Display for Record {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
// apply trait necessary to check for equality
impl PartialEq for Record {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
fn default_id() -> String {
Uuid::new_v4().to_hyphenated().to_string()
}
fn file_contents_from(f: &str) -> Result<String, io::Error> {
let mut file_content = String::new();
File::open(f)?.read_to_string(&mut file_content)?;
Ok(file_content)
}