Settings, conventions (posting templates, approval rules, policies, close steps, calendar, mappings, notes), decisions, open items and entry approvals are journal events (journal format 2). Books that keep their settings never read wanderbooks.toml or config.json; the server and the viewer take the vault key from --vault-key-file instead. books.import_settings moves a settings file in once, exactly as the server reads it. Approval rules, when enforced, gate every posting path. Replay refuses what it can't read as newer, never as corrupt. Lean: a record changes no figure. Mutation on the whole diff: every survivor killed by a test but two equivalent (docs/testing.md). The Company page says a late open item is overdue in words, not colour alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
80 lines
3.0 KiB
Rust
80 lines
3.0 KiB
Rust
//! `ledger-gui [--data-dir PATH] [--books-toml PATH] [--vault-key-file PATH] [--today YYYY-MM-DD] [--demo]`
|
|
//!
|
|
//! Opens the books read-only and follows them live. With no flags it looks
|
|
//! for `books-data/` and `wanderbooks.toml` in the current directory.
|
|
//! `--demo` builds a demo company in a temp directory and opens that.
|
|
|
|
use ledger_gui::{App, Source};
|
|
use std::path::PathBuf;
|
|
|
|
fn main() -> eframe::Result<()> {
|
|
let mut data_dir: Option<PathBuf> = None;
|
|
let mut books_toml: Option<PathBuf> = None;
|
|
let mut today = None;
|
|
let mut demo = false;
|
|
let mut args = std::env::args().skip(1);
|
|
while let Some(a) = args.next() {
|
|
match a.as_str() {
|
|
"--data-dir" => data_dir = args.next().map(PathBuf::from),
|
|
"--books-toml" => books_toml = args.next().map(PathBuf::from),
|
|
"--vault-key-file" => {
|
|
if let Some(k) = args.next() {
|
|
ledger_gui::set_vault_key_file(PathBuf::from(k));
|
|
}
|
|
}
|
|
"--today" => today = args.next().and_then(|d| d.parse().ok()),
|
|
"--demo" => demo = true,
|
|
"-h" | "--help" => {
|
|
println!(
|
|
"usage: ledger-gui [--data-dir PATH] [--books-toml PATH] [--vault-key-file PATH] [--today YYYY-MM-DD] [--demo]"
|
|
);
|
|
return Ok(());
|
|
}
|
|
other => {
|
|
eprintln!("unknown argument {other}");
|
|
std::process::exit(2);
|
|
}
|
|
}
|
|
}
|
|
if demo {
|
|
let d = today.unwrap_or_else(|| chrono::Local::now().date_naive());
|
|
let dir = std::env::temp_dir().join(format!("wanderbooks-demo-{d}"));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
if let Err(e) = ledger_gui::demo::build(&dir, d) {
|
|
eprintln!("building the demo books failed: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
eprintln!("demo books: {}", dir.display());
|
|
data_dir = Some(dir.join("books-data"));
|
|
books_toml = Some(dir.join("wanderbooks.toml"));
|
|
}
|
|
let data_dir = data_dir.unwrap_or_else(|| PathBuf::from("books-data"));
|
|
let books_toml = books_toml.or_else(|| {
|
|
let guess = data_dir
|
|
.parent()
|
|
.unwrap_or(&data_dir)
|
|
.join("wanderbooks.toml");
|
|
guess.exists().then_some(guess)
|
|
});
|
|
let source = Source {
|
|
data_dir,
|
|
books_toml,
|
|
today,
|
|
};
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default()
|
|
.with_title("wanderbooks")
|
|
.with_app_id("wanderbooks")
|
|
.with_inner_size([1440.0, 920.0])
|
|
// Small on purpose: tiling window managers hand out small slots
|
|
// regardless, and every screen lays itself out to fit.
|
|
.with_min_inner_size([360.0, 400.0]),
|
|
..Default::default()
|
|
};
|
|
eframe::run_native(
|
|
"wanderbooks",
|
|
options,
|
|
Box::new(move |cc| Ok(Box::new(App::new(cc, source)))),
|
|
)
|
|
}
|