use std::collections::HashMap;
use wasm_bindgen::prelude::*;
use rustledger_booking::interpolate;
use rustledger_core::Directive;
use rustledger_parser::{ParseResult as ParserResult, parse as parse_beancount};
use rustledger_validate::validate as validate_ledger;
use crate::types::{Error, LedgerOptions, Severity};
use crate::utils::LineLookup;
pub struct LoadResult {
pub directives: Vec<Directive>,
pub options: LedgerOptions,
pub errors: Vec<Error>,
pub lookup: LineLookup,
pub parse_result: ParserResult,
}
pub fn load_and_interpolate(source: &str) -> LoadResult {
let parse_result = parse_beancount(source);
let lookup = LineLookup::new(source);
let mut errors: Vec<Error> = parse_result
.errors
.iter()
.map(|e| Error::with_line(e.to_string(), lookup.byte_to_line(e.span().0)))
.collect();
let options = extract_options(&parse_result.options);
let mut directives: Vec<_> = parse_result
.directives
.iter()
.map(|s| s.value.clone())
.collect();
if errors.is_empty() {
for (i, directive) in directives.iter_mut().enumerate() {
if let Directive::Transaction(txn) = directive {
match interpolate(txn) {
Ok(result) => {
*txn = result.transaction;
}
Err(e) => {
let line = lookup.byte_to_line(parse_result.directives[i].span.start);
errors.push(Error::with_line(e.to_string(), line));
}
}
}
}
}
LoadResult {
directives,
options,
errors,
lookup,
parse_result,
}
}
pub fn run_validation(load: &LoadResult) -> Vec<Error> {
if !load.errors.is_empty() {
return Vec::new();
}
let mut date_to_line: HashMap<String, u32> = HashMap::new();
for spanned in &load.parse_result.directives {
let line = load.lookup.byte_to_line(spanned.span.start);
let date = spanned.value.date().to_string();
date_to_line.entry(date).or_insert(line);
}
validate_ledger(&load.directives)
.into_iter()
.map(|err| {
let line = date_to_line.get(&err.date.to_string()).copied();
Error {
message: err.message,
line,
column: None,
severity: Severity::Error,
}
})
.collect()
}
pub fn to_js<T: serde::Serialize>(value: &T) -> Result<JsValue, JsError> {
let serializer = serde_wasm_bindgen::Serializer::json_compatible();
value
.serialize(&serializer)
.map_err(|e| JsError::new(&e.to_string()))
}
pub fn extract_options(options: &[(String, String, rustledger_parser::Span)]) -> LedgerOptions {
let mut ledger_options = LedgerOptions::default();
for (key, value, _span) in options {
match key.as_str() {
"title" => ledger_options.title = Some(value.clone()),
"operating_currency" => {
ledger_options.operating_currencies.push(value.clone());
}
_ => {} }
}
ledger_options
}