use anyhow::{Context, Result};
use rustledger_core::Directive;
use rustledger_loader::Loader;
use std::collections::HashSet;
use std::io::Write;
use std::path::PathBuf;
pub(super) fn cmd_linked<W: Write>(file: &PathBuf, location: &str, writer: &mut W) -> Result<()> {
let mut loader = Loader::new();
let load_result = loader
.load(file)
.with_context(|| format!("failed to load {}", file.display()))?;
let directives: Vec<_> = load_result.directives.iter().map(|s| &s.value).collect();
let linked: Vec<_> = if let Some(link_name) = location.strip_prefix('^') {
directives
.iter()
.filter(|d| {
if let Directive::Transaction(txn) = d {
txn.links.iter().any(|l| l.as_str() == link_name)
} else {
false
}
})
.copied()
.collect()
} else if let Some(tag_name) = location.strip_prefix('#') {
directives
.iter()
.filter(|d| {
if let Directive::Transaction(txn) = d {
txn.tags.iter().any(|t| t.as_str() == tag_name)
} else {
false
}
})
.copied()
.collect()
} else {
let line: usize = location
.parse()
.with_context(|| format!("invalid line number: {location}"))?;
let mut links_to_find: HashSet<String> = HashSet::new();
for spanned in &load_result.directives {
if spanned.span.start <= line
&& spanned.span.end >= line
&& let Directive::Transaction(txn) = &spanned.value
{
links_to_find.extend(txn.links.iter().map(ToString::to_string));
}
}
if links_to_find.is_empty() {
writeln!(writer, "No transaction found at line {line}")?;
return Ok(());
}
directives
.iter()
.filter(|d| {
if let Directive::Transaction(txn) = d {
txn.links.iter().any(|l| links_to_find.contains(l.as_str()))
} else {
false
}
})
.copied()
.collect()
};
writeln!(writer, "Found {} linked entries:", linked.len())?;
writeln!(writer, "{}", "=".repeat(60))?;
for directive in linked {
if let Directive::Transaction(txn) = directive {
writeln!(writer)?;
writeln!(writer, "{} {} \"{}\"", txn.date, txn.flag, txn.narration)?;
if !txn.links.is_empty() {
writeln!(
writer,
" Links: {}",
txn.links
.iter()
.map(|l| format!("^{l}"))
.collect::<Vec<_>>()
.join(" ")
)?;
}
for posting in &txn.postings {
if let Some(amount) = posting.amount() {
writeln!(
writer,
" {} {} {}",
posting.account, amount.number, amount.currency
)?;
} else {
writeln!(writer, " {}", posting.account)?;
}
}
}
}
Ok(())
}