use std::collections::HashMap;
use serde::Serialize;
#[derive(Serialize, Clone)]
pub struct Error {
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub field: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entry_index: Option<usize>,
pub severity: String,
pub phase: String,
}
impl Error {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
line: None,
column: None,
field: None,
entry_index: None,
severity: "error".to_string(),
phase: "parse".to_string(),
}
}
pub const fn with_line(mut self, line: u32) -> Self {
self.line = Some(line);
self
}
pub fn validate_phase(mut self) -> Self {
self.phase = "validate".to_string();
self
}
}
#[derive(Serialize)]
pub struct LedgerOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub operating_currency: Vec<String>,
pub name_assets: String,
pub name_liabilities: String,
pub name_equity: String,
pub name_income: String,
pub name_expenses: String,
pub documents: Vec<String>,
pub commodities: Vec<String>,
pub booking_method: String,
pub display_precision: HashMap<String, u32>,
pub render_commas: bool,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub inferred_tolerance_default: HashMap<String, String>,
pub inferred_tolerance_multiplier: String,
pub infer_tolerance_from_cost: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub account_rounding: Option<String>,
pub account_previous_balances: String,
pub account_previous_earnings: String,
pub account_previous_conversions: String,
pub account_current_earnings: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub account_current_conversions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub account_unrealized_gains: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversion_currency: Option<String>,
}
impl Default for LedgerOptions {
fn default() -> Self {
Self {
title: None,
operating_currency: Vec::new(),
name_assets: "Assets".to_string(),
name_liabilities: "Liabilities".to_string(),
name_equity: "Equity".to_string(),
name_income: "Income".to_string(),
name_expenses: "Expenses".to_string(),
documents: Vec::new(),
commodities: Vec::new(),
booking_method: "STRICT".to_string(),
display_precision: HashMap::new(),
render_commas: false,
inferred_tolerance_default: HashMap::new(),
inferred_tolerance_multiplier: "0.5".to_string(),
infer_tolerance_from_cost: false,
account_rounding: None,
account_previous_balances: "Equity:Opening-Balances".to_string(),
account_previous_earnings: "Equity:Earnings:Previous".to_string(),
account_previous_conversions: "Equity:Conversions:Previous".to_string(),
account_current_earnings: "Equity:Earnings:Current".to_string(),
account_current_conversions: None,
account_unrealized_gains: None,
conversion_currency: None,
}
}
}
#[derive(Serialize)]
pub struct Plugin {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<String>,
}
#[derive(Serialize)]
pub struct Include {
pub path: String,
pub lineno: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_default_phase_is_parse() {
let err = Error::new("test");
assert_eq!(err.phase, "parse");
}
#[test]
fn test_error_validate_phase() {
let err = Error::new("test").validate_phase();
assert_eq!(err.phase, "validate");
}
#[test]
fn test_error_phase_serializes() {
let err = Error::new("test");
let json = serde_json::to_value(&err).unwrap();
assert_eq!(json["phase"], "parse");
let err = Error::new("test").validate_phase();
let json = serde_json::to_value(&err).unwrap();
assert_eq!(json["phase"], "validate");
}
}