Lines
0 %
Functions
Branches
100 %
use serde::{Deserialize, Deserializer};
use server::command::{FilterEntity, ReportFilter};
use sqlx::types::Uuid;
pub mod balance;
pub mod income_expense;
pub mod trial_balance;
pub fn empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let opt = Option::<String>::deserialize(deserializer)?;
Ok(opt.filter(|s| !s.is_empty()))
}
#[derive(Deserialize)]
#[serde(tag = "type")]
enum FilterItem {
#[serde(rename = "tag")]
Tag {
entities: Vec<String>,
name: String,
values: Vec<String>,
},
#[serde(rename = "account")]
Account {
account_id: String,
#[serde(default, rename = "display_name")]
_display_name: String,
include_subtree: bool,
#[serde(rename = "group")]
Group {
logic: String,
items: Vec<FilterItem>,
struct FilterGroup {
fn build_tag_entity_filter(entity: FilterEntity, name: &str, values: &[String]) -> ReportFilter {
match values.len() {
1 => ReportFilter::Tag {
entity,
name: name.to_string(),
value: values[0].clone(),
_ => ReportFilter::TagIn {
values: values.to_vec(),
fn build_filter_item(item: &FilterItem) -> Option<ReportFilter> {
match item {
FilterItem::Tag {
entities,
name,
values,
} => {
if name.trim().is_empty() || values.is_empty() {
return None;
let filters: Vec<ReportFilter> = entities
.iter()
.filter_map(|e| {
let entity = match e.as_str() {
"account" => FilterEntity::Account,
"transaction" => FilterEntity::Transaction,
"split" => FilterEntity::Split,
_ => return None,
};
Some(build_tag_entity_filter(entity, name, values))
})
.collect();
match filters.len() {
0 => None,
1 => filters.into_iter().next(),
_ => Some(ReportFilter::Or(filters)),
FilterItem::Account {
account_id,
include_subtree,
..
let id = account_id.parse::<Uuid>().ok()?;
if *include_subtree {
Some(ReportFilter::AccountSubtree(id))
} else {
Some(ReportFilter::AccountEq(id))
FilterItem::Group { logic, items } => {
let filters: Vec<ReportFilter> = items.iter().filter_map(build_filter_item).collect();
_ => {
if logic == "or" {
Some(ReportFilter::Or(filters))
Some(ReportFilter::And(filters))
fn build_filter_from_group(group: &FilterGroup) -> Option<ReportFilter> {
let filters: Vec<ReportFilter> = group.items.iter().filter_map(build_filter_item).collect();
if group.logic == "or" {
#[must_use]
pub fn build_report_filter(
tag_filters: Option<&str>,
tag_filter_mode: Option<&str>,
) -> Option<ReportFilter> {
let raw = tag_filters.filter(|s| !s.is_empty())?;
if tag_filter_mode == Some("script") {
return build_filter_from_sexpr(raw);
let group: FilterGroup = serde_json::from_str(raw).ok()?;
build_filter_from_group(&group)
#[cfg(feature = "scripting")]
fn build_filter_from_sexpr(raw: &str) -> Option<ReportFilter> {
ReportFilter::from_sexpr(raw).ok()
#[cfg(not(feature = "scripting"))]
fn build_filter_from_sexpr(_raw: &str) -> Option<ReportFilter> {
None