add a simple writer for saving a set of directives to a tree of files
Dependencies
- [2]
MYQI5JIDupdate dependencies - [3]
QRIJE4AQadd a simple pretty printer for beancount directives - [*]
I2P2FTLEadd basic parser for german decimals - [*]
YDK6X6PPadd a library of important types for beancount
Change contents
- file addition: beancount-tree-writer[5.18]
- file addition: src[0.33]
- file addition: lib.rs[0.50]
use beancount_pretty_printer::PrettyPrinter;use beancount_types::Account;use beancount_types::Directive;use camino::Utf8Path;use camino::Utf8PathBuf;use color_eyre::eyre::format_err;use color_eyre::Help;use color_eyre::Report;use color_eyre::Result;use dashmap::DashMap;use rayon::prelude::IntoParallelIterator as _;use rayon::prelude::ParallelIterator as _;use rayon::slice::ParallelSliceMut as _;use snafu::Backtrace;use snafu::ResultExt as _;use snafu::Snafu;use std::fmt::Write as _;use std::fs;use std::fs::File;use std::io;use time::Month;pub struct Config {pub base_directory: Utf8PathBuf,}#[derive(Debug, Snafu)]pub struct TreeWriterError(Error);#[derive(Debug, Snafu)]enum Error {#[snafu(display("encountered unsupported directive while indexing: {directive:?}"))]IndexingDirective {backtrace: Backtrace,directive: Directive,},#[snafu(display("error while pretty printing directives for key {index:?} to {file}"))]PrettyPrinting {backtrace: Backtrace,file: Utf8PathBuf,index: DirectiveIndex,source: io::Error,},}pub struct TreeWriter {config: Config,}impl TreeWriter {pub fn new(config: Config) -> Self {Self { config }}}impl TreeWriter {pub fn write_directives(&self, directives: Vec<Directive>) -> Result<()> {let accounts = DashMap::<_, Vec<_>>::new();directives.into_par_iter().for_each_with(&accounts, |directives, directive| {let index = (&directive).try_into().unwrap();directives.entry(index).or_default().push(directive);});let errors = accounts.into_par_iter().map(|(index, directives)| self.write_month(index, directives)).filter_map(Result::err).collect::<Vec<_>>();if errors.is_empty() {Ok(())} else {Err(errors.into_iter().fold(format_err!("encountered errors while writing directives to file tree"),Report::error,))}}}impl TreeWriter {fn write_month(&self,index: DirectiveIndex,mut directives: Vec<Directive>,) -> Result<(), Error> {directives.par_sort_by_key(Directive::date);let file = index.file_in(&self.config.base_directory);self.open_file(&file).and_then(|file| {PrettyPrinter::<_, 2, 2, 100>::buffered(file).print_directives(&directives)}).context(PrettyPrintingSnafu { file, index })}fn open_file(&self, file_path: &Utf8Path) -> io::Result<File> {self.ensure_directory(file_path.parent().unwrap())?;File::create(file_path).map_err(<_>::into)}fn ensure_directory(&self, directory: &Utf8Path) -> io::Result<()> {fs::create_dir_all(directory)}}#[derive(Debug, Eq, Hash, PartialEq)]struct DirectiveIndex {account: Account,year: i32,month: Month,}impl DirectiveIndex {fn file_in(&self, path: &Utf8Path) -> Utf8PathBuf {let Self {account,year,month,} = self;let month = *month as u8;let mut path = path.to_string();let additional = AsRef::<str>::as_ref(&account).len() + 4 + 2 + 5 + 3; // account name + year + month + extension + extra separatorspath.reserve(additional);account.segments().for_each(|component| write!(path, "/{component}").unwrap());write!(path, "/{year}/{month:02}.bean").unwrap();path.into()}}impl TryFrom<&Directive> for DirectiveIndex {type Error = Error;fn try_from(value: &Directive) -> Result<Self, Self::Error> {let account = if let Some(account) = value.main_account() {account.to_owned()} else {return IndexingDirectiveSnafu {directive: value.clone(),}.fail();};let date = value.date();let year = date.year();let month = date.month();Ok(Self {account,year,month,})}} - file addition: Cargo.toml[0.33]
[package]name = "beancount-tree-writer"version = "0.0.0-dev.0"edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]camino = "1.1.1"color-eyre = "0.6.2"itertools = "0.10.5"rayon = "1.5.3"time = "0.3.14"snafu = "0.7.1"[dependencies.beancount-pretty-printer]path = "../beancount-pretty-printer"[dependencies.beancount-types]path = "../beancount-types"[dependencies.dashmap]version = "5.4.0"features = ["rayon"] - edit in Cargo.toml at line 4
"common/beancount-tree-writer", - edit in Cargo.lock at line 4
[[package]]name = "addr2line"version = "0.17.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"dependencies = ["gimli",] - edit in Cargo.lock at line 15[5.10033][6.26307]
name = "adler"version = "1.0.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"[[package]] - edit in Cargo.lock at line 42
name = "backtrace"version = "0.3.66"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"dependencies = ["addr2line","cc","cfg-if","libc","miniz_oxide","object","rustc-demangle",][[package]] - edit in Cargo.lock at line 62
][[package]]name = "beancount-tree-writer"version = "0.0.0-dev.0"dependencies = ["beancount-pretty-printer","beancount-types","camino","color-eyre","dashmap","itertools","rayon","snafu","time", - edit in Cargo.lock at line 125[6.27574][6.27574]
name = "camino"version = "1.1.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"[[package]]name = "cc"version = "1.0.73"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"[[package]] - edit in Cargo.lock at line 141[6.27751][6.27751]
[[package]]name = "color-eyre"version = "0.6.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"dependencies = ["backtrace","color-spantrace","eyre","indenter","once_cell","owo-colors","tracing-error",][[package]]name = "color-spantrace"version = "0.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"dependencies = ["once_cell","owo-colors","tracing-core","tracing-error",] - edit in Cargo.lock at line 180[6.28031][6.28031]
][[package]]name = "crossbeam-channel"version = "0.5.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"dependencies = ["cfg-if","crossbeam-utils", - edit in Cargo.lock at line 193[6.28046][6.28046]
name = "crossbeam-deque"version = "0.8.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"dependencies = ["cfg-if","crossbeam-epoch","crossbeam-utils",][[package]]name = "crossbeam-epoch"version = "0.9.10"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"dependencies = ["autocfg","cfg-if","crossbeam-utils","memoffset","once_cell","scopeguard",][[package]]name = "crossbeam-utils"version = "0.8.11"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"dependencies = ["cfg-if","once_cell",][[package]]name = "dashmap"version = "5.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"dependencies = ["cfg-if","hashbrown","lock_api","once_cell","parking_lot_core","rayon",][[package]] - edit in Cargo.lock at line 257[6.28473][6.28473]
[[package]]name = "either"version = "1.8.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - edit in Cargo.lock at line 269[6.28671][6.28671]
[[package]]name = "eyre"version = "0.6.8"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"dependencies = ["indenter","once_cell",] - edit in Cargo.lock at line 313[5.10524][6.29337]
[[package]]name = "gimli"version = "0.26.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"[[package]]name = "hashbrown"version = "0.12.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - edit in Cargo.lock at line 331[6.29525][5.10524]
[[package]]name = "hermit-abi"version = "0.1.19"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"dependencies = ["libc",][[package]]name = "indenter"version = "0.3.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - edit in Cargo.lock at line 370[6.30029][6.30029]
name = "itertools"version = "0.10.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"dependencies = ["either",][[package]] - edit in Cargo.lock at line 418[6.31133][6.31133]
[[package]]name = "lock_api"version = "0.4.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"dependencies = ["autocfg","scopeguard",] - edit in Cargo.lock at line 434[6.31323][6.31323]
[[package]]name = "memoffset"version = "0.6.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"dependencies = ["autocfg",][[package]]name = "miniz_oxide"version = "0.5.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"dependencies = ["adler",] - edit in Cargo.lock at line 463[6.31352][6.31352]
name = "num_cpus"version = "1.13.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"dependencies = ["hermit-abi","libc",][[package]] - edit in Cargo.lock at line 482[6.31575][6.31575]
name = "object"version = "0.29.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"dependencies = ["memchr",][[package]] - edit in Cargo.lock at line 495[2.430][6.31756]
[[package]]name = "owo-colors"version = "3.5.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"[[package]]name = "parking_lot_core"version = "0.9.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"dependencies = ["cfg-if","libc","redox_syscall","smallvec","windows-sys",] - edit in Cargo.lock at line 516[6.31769][6.31769]
name = "pin-project-lite"version = "0.2.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"[[package]] - edit in Cargo.lock at line 638[6.34242][6.34242]
][[package]]name = "rayon"version = "1.5.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"dependencies = ["autocfg","crossbeam-deque","either","rayon-core",][[package]]name = "rayon-core"version = "1.9.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"dependencies = ["crossbeam-channel","crossbeam-deque","crossbeam-utils","num_cpus", - edit in Cargo.lock at line 721[6.35166][6.35166]
name = "rustc-demangle"version = "0.1.21"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"[[package]] - edit in Cargo.lock at line 737
[[package]]name = "scopeguard"version = "1.1.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - edit in Cargo.lock at line 751[6.35438][6.35438]
name = "sharded-slab"version = "0.1.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"dependencies = ["lazy_static",][[package]] - edit in Cargo.lock at line 766[6.35629][6.35629]
name = "smallvec"version = "1.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"[[package]] - edit in Cargo.lock at line 854[6.37612][6.37612]
][[package]]name = "thread_local"version = "1.1.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"dependencies = ["once_cell", - edit in Cargo.lock at line 873[6.37845][6.37845]
][[package]]name = "tracing"version = "0.1.36"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"dependencies = ["cfg-if","pin-project-lite","tracing-core",][[package]]name = "tracing-core"version = "0.1.29"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"dependencies = ["once_cell","valuable",][[package]]name = "tracing-error"version = "0.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"dependencies = ["tracing","tracing-subscriber",][[package]]name = "tracing-subscriber"version = "0.3.15"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"dependencies = ["sharded-slab","thread_local","tracing-core", - edit in Cargo.lock at line 924[6.37861][6.37861]
name = "valuable"version = "0.1.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"[[package]] - edit in Cargo.lock at line 971[6.39176][6.39176]
[[package]]name = "windows-sys"version = "0.36.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"dependencies = ["windows_aarch64_msvc","windows_i686_gnu","windows_i686_msvc","windows_x86_64_gnu","windows_x86_64_msvc",][[package]]name = "windows_aarch64_msvc"version = "0.36.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"[[package]]name = "windows_i686_gnu"version = "0.36.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"[[package]]name = "windows_i686_msvc"version = "0.36.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - edit in Cargo.lock at line 1004[6.39189][6.39189]
name = "windows_x86_64_gnu"version = "0.36.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"[[package]]name = "windows_x86_64_msvc"version = "0.36.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"[[package]]