-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #39 from frosklis/release/0.12
Release/0.12
- Loading branch information
Showing
42 changed files
with
871 additions
and
273 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
# Changelog | ||
Changelog file for dinero-rs project, a command line application for managing finances. | ||
|
||
## [0.12.0] - 2021-02-xx (planned) | ||
### Added | ||
- support for (some of the) automated transaction syntax, what Claudio uses in his personal ledger | ||
### Fixed | ||
- speed bump (from 44 seconds to 7 seconds) in a big personal ledger | ||
## [0.11.1] - 2021-02-22 | ||
### Fixed | ||
- Fixed bug in balance report |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
use crate::models::{HasName, Payee}; | ||
use crate::parser::Tokenizer; | ||
use crate::Error; | ||
use std::ops::Deref; | ||
use std::path::PathBuf; | ||
|
||
pub fn execute(path: PathBuf, no_balance_check: bool) -> Result<(), Error> { | ||
let mut tokenizer: Tokenizer = Tokenizer::from(&path); | ||
let items = tokenizer.tokenize()?; | ||
let ledger = items.to_ledger(no_balance_check)?; | ||
let mut payees = ledger | ||
.payees | ||
.iter() | ||
.map(|x| x.1.deref().to_owned()) | ||
.collect::<Vec<Payee>>(); | ||
payees.sort_by(|a, b| a.get_name().cmp(b.get_name())); | ||
for payee in payees.iter() { | ||
println!("{}", payee); | ||
} | ||
Ok(()) | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use crate::models::{Currency, Posting, PostingType, Transaction}; | ||
use crate::parser::value_expr::{eval_expression, EvalResult}; | ||
use crate::{CommonOpts, Error, List}; | ||
use colored::Colorize; | ||
use regex::Regex; | ||
use std::collections::HashMap; | ||
|
||
pub fn filter( | ||
options: &CommonOpts, | ||
transaction: &Transaction<Posting>, | ||
posting: &Posting, | ||
commodities: &mut List<Currency>, | ||
) -> Result<bool, Error> { | ||
// Get what's needed | ||
let predicate = preprocess_query(&options.query); | ||
let real = options.real; | ||
|
||
// Check for real postings | ||
if real { | ||
if let PostingType::Real = posting.kind { | ||
} else { | ||
return Ok(false); | ||
} | ||
} | ||
|
||
// Check for dates at the transaction level | ||
// todo should do this at the posting level | ||
if let Some(date) = options.end { | ||
if transaction.date.unwrap() >= date { | ||
return Ok(false); | ||
} | ||
} | ||
if let Some(date) = options.begin { | ||
if transaction.date.unwrap() < date { | ||
return Ok(false); | ||
} | ||
} | ||
|
||
filter_predicate( | ||
predicate.as_str(), | ||
posting, | ||
transaction, | ||
commodities, | ||
&mut HashMap::new(), | ||
) | ||
} | ||
|
||
pub fn filter_predicate( | ||
predicate: &str, | ||
posting: &Posting, | ||
transaction: &Transaction<Posting>, | ||
commodities: &mut List<Currency>, | ||
regexes: &mut HashMap<String, Regex>, | ||
) -> Result<bool, Error> { | ||
if (predicate.len() == 0) | (predicate == "()") { | ||
return Ok(true); | ||
} | ||
let result = eval_expression(predicate, posting, transaction, commodities, regexes); | ||
match result { | ||
EvalResult::Boolean(b) => Ok(b), | ||
_ => Err(Error { | ||
message: vec![predicate.red().bold(), "should return a boolean".normal()], | ||
}), | ||
} | ||
} | ||
|
||
/// Create search expression from Strings | ||
/// | ||
/// The command line arguments provide syntactic sugar which save time when querying the journal. | ||
/// This expands it to an actual query | ||
/// | ||
/// # Examples | ||
/// ```rust | ||
/// # use dinero::filter::preprocess_query; | ||
/// let params:Vec<String> = vec!["@payee", "savings" , "and", "checking", "and", "expr", "/aeiou/"].iter().map(|x| x.to_string()).collect(); | ||
/// let processed = preprocess_query(¶ms); | ||
/// assert_eq!(processed, "((payee =~ /(?i)payee/) or (account =~ /(?i)savings/) and (account =~ /(?i)checking/) and (/aeiou/))") | ||
/// ``` | ||
pub fn preprocess_query(query: &Vec<String>) -> String { | ||
let mut expression = String::new(); | ||
let mut and = false; | ||
let mut first = true; | ||
let mut expr = false; | ||
for raw_term in query.iter() { | ||
let term = raw_term.trim(); | ||
if term.len() == 0 { | ||
continue; | ||
} | ||
if term == "and" { | ||
and = true; | ||
continue; | ||
} else if term == "or" { | ||
and = false; | ||
continue; | ||
} else if term == "expr" { | ||
expr = true; | ||
continue; | ||
} | ||
let join_term = if !first { | ||
if and { | ||
" and (" | ||
} else { | ||
" or (" | ||
} | ||
} else { | ||
"(" | ||
}; | ||
expression.push_str(join_term); | ||
if expr { | ||
expression.push_str(term); | ||
} else if let Some(c) = term.chars().next() { | ||
match c { | ||
'@' => { | ||
expression.push_str("payee =~ /(?i)"); // case insensitive | ||
expression.push_str(&term.to_string()[1..]); | ||
expression.push_str("/") | ||
} | ||
'%' => { | ||
expression.push_str("has_tag(/(?i)"); // case insensitive | ||
expression.push_str(&term.to_string()[1..]); | ||
expression.push_str("/)") | ||
} | ||
'/' => { | ||
expression.push_str("account =~ "); // case insensitive | ||
expression.push_str(term); | ||
} | ||
_ => { | ||
expression.push_str("account =~ /(?i)"); // case insensitive | ||
expression.push_str(term); | ||
expression.push_str("/") | ||
} | ||
} | ||
} | ||
expression.push_str(")"); | ||
and = false; | ||
expr = false; | ||
first = false; | ||
} | ||
format!("({})", expression) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Grammar specification for value expressions | ||
|
||
// A value expression is an expression between parenthesis | ||
value_expr = {"(" ~ ws* ~ expr ~ ws* ~ ")"} | ||
|
||
// Then the expression builds up in terms of increasing preference | ||
expr = { or_expr } | ||
or_expr = { and_expr ~ ws* ~ ( or ~ ws* ~ and_expr ) * } | ||
and_expr = { comparison_expr ~ ws* ~ ( and ~ ws* ~ comparison_expr ) * } | ||
comparison_expr = { additive_expr ~ ws* ~ ( comparison ~ ws* ~ additive_expr ) * } | ||
additive_expr = { multiplicative_expr ~ ws* ~ ( add ~ ws* ~ multiplicative_expr ) * } | ||
multiplicative_expr = { primary ~ ws* ~ ( mult ~ ws* ~ primary )* } | ||
primary = { | ||
("(" ~ ws* ~ expr ~ ws* ~ ")") | | ||
(unary ~ ws* ~ expr) | | ||
term | | ||
(function ~ ws* ~ "(" ~ ws* ~ expr ~ ws* ~ ("," ~ ws* ~ expr ~ ws*)* ~ ")") | ||
} | ||
|
||
|
||
term = _{ variable | money | number | regex | string } | ||
money = { (number ~ ws* ~ currency) | (currency ~ ws* ~ number) } | ||
currency = { LETTER+ | ("\"" ~ (!"\"" ~ ANY)+ ~ "\"")} | ||
regex = { "/" ~ (!"/" ~ ANY)* ~ "/"} | ||
string = { "'" ~ (!"'" ~ ANY)* ~ "'"} | ||
variable = { | ||
"account" | | ||
"payee" | | ||
"date" | | ||
"note" | | ||
"amount" | | ||
"total_amount" | | ||
"cost" | | ||
"value" | | ||
"gain" | | ||
"depth" | | ||
"posting_number" | | ||
"posting_count" | | ||
"cleared" | | ||
"real" | | ||
"not_automated" | | ||
"running_total" | | ||
"note" | | ||
// Abbreviations go later | ||
"T" | "N" | "O" | "Z" | "R" | "X" | | ||
"n" | "l" | "g" | "v" | "b" | ||
} | ||
|
||
|
||
// helpers | ||
number = { "-"? ~ bigint ~ ("." ~ bigint)? } | ||
bigint = _{ ASCII_DIGIT+ } | ||
ws = _{" "} | ||
|
||
|
||
add = { "+" | "-" } | ||
mult = { "*" | "/" } | ||
and = {"&" | "and"} | ||
or = {"|" | "or" } | ||
unary = { "-" | "!" | "not" } | ||
function = { "abs" | "has_tag" | "to_date" | "any" | "tag" } | ||
comparison = { eq | ne | ge | gt | le | lt } | ||
eq = { "=~" | "=="} | ||
ne = { "!=" } | ||
gt = { ">" } | ||
ge = { ">=" } | ||
le = { "<=" } | ||
lt = { "<" } |
Oops, something went wrong.