Skip to content

Commit b2424c8

Browse files
authored
Fix substraction with overflow (#28)
1 parent 0db525f commit b2424c8

File tree

7 files changed

+68
-25
lines changed

7 files changed

+68
-25
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# CHANGELOG
22

3+
## 2025-06-13 - [0.2.9]
4+
5+
### Bug fixes
6+
7+
- Fix substraction with overflow formatting some entry values
8+
with currencies with more than 3 characters.
9+
310
## 2025-05-20 - [0.2.8]
411

512
### Bug fixes
@@ -108,6 +115,7 @@
108115

109116
First beta release
110117

118+
[0.2.9]: https://github.com/mondeja/hledger-fmt/compare/v0.2.8...v0.2.9
111119
[0.2.8]: https://github.com/mondeja/hledger-fmt/compare/v0.2.7...v0.2.8
112120
[0.2.7]: https://github.com/mondeja/hledger-fmt/compare/v0.2.6...v0.2.7
113121
[0.2.6]: https://github.com/mondeja/hledger-fmt/compare/v0.2.5...v0.2.6

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hledger-fmt"
3-
version = "0.2.8"
3+
version = "0.2.9"
44
rust-version = "1.74.1"
55
edition = "2021"
66
description = "An opinionated hledger's journal files formatter."

src/cli/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn run(cmd: clap::Command) -> i32 {
3838
"{}",
3939
// Rewrite without concatenation
4040
"No hledger journal files found in the current directory nor its subdirectories.\n\
41-
Ensure that have extensions '.hledger', '.journal' or '.j."
41+
Ensure that have extensions '.hledger', '.journal' or '.j'."
4242
);
4343
exitcode = 1;
4444
return exitcode;
@@ -219,7 +219,7 @@ fn gather_files_from_directory_and_subdirectories(
219219
.contains(&ext)
220220
{
221221
let file_path = path.to_str().unwrap();
222-
let maybe_file_content = read_file(&file_path);
222+
let maybe_file_content = read_file(file_path);
223223
if let Ok(content) = maybe_file_content {
224224
files.push((file_path.to_string(), content));
225225
} else {

src/formatter/mod.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ pub(crate) fn format_content_with_options(
118118
max_entry_value_first_part_commodity_trailing_len,
119119
max_entry_value_first_separator_len,
120120
max_entry_value_second_part_decimal_len,
121-
max_entry_value_second_part_numeric_units_len,
121+
max_entry_value_second_part_units_len,
122+
max_entry_value_second_part_commodity_leading_len,
122123
max_entry_value_second_separator_len,
123124
max_entry_value_third_part_decimal_len,
124125
max_entry_value_third_part_numeric_units_len,
@@ -193,15 +194,26 @@ pub(crate) fn format_content_with_options(
193194
if !value_second_part_units.is_empty()
194195
|| !value_second_part_decimal.is_empty()
195196
{
196-
" ".repeat(
197-
3 + max_entry_value_first_separator_len
198-
- value_first_separator.len()
197+
" ".repeat({
198+
let value_first_separator_len = value_first_separator.len();
199+
let n = 3 + (*max_entry_value_first_separator_len as i32)
200+
- value_first_separator_len as i32
199201
- leading_commodity_len_from_units(
200202
value_second_part_units,
201-
)
202-
+ max_entry_value_second_part_numeric_units_len
203-
- value_second_part_numeric_units.chars().count(),
204-
)
203+
) as i32
204+
+ (*max_entry_value_second_part_units_len as i32)
205+
- value_second_part_numeric_units.chars().count()
206+
as i32
207+
- (*max_entry_value_second_part_commodity_leading_len
208+
as i32);
209+
210+
if n > 3 {
211+
n as usize
212+
} else {
213+
3 + max_entry_value_first_separator_len
214+
- value_first_separator_len
215+
}
216+
})
205217
} else {
206218
"".to_string()
207219
},

src/formatter/tests.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,8 @@ fn transaction_with_shares() {
330330
"#,
331331
r#"
332332
2024-01-15 buy some shares, in two lots ; Cost can be noted.
333-
assets:investments:2024-01-15 2.0 AAAA @ $1.50 ; @ means per-unit cost
334-
assets:investments:2024-01-15-02 3.0 AAAA @@ $4 ; @@ means total cost
333+
assets:investments:2024-01-15 2.0 AAAA @ $1.50 ; @ means per-unit cost
334+
assets:investments:2024-01-15-02 3.0 AAAA @@ $4 ; @@ means total cost
335335
; ^ Per-lot subaccounts are sometimes useful.
336336
assets:checking $-7
337337
"#,
@@ -492,7 +492,7 @@ fn lots() {
492492
}
493493

494494
#[test]
495-
fn issue25() {
495+
fn issue_25() {
496496
assert_format(
497497
r#"1/1/1 * transaction
498498
; vacation $2350 hawaii flight
@@ -502,3 +502,17 @@ fn issue25() {
502502
"#,
503503
);
504504
}
505+
506+
#[test]
507+
fn issue_27() {
508+
assert_format(
509+
r#"2024-01-02 exchange imaginary currency
510+
income:cash EUR -100 @@ USDT 120
511+
assets:cash USDT 120
512+
"#,
513+
r#"2024-01-02 exchange imaginary currency
514+
income:cash EUR-100 @@ USDT120
515+
assets:cash USDT120
516+
"#,
517+
)
518+
}

src/parser/mod.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ pub enum JournalCstNode {
6565
max_entry_value_first_part_commodity_trailing_len: usize,
6666
max_entry_value_first_separator_len: usize,
6767
max_entry_value_second_part_decimal_len: usize,
68-
max_entry_value_second_part_numeric_units_len: usize,
68+
max_entry_value_second_part_units_len: usize,
69+
max_entry_value_second_part_commodity_leading_len: usize,
6970
max_entry_value_second_separator_len: usize,
7071
max_entry_value_third_part_decimal_len: usize,
7172
max_entry_value_third_part_numeric_units_len: usize,
@@ -198,7 +199,8 @@ struct ParserTempData {
198199
max_entry_value_first_part_commodity_trailing_len: usize,
199200
max_entry_value_first_separator_len: usize,
200201
max_entry_value_second_part_decimal_len: usize,
201-
max_entry_value_second_part_numeric_units_len: usize,
202+
max_entry_value_second_part_units_len: usize,
203+
max_entry_value_second_part_commodity_leading_len: usize,
202204
max_entry_value_second_separator_len: usize,
203205
max_entry_value_third_part_decimal_len: usize,
204206
max_entry_value_third_part_numeric_units_len: usize,
@@ -225,7 +227,8 @@ impl ParserTempData {
225227
max_entry_value_first_part_commodity_trailing_len: 0,
226228
max_entry_value_first_separator_len: 0,
227229
max_entry_value_second_part_decimal_len: 0,
228-
max_entry_value_second_part_numeric_units_len: 0,
230+
max_entry_value_second_part_units_len: 0,
231+
max_entry_value_second_part_commodity_leading_len: 0,
229232
max_entry_value_second_separator_len: 0,
230233
max_entry_value_third_part_decimal_len: 0,
231234
max_entry_value_third_part_numeric_units_len: 0,
@@ -525,9 +528,12 @@ pub fn parse_content(content: &str) -> Result<JournalFile, errors::SyntaxError>
525528
data.max_entry_value_second_part_decimal_len = data
526529
.max_entry_value_second_part_decimal_len
527530
.max(p.second_part_decimal.chars().count());
528-
data.max_entry_value_second_part_numeric_units_len = data
529-
.max_entry_value_second_part_numeric_units_len
530-
.max(p.second_part_numeric_units.len());
531+
data.max_entry_value_second_part_units_len = data
532+
.max_entry_value_second_part_units_len
533+
.max(p.second_part_units.chars().count());
534+
data.max_entry_value_second_part_commodity_leading_len = data
535+
.max_entry_value_second_part_commodity_leading_len
536+
.max(leading_commodity_len_from_units(&p.second_part_units));
531537

532538
data.max_entry_value_second_separator_len = data
533539
.max_entry_value_second_separator_len
@@ -776,8 +782,9 @@ fn save_transaction(data: &mut ParserTempData, journal: &mut Vec<JournalCstNode>
776782
.max_entry_value_first_part_commodity_trailing_len,
777783
max_entry_value_first_separator_len: data.max_entry_value_first_separator_len,
778784
max_entry_value_second_part_decimal_len: data.max_entry_value_second_part_decimal_len,
779-
max_entry_value_second_part_numeric_units_len: data
780-
.max_entry_value_second_part_numeric_units_len,
785+
max_entry_value_second_part_units_len: data.max_entry_value_second_part_units_len,
786+
max_entry_value_second_part_commodity_leading_len: data
787+
.max_entry_value_second_part_commodity_leading_len,
781788
max_entry_value_second_separator_len: data.max_entry_value_second_separator_len,
782789
max_entry_value_third_part_decimal_len: data.max_entry_value_third_part_decimal_len,
783790
max_entry_value_third_part_numeric_units_len: data
@@ -798,7 +805,8 @@ fn save_transaction(data: &mut ParserTempData, journal: &mut Vec<JournalCstNode>
798805
data.max_entry_value_first_part_commodity_trailing_len = 0;
799806
data.max_entry_value_first_separator_len = 0;
800807
data.max_entry_value_second_part_decimal_len = 0;
801-
data.max_entry_value_second_part_numeric_units_len = 0;
808+
data.max_entry_value_second_part_units_len = 0;
809+
data.max_entry_value_second_part_commodity_leading_len = 0;
802810
data.max_entry_value_second_separator_len = 0;
803811
data.max_entry_value_third_part_decimal_len = 0;
804812
data.max_entry_value_third_part_numeric_units_len = 0;
@@ -1000,6 +1008,7 @@ impl EntryValueParser {
10001008
} else if !c.is_whitespace() {
10011009
second_part_value.push(c);
10021010
state = SecondPartCommodityBefore;
1011+
current_spaces_in_a_row = 0;
10031012
}
10041013
}
10051014
SecondPartCommodityBefore => {
@@ -1235,7 +1244,7 @@ mod test {
12351244
#[test]
12361245
fn test_parser() {
12371246
let mut parser = EntryValueParser::default();
1238-
_ = parser.parse("0.0 AAAA = 2.0 AAAA @ $1.50");
1247+
_ = parser.parse("EUR -100 @@ USDT 120");
12391248
println!("{:?}", parser);
12401249
assert!(false);
12411250
}

0 commit comments

Comments
 (0)