Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,29 @@ impl Open {
}
}

impl Display for Open {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "open {}", self.account)?;

let mut first = true;
for currency in &self.currencies {
if first {
write!(f, " ")?;
first = false;
} else {
write!(f, ",")?;
}
write!(f, "{currency}")?;
}

if let Some(booking_method) = &self.booking_method {
write!(f, " \"{booking_method}\"")?;
}

Ok(())
}
}

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct BookingMethod(Arc<str>);

Expand Down Expand Up @@ -161,6 +184,12 @@ impl Close {
}
}

impl Display for Close {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "close {}", self.account)
}
}

/// Balance assertion
///
/// # Example
Expand Down Expand Up @@ -197,6 +226,20 @@ impl<D> Balance<D> {
}
}

impl<D: Display> Display for Balance<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "balance {} {}", self.account, self.amount.value)?;

if let Some(tolerance) = &self.tolerance {
write!(f, " ~ {}", &tolerance)?;
}

write!(f, " {}", self.amount.currency)?;

Ok(())
}
}

/// Pad directive
///
/// # Example
Expand Down Expand Up @@ -228,6 +271,12 @@ impl Pad {
}
}

impl Display for Pad {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "pad {} {}", self.account, self.source_account)
}
}

pub(super) fn parse(input: Span<'_>) -> IResult<'_, Account> {
let (input, name) = recognize(preceded(
preceded(
Expand Down
12 changes: 12 additions & 0 deletions src/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ pub struct Price<D> {
pub amount: Amount<D>,
}

impl<D: Display> Display for Price<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "price {} {}", self.currency, self.amount)
}
}

/// Amount
///
/// Where `D` is the decimal type (like `f64` or `rust_decimal::Decimal`)
Expand All @@ -51,6 +57,12 @@ pub struct Amount<D> {
pub currency: Currency,
}

impl<D: Display> Display for Amount<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.value, self.currency)
}
}

/// Currency
///
/// One may use [`Currency::as_str`] to get the string representation of the currency
Expand Down
8 changes: 7 additions & 1 deletion src/date.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{cmp::Ordering, str::FromStr};
use std::{cmp::Ordering, fmt::Display, str::FromStr};

use nom::{
bytes::complete::take,
Expand Down Expand Up @@ -37,6 +37,12 @@ pub struct Date {
pub day: u8,
}

impl Display for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{:02}-{:02}", self.year, self.month, self.day)
}
}

impl Date {
/// Create a new date from year, month and day
#[must_use]
Expand Down
6 changes: 6 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ impl Event {
}
}

impl std::fmt::Display for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "event \"{}\" \"{}\"", self.name, self.value)
}
}

pub(super) fn parse(input: Span<'_>) -> IResult<'_, Event> {
let (input, name) = string(input)?;
let (input, _) = space1(input)?;
Expand Down
30 changes: 30 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

use std::{
collections::HashSet,
fmt::{Debug, Display},
fs::File,
io::Read,
path::{Path, PathBuf},
Expand Down Expand Up @@ -354,6 +355,20 @@ pub struct Directive<D> {
pub line_number: u32,
}

impl<D: Display> Display for Directive<D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.date, f)?;
f.write_str(" ")?;
Display::fmt(&self.content, f)?;

for (key, value) in &self.metadata {
write!(f, "\n {key}: {value}")?;
}

Ok(())
}
}

impl<D: Decimal> FromStr for Directive<D> {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Expand All @@ -379,6 +394,21 @@ pub enum DirectiveContent<D> {
Event(Event),
}

impl<D: Display> Display for DirectiveContent<D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DirectiveContent::Transaction(transaction) => Display::fmt(transaction, f),
DirectiveContent::Price(price) => Display::fmt(price, f),
DirectiveContent::Balance(balance) => Display::fmt(balance, f),
DirectiveContent::Open(open) => Display::fmt(open, f),
DirectiveContent::Close(close) => Display::fmt(close, f),
DirectiveContent::Pad(pad) => Display::fmt(pad, f),
DirectiveContent::Commodity(currency) => write!(f, "commodity {currency}"),
DirectiveContent::Event(event) => Display::fmt(event, f),
}
}
}

impl<D> DirectiveContent<D> {
/// Returns `Some` if the directive content is a transaction
pub fn as_transaction(&self) -> Option<&Transaction<D>> {
Expand Down
9 changes: 9 additions & 0 deletions src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ impl<D> Value<D> {
}
}
}
impl<D: Display> Display for Value<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::String(value) => write!(f, r#""{value}""#),
Value::Number(value) => Display::fmt(value, f),
Value::Currency(value) => Display::fmt(value, f),
}
}
}

pub(crate) fn parse<D: Decimal>(input: Span<'_>) -> IResult<'_, Map<D>> {
let mut iter = iterator(input, alt((entry.map(Some), empty_line.map(|()| None))));
Expand Down
86 changes: 86 additions & 0 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,41 @@ pub struct Transaction<D> {
pub postings: Vec<Posting<D>>,
}

impl<D: Display> Display for Transaction<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.flag {
Some(flag) => write!(f, "{flag}")?,
None => write!(f, "txn")?,
}

if let Some(payee) = &self.payee {
write!(f, r#" "{payee}""#)?;
}

write!(f, r#" "{}""#, self.narration.as_deref().unwrap_or_default())?;

// Sort tags and links for deterministic output
let mut tags: Vec<_> = self.tags.iter().collect();
tags.sort();
for tag in tags {
write!(f, " #{tag}")?;
}

let mut links: Vec<_> = self.links.iter().collect();
links.sort();
for link in links {
write!(f, " ^{link}")?;
}

for posting in &self.postings {
writeln!(f)?;
fmt_posting(posting, " ", f)?;
}

Ok(())
}
}

/// A transaction posting
///
/// # Example
Expand Down Expand Up @@ -118,6 +153,57 @@ impl<D> Posting<D> {
}
}

impl<D: Display> Display for Posting<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fmt_posting(self, "", f)
}
}

fn fmt_posting<D: Display>(
posting: &Posting<D>,
indent: &str,
f: &mut Formatter<'_>,
) -> std::fmt::Result {
write!(f, "{indent}")?;

if let Some(flag) = posting.flag {
write!(f, "{flag} ")?;
}

write!(f, "{}", posting.account)?;

if let Some(amount) = &posting.amount {
write!(f, " {amount}")?;
}

if let Some(cost) = &posting.cost {
write!(f, " {{")?;
if let Some(date) = &cost.date {
write!(f, "{date}")?;
if cost.amount.is_some() {
write!(f, ", ")?;
}
}
if let Some(amount) = &cost.amount {
write!(f, "{amount}")?;
}
write!(f, "}}")?;
}

if let Some(price) = &posting.price {
match price {
PostingPrice::Unit(amount) => write!(f, " @ {amount}")?,
PostingPrice::Total(amount) => write!(f, " @@ {amount}")?,
}
}

for (key, value) in &posting.metadata {
write!(f, "\n{indent} {key}: {value}")?;
}

Ok(())
}

/// Cost of a posting
///
/// It is the amount within `{` and `}`.
Expand Down
Loading
Loading