beanru 0.1.0

Library to simplify writing read-modify-write scripts for beancount.
Documentation
use crate::types::*;
use beancount_parser as parser;

pub fn parse(content: &str) -> anyhow::Result<BeancountFile<rust_decimal::Decimal>> {
    let beancount = match parser::parse::<rust_decimal::Decimal>(content) {
        Ok(b) => b,
        Err(err) => anyhow::bail!("failed to parse the beancount file: {:?}", err),
    };
    Ok(beancount.into())
}

impl<D> From<parser::BeancountFile<D>> for BeancountFile<D>
where
    D: Decimal,
{
    fn from(f: parser::BeancountFile<D>) -> Self {
        BeancountFile {
            directives: f.directives.into_iter().map(|d| d.into()).collect(),
        }
    }
}

impl<D> From<parser::Directive<D>> for Directive<D>
where
    D: Decimal,
{
    fn from(d: parser::Directive<D>) -> Self {
        Directive {
            date: from_parser_date_to_chrono_date(&d.date),
            content: d.content.into(),
            metadata: d
                .metadata
                .into_iter()
                .map(|(key, value)| (key.to_string(), value.into()))
                .collect(),
        }
    }
}

impl<D> From<parser::metadata::Value<D>> for MetadataValue<D> {
    fn from(v: parser::metadata::Value<D>) -> Self {
        match v {
            parser::metadata::Value::String(x) => MetadataValue::String(x),
            parser::metadata::Value::Number(x) => MetadataValue::Number(x),
            parser::metadata::Value::Currency(x) => MetadataValue::Currency(x.into()),
            _ => unimplemented!("given metadata value type is not supported yet"),
        }
    }
}

impl<D> From<parser::DirectiveContent<D>> for DirectiveContent<D>
where
    D: Decimal,
{
    fn from(v: parser::DirectiveContent<D>) -> Self {
        match v {
            parser::DirectiveContent::Balance(x) => DirectiveContent::Balance(x.into()),
            parser::DirectiveContent::Close(x) => DirectiveContent::Close(x.into()),
            parser::DirectiveContent::Commodity(x) => DirectiveContent::Commodity(x.into()),
            parser::DirectiveContent::Open(x) => DirectiveContent::Open(x.into()),
            parser::DirectiveContent::Pad(x) => DirectiveContent::Pad(x.into()),
            parser::DirectiveContent::Price(x) => DirectiveContent::Price(x.into()),
            parser::DirectiveContent::Transaction(x) => DirectiveContent::Transaction(x.into()),
            _ => unimplemented!(),
        }
    }
}

impl From<parser::Pad> for Pad {
    fn from(v: parser::Pad) -> Self {
        Self {
            account: v.account.into(),
            source_account: v.source_account.into(),
        }
    }
}

impl From<parser::Open> for Open {
    fn from(v: parser::Open) -> Self {
        Self {
            account: v.account.into(),
            currencies: v.currencies.into_iter().map(|c| c.into()).collect(),
        }
    }
}

impl From<parser::Event> for Event {
    fn from(v: parser::Event) -> Self {
        Self {
            name: v.name.to_owned(),
            value: v.value,
        }
    }
}

impl From<parser::Close> for Close {
    fn from(v: parser::Close) -> Self {
        Self {
            account: v.account.into(),
        }
    }
}

impl From<parser::Account> for Account {
    fn from(v: parser::Account) -> Self {
        Account(v.as_str().to_owned())
    }
}

impl<D> From<parser::Price<D>> for Price<D> {
    fn from(v: parser::Price<D>) -> Self {
        Self {
            currency: v.currency.into(),
            amount: v.amount.into(),
        }
    }
}

impl<D> From<parser::Balance<D>> for Balance<D> {
    fn from(v: parser::Balance<D>) -> Self {
        Self {
            account: v.account.into(),
            amount: v.amount.into(),
        }
    }
}

impl<D> From<parser::Transaction<D>> for Transaction<D>
where
    D: Decimal,
{
    fn from(v: parser::Transaction<D>) -> Self {
        let mut t = Self {
            flag: v.flag,
            payee: v.payee.map(String::from),
            narration: v.narration.map(String::from),
            tags: v.tags.into_iter().map(|x| x.to_string()).collect(),
            links: v.links.into_iter().map(|x| x.to_string()).collect(),
            postings: v.postings.into_iter().map(|x| x.into()).collect(),
            balanced: true,
        };
        if t.book().is_err() {
            t.balanced = false
        }
        t
    }
}

impl<D> From<parser::Posting<D>> for Posting<D> {
    fn from(v: parser::Posting<D>) -> Self {
        Self {
            flag: v.flag,
            account: v.account.into(),
            amount: v.amount.map(|x| x.into()),
            cost: v.cost.map(|x| x.into()),
            price: v.price.map(|x| x.into()),
            metadata: v
                .metadata
                .into_iter()
                .map(|(key, value)| (key.to_string(), value.into()))
                .collect(),
            autocomputed: false,
        }
    }
}

impl<D> From<parser::Cost<D>> for Cost<D> {
    fn from(v: parser::Cost<D>) -> Self {
        Self {
            amount: v.amount.map(|x| x.into()),
            date: v.date.map(|d| from_parser_date_to_chrono_date(&d)),
        }
    }
}

fn from_parser_date_to_chrono_date(d: &parser::Date) -> chrono::NaiveDate {
    chrono::NaiveDate::from_ymd_opt(d.year.into(), d.month.into(), d.day.into()).unwrap()
}

impl<D> From<parser::Amount<D>> for Amount<D> {
    fn from(v: parser::Amount<D>) -> Self {
        Self {
            value: v.value,
            currency: v.currency.into(),
        }
    }
}

impl<D> From<parser::PostingPrice<D>> for PostingPrice<D> {
    fn from(v: parser::PostingPrice<D>) -> Self {
        match v {
            parser::PostingPrice::Unit(x) => PostingPrice::Unit(x.into()),
            parser::PostingPrice::Total(x) => PostingPrice::Total(x.into()),
        }
    }
}

impl From<parser::Currency> for Currency {
    fn from(v: parser::Currency) -> Self {
        Currency(v.as_str().to_owned())
    }
}