use rustledger_core::format::FormatConfig;
use rustledger_parser::parse as parse_beancount;
use serde::Serialize;
use crate::helpers::LineLookup;
use crate::types::{Error, InputEntry, input_entry_to_directive};
use crate::{API_VERSION, output_json, parse_json_error};
#[derive(Serialize)]
pub struct FormatOutput {
pub api_version: &'static str,
pub formatted: String,
pub errors: Vec<Error>,
}
#[derive(Serialize)]
pub struct FormatEntryOutput {
pub api_version: &'static str,
pub formatted: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub errors: Vec<Error>,
}
pub fn cmd_format(source: &str) -> i32 {
let parse_result = parse_beancount(source);
let lookup = LineLookup::new(source);
let errors: Vec<Error> = parse_result
.errors
.iter()
.map(|e| Error::new(e.to_string()).with_line(lookup.byte_to_line(e.span().0)))
.collect();
let config = FormatConfig::default();
let mut formatted = String::new();
for (key, value, _span) in &parse_result.options {
formatted.push_str(&format!("option \"{key}\" \"{value}\"\n"));
}
if !parse_result.options.is_empty() {
formatted.push('\n');
}
for (plugin, config_opt, _span) in &parse_result.plugins {
if let Some(cfg) = config_opt {
formatted.push_str(&format!("plugin \"{plugin}\" \"{cfg}\"\n"));
} else {
formatted.push_str(&format!("plugin \"{plugin}\"\n"));
}
}
if !parse_result.plugins.is_empty() {
formatted.push('\n');
}
for spanned in &parse_result.directives {
formatted.push_str(&rustledger_core::format::format_directive(
&spanned.value,
&config,
));
}
let output = FormatOutput {
api_version: API_VERSION,
formatted,
errors,
};
output_json(&output)
}
pub fn cmd_format_entry(json_str: &str) -> i32 {
let entry: InputEntry = match serde_json::from_str(json_str) {
Ok(e) => e,
Err(e) => {
let output = FormatEntryOutput {
api_version: API_VERSION,
formatted: String::new(),
errors: vec![parse_json_error(&e)],
};
return output_json(&output);
}
};
let directive = match input_entry_to_directive(&entry) {
Ok(d) => d,
Err(e) => {
let output = FormatEntryOutput {
api_version: API_VERSION,
formatted: String::new(),
errors: vec![Error::new(e)],
};
return output_json(&output);
}
};
let config = FormatConfig::default();
let formatted = rustledger_core::format::format_directive(&directive, &config);
let output = FormatEntryOutput {
api_version: API_VERSION,
formatted,
errors: vec![],
};
output_json(&output)
}
pub fn cmd_format_entries(json_str: &str) -> i32 {
let entries: Vec<InputEntry> = match serde_json::from_str(json_str) {
Ok(e) => e,
Err(e) => {
let output = FormatEntryOutput {
api_version: API_VERSION,
formatted: String::new(),
errors: vec![parse_json_error(&e)],
};
return output_json(&output);
}
};
let config = FormatConfig::default();
let mut formatted = String::new();
let mut errors = Vec::new();
for (i, entry) in entries.iter().enumerate() {
match input_entry_to_directive(entry) {
Ok(directive) => {
formatted.push_str(&rustledger_core::format::format_directive(
&directive, &config,
));
}
Err(e) => {
errors.push(Error::new(format!("Entry {i}: {e}")).with_entry_index(i));
}
}
}
let output = FormatEntryOutput {
api_version: API_VERSION,
formatted,
errors,
};
output_json(&output)
}