Skip to content

Commit

Permalink
fun with pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
alensiljak committed Aug 11, 2023
1 parent 25d5be0 commit baf502e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 48 deletions.
102 changes: 70 additions & 32 deletions src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Account definition and operations
*/

use std::{collections::HashMap, vec};
use std::{collections::HashMap, ptr::addr_of, vec};

use crate::{balance::Balance, post::Post};

Expand Down Expand Up @@ -32,6 +32,22 @@ impl Account {
}
}

/// called from find_or_create.
fn create_account(&self, first: &str) -> &Account {
let mut new_account = Account::new(first);
log::debug!("Setting account parent: {:?}", self);
new_account.set_parent(self);

let self_mut = self.get_account_mut(self as *const Account as *mut Account);

self_mut.accounts.insert(first.into(), new_account);

let Some(new_ref) = self.accounts.get(first)
else {panic!("should not happen")};

new_ref
}

pub fn fullname(&self) -> &str {
// skip the master account.
if self.parent.is_null() {
Expand Down Expand Up @@ -61,27 +77,33 @@ impl Account {

fn set_fullname(&self, fullname: String) {
// alchemy?
let ptr = self as *const Account;
// let mut_ptr = ptr as *mut Account;
// let ptr = self as *const Account;
let ptr = addr_of!(*self);
let subject = self.get_account_mut(ptr);

subject.fullname = fullname;
}

/// Finds account by full name.
/// i.e. "Assets:Cash"
pub fn find_account(&mut self, name: &str) -> Option<*const Account> {
self.find_or_create(name, true)
pub fn find_account(&self, name: &str) -> Option<&Account> {
if let Some(ptr) = self.find_or_create(name, true) {
let acct = self.get_account(ptr);
return Some(acct);
} else {
return None;
}
}

/// The variant with all the parameters.
/// account_t * find_account(const string& name, bool auto_create = true);
fn find_or_create(&mut self, name: &str, auto_create: bool) -> Option<*const Account> {
pub fn find_or_create(&self, name: &str, auto_create: bool) -> Option<*const Account> {
// search for direct hit.
if let Some(found) = self.accounts.get(name) {
return Some(found);
}

// otherwise create
// otherwise search for name parts in between the `:`

let mut account: *const Account;
let first: &str;
Expand All @@ -96,23 +118,15 @@ impl Account {
rest = "";
}

if let Some(account_opt) = self.accounts.get_mut(first) {
if let Some(account_opt) = self.accounts.get(first) {
// keep this value
account = account_opt;
} else {
if !auto_create {
return None;
}

let mut new_account = Account::new(first);
log::debug!("Setting account parent: {:?}", self);
new_account.set_parent(self);

self.accounts.insert(first.into(), new_account);

let Some(new_ref) = self.accounts.get(first)
else {panic!("should not happen")};
account = new_ref;
account = self.create_account(first);
}

// Search recursively.
Expand Down Expand Up @@ -166,7 +180,8 @@ impl Account {
}

pub(crate) fn set_parent(&mut self, parent: &Account) {
self.parent = parent as *const Account;
// self.parent = parent as *const Account;
self.parent = addr_of!(*parent);
}

/// Returns the balance of this account and all sub-accounts.
Expand Down Expand Up @@ -195,7 +210,7 @@ impl Account {

#[cfg(test)]
mod tests {
use std::io::Cursor;
use std::{io::Cursor, ptr::addr_of};

use crate::{amount::Quantity, journal::Journal, parse_file, parse_text, parser};

Expand Down Expand Up @@ -229,20 +244,18 @@ mod tests {
/// Search for an account by the full account name.
#[test]
fn test_fullname() {
let mut j = Journal::new();
let input = r#"2023-05-01 Test
Expenses:Food 10 EUR
Assets:Cash
"#;
parser::read_into_journal(Cursor::new(input), &mut j);
let mut journal = Journal::new();
parser::read_into_journal(Cursor::new(input), &mut journal);

let Some(ptr) = j.find_account("Expenses:Food")
else {panic!("account not found");};
let account = j.get_account(ptr);
let account = journal.find_account("Expenses:Food").unwrap();

let actual = account.fullname();

assert_eq!(5, j.master.flatten_account_tree().len());
assert_eq!(5, journal.master.flatten_account_tree().len());
assert_eq!("Food", account.name);
assert_eq!("Expenses:Food", actual);
}
Expand Down Expand Up @@ -291,7 +304,7 @@ mod tests {
}

#[test]
fn test_parent_pointers() {
fn test_parent_pointer() {
let input = r#"2023-05-05 Payee
Expenses 20
Assets
Expand All @@ -304,11 +317,11 @@ mod tests {
let ptr = journal.master.find_account("Assets").unwrap();
let assets = journal.get_account(ptr);

assert_eq!(&journal.master as *const Account, assets.parent);
assert_eq!(addr_of!(journal.master), assets.parent);
}

#[test]
fn test_parent_pointers_after_fullname() {
fn test_parent_pointer_after_fullname() {
let input = r#"2023-05-05 Payee
Expenses 20
Assets
Expand All @@ -317,10 +330,10 @@ mod tests {
parse_text(input, &mut journal);

// test parent
// let ptr = journal.master.find_account("Assets").unwrap();
// let assets = journal.get_account(ptr);
let ptr = journal.master.find_account("Assets").unwrap();
let assets = journal.get_account(ptr);

// assert_eq!(&journal.master as *const Account, assets.parent);
assert_eq!(&journal.master as *const Account, assets.parent);

// test fullname
let assets_fullname = journal.master.accounts.get("Assets").unwrap().fullname();
Expand All @@ -333,6 +346,31 @@ mod tests {
let ptr = journal.master.find_account("Assets").unwrap();
let assets = journal.get_account(ptr);

assert_eq!(&journal.master as *const Account, assets.parent);
assert_eq!(addr_of!(journal.master), assets.parent);
}

#[test]
fn test_parent_pointers() {
let input = r#"2023-05-05 Payee
Expenses:Groceries 20
Assets:Cash
"#;
let mut journal = Journal::new();

parse_text(input, &mut journal);

// assets
let ptr = journal.master.find_account("Assets").unwrap();
let assets = journal.get_account(ptr);
assert_eq!(addr_of!(journal.master), assets.parent);

// cash
let ptr = journal.master.find_account("Cash").unwrap();
let cash = journal.get_account(ptr);
assert_eq!(addr_of!(*assets), cash.parent);

// expenses

// groceries
}
}
34 changes: 18 additions & 16 deletions src/journal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl Journal {
// Create the account object and associate it with the journal; this
// is registering the account.

let Some(account_ptr) = master_account.find_account(name)
let Some(account_ptr) = master_account.find_or_create(name, true)
else { return None };

// todo: add any validity checks here.
Expand All @@ -87,7 +87,7 @@ impl Journal {
Some(account)
}

pub fn find_account(&mut self, name: &str) -> Option<*const Account> {
pub fn find_account(&self, name: &str) -> Option<&Account> {
self.master.find_account(name)
}

Expand All @@ -107,7 +107,7 @@ impl Journal {
#[cfg(test)]
mod tests {
use core::panic;
use std::io::Cursor;
use std::{io::Cursor, ptr::addr_of};

use super::Journal;
use crate::{account::Account, parse_file};
Expand Down Expand Up @@ -148,34 +148,36 @@ mod tests {
#[test]
fn test_register_account() {
const NAME: &str = "Assets:Investments:Broker";
let mut journal = Journal::new();
let mut j = Journal::new();

// act
let new_acct = journal.register_account(NAME).unwrap();
let actual = journal.get_account_mut(new_acct);
let new_acct = j.register_account(NAME).unwrap();
let actual = j.get_account_mut(new_acct);

let journal = &j;

// Asserts
assert_eq!(4, journal.master.flatten_account_tree().len());
assert_eq!(NAME, actual.fullname());

// tree structure
let master = &mut journal.master;
let master = &journal.master;
assert_eq!("", master.name);

let assets_ptr = master.find_account("Assets").unwrap();
let assets = journal.get_account_mut(assets_ptr);
let assets = master.find_account("Assets").unwrap();
// let assets = journal.get_account(assets_ptr);
assert_eq!("Assets", assets.name);
assert_eq!(&journal.master as *const Account, assets.parent);
assert_eq!(addr_of!(journal.master), assets.parent);

let inv_ix = assets.find_account("Investments").unwrap();
let inv = journal.get_account_mut(inv_ix);
let inv = assets.find_account("Investments").unwrap();
// let inv = journal.get_account_mut(inv_ix);
assert_eq!("Investments", inv.name);
assert_eq!(assets_ptr, inv.parent);
assert_eq!(addr_of!(*assets), inv.parent);

let broker_ix = inv.find_account("Broker").unwrap();
let broker = journal.get_account(broker_ix);
let broker = inv.find_account("Broker").unwrap();
// let broker = journal.get_account(broker_ix);
assert_eq!("Broker", broker.name);
assert_eq!(inv_ix, broker.parent);
assert_eq!(inv as *const Account, broker.parent);
}

/// The master account needs to be created in the Journal automatically.
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ mod lib_tests {

use crate::{amount::{Amount, Quantity}, option, run};

// Try to understand why this test fails when dereferencing.
#[test]
fn test_minimal() {
// create a ledger command
Expand Down

0 comments on commit baf502e

Please sign in to comment.