Lines
7.27 %
Functions
3.7 %
Branches
100 %
use derive_more::From;
use finance::{
account::Account, commodity::Commodity, error::FinanceError, price::Price, split::Split,
tag::Tag, transaction::Transaction,
};
use num_rational::Rational64;
use sqlx::{
types::Uuid,
types::chrono::{DateTime, Utc},
use std::{
collections::HashMap,
fmt::{self, Debug},
use thiserror::Error;
use crate::{config::ConfigError, error::ServerError};
pub mod account;
pub mod commodity;
pub mod config;
pub mod split;
pub mod transaction;
#[derive(Debug, Clone)]
pub struct CommodityInfo {
pub commodity_id: Uuid,
pub symbol: String,
pub name: String,
}
#[derive(Debug, From)]
pub enum FinanceEntity {
Commodity(Commodity),
Tag(Tag),
Split(Split),
Transaction(Transaction),
Price(Price),
Account(Account),
pub enum Argument {
String(String),
Rational(Rational64),
Uuid(Uuid),
Data(Vec<u8>),
FinanceEntity(FinanceEntity),
FinanceEntities(Vec<FinanceEntity>),
DateTime(DateTime<Utc>),
impl From<Argument> for String {
fn from(arg: Argument) -> Self {
match arg {
Argument::String(s) => s,
_ => panic!("Cannot convert {arg:?} to String"),
impl From<Argument> for Rational64 {
Argument::Rational(r) => r,
_ => panic!("Cannot convert {arg:?} to Rational64"),
impl From<Argument> for Vec<u8> {
Argument::Data(d) => d,
_ => panic!("Cannot convert {arg:?} to Vec<u8>"),
#[derive(Debug, Error)]
pub enum CmdError {
#[error("Wrong arguments: {0}")]
Args(String),
#[error("Config: {0}")]
Config(#[from] ConfigError),
#[error("Database: {0}")]
DB(#[from] sqlx::Error),
#[error("Server: {0}")]
Server(#[from] ServerError),
#[error("Finance: {0}")]
Finance(#[from] FinanceError),
// Implementing CmdResult as an enum with String and Rational returning options
pub enum CmdResult {
Lines(Vec<String>),
Entity(FinanceEntity),
Entities(Vec<FinanceEntity>),
TaggedEntities(Vec<(FinanceEntity, HashMap<String, FinanceEntity>)>),
CommodityInfoList(Vec<CommodityInfo>),
MultiCurrencyBalance(Vec<(Commodity, Rational64)>),
pub struct LinesView<'view>(&'view CmdResult);
pub struct LinesViewMut<'view>(&'view mut CmdResult);
impl std::ops::Deref for LinesView<'_> {
type Target = Vec<String>;
fn deref(&self) -> &Self::Target {
match self.0 {
CmdResult::Lines(lines) => lines,
_ => panic!("Attempted to use Lines view on non-Lines variant"),
impl std::ops::Deref for LinesViewMut<'_> {
&mut CmdResult::Lines(ref lines) => lines,
impl std::ops::DerefMut for LinesViewMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut CmdResult::Lines(ref mut lines) => lines,
impl CmdResult {
#[must_use]
pub fn as_lines(&self) -> LinesView<'_> {
LinesView(self)
pub fn as_lines_mut(&mut self) -> LinesViewMut<'_> {
LinesViewMut(self)
impl fmt::Display for CmdResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CmdResult::String(s) => write!(f, "{s}"),
CmdResult::Rational(r) => write!(f, "{r}"),
CmdResult::Data(d) => write!(f, "CmdResult<Data>: \"{}\" bytes", d.len()),
CmdResult::Lines(l) => {
// Find the maximum width for alignment
let max_width = l.iter().map(std::string::String::len).max().unwrap_or(0);
// Write header
writeln!(f, "CmdResult<Lines>: {} items", l.len())?;
// Write each item in a column format
for (i, item) in l.iter().enumerate() {
writeln!(f, "{:>4}. {:<width$}", i + 1, item, width = max_width)?;
Ok(())
CmdResult::Entity(e) => write!(f, "CmdResult<FinanceEntity>: \"{e:?}\""),
CmdResult::Entities(e) => write!(f, "CmdResult<FinanceEntities>: \"{}\"", e.len()),
CmdResult::TaggedEntities(e) => write!(f, "CmdResult<TaggedEntities>: \"{}\"", e.len()),
CmdResult::CommodityInfoList(e) => {
write!(f, "CmdResult<CommodityInfoList>: \"{}\"", e.len())
CmdResult::MultiCurrencyBalance(e) => {
write!(f, "CmdResult<MultiCurrencyBalance>: \"{}\"", e.len())