From 27b95c9186bd45edd4c982a7cc89cf69cf2c5d46 Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Wed, 16 Oct 2024 12:51:21 +0100 Subject: All backend stuff lives in backend folder --- backend/Cargo.lock | 2011 -------------------- backend/Cargo.toml | 15 - backend/Rocket.toml | 2 - .../app/2024-08-31-084439_initial_setup/down.sql | 6 + backend/app/2024-08-31-084439_initial_setup/up.sql | 70 + backend/app/Cargo.lock | 2011 ++++++++++++++++++++ backend/app/Cargo.toml | 15 + backend/app/Rocket.toml | 2 + backend/app/diesel.toml | 9 + backend/app/src/databases.rs | 11 + backend/app/src/main.rs | 57 + backend/app/src/models/account.rs | 15 + backend/app/src/models/budget.rs | 44 + backend/app/src/models/budget_update.rs | 17 + backend/app/src/models/category.rs | 9 + backend/app/src/models/category_balance.rs | 7 + backend/app/src/models/category_budget.rs | 9 + backend/app/src/models/category_transfer.rs | 11 + backend/app/src/models/date_time_utc.rs | 131 ++ backend/app/src/models/mod.rs | 10 + backend/app/src/models/transaction.rs | 17 + .../app/src/models/transaction_categorisation.rs | 11 + backend/app/src/queries/accounts/get_all.rs | 7 + backend/app/src/queries/accounts/insert.rs | 14 + backend/app/src/queries/accounts/mod.rs | 5 + backend/app/src/queries/budget_updates/get_all.rs | 13 + .../queries/budget_updates/get_first_with_date.rs | 19 + .../get_most_recent_with_category_id.rs | 18 + backend/app/src/queries/budget_updates/mod.rs | 7 + backend/app/src/queries/categories/get_all.rs | 8 + backend/app/src/queries/categories/get_all_ids.rs | 8 + backend/app/src/queries/categories/mod.rs | 5 + .../app/src/queries/category_balances/get_all.rs | 43 + backend/app/src/queries/category_balances/mod.rs | 3 + .../get_most_recent_per_category_id.rs | 42 + backend/app/src/queries/category_budgets/mod.rs | 3 + backend/app/src/queries/category_transfers/mod.rs | 5 + .../sum_quantity_per_from_category_id.rs | 17 + .../sum_quantity_per_to_category_id.rs | 17 + backend/app/src/queries/mod.rs | 7 + .../src/queries/transaction_categorisations/mod.rs | 3 + .../sum_quantity_per_category_id.rs | 17 + .../sum_quantity_per_to_category_id.rs | 0 backend/app/src/routes/account.rs | 57 + backend/app/src/routes/balance.rs | 17 + backend/app/src/routes/budget.rs | 18 + backend/app/src/routes/budget_update.rs | 18 + backend/app/src/routes/category.rs | 16 + backend/app/src/routes/mod.rs | 5 + backend/app/src/schema.rs | 72 + backend/app/src/utils/calculate_budgets_accrual.rs | 159 ++ .../app/src/utils/calculate_category_balance.rs | 141 ++ backend/app/src/utils/find_by_id_or.rs | 53 + backend/app/src/utils/mod.rs | 7 + backend/diesel.toml | 9 - backend/importers/actualbudget/Cargo.lock | 356 ++++ backend/importers/actualbudget/Cargo.toml | 8 + backend/importers/actualbudget/src/getters.rs | 61 + backend/importers/actualbudget/src/inserters.rs | 120 ++ backend/importers/actualbudget/src/main.rs | 57 + .../src/models/actualbudget/account.rs | 15 + .../src/models/actualbudget/category.rs | 15 + .../actualbudget/src/models/actualbudget/date.rs | 69 + .../actualbudget/src/models/actualbudget/mod.rs | 5 + .../src/models/actualbudget/transaction.rs | 43 + .../src/models/actualbudget/zero_budget.rs | 30 + .../src/models/budgeting_app/DateTimeUtc.rs | 57 + .../src/models/budgeting_app/account.rs | 8 + .../src/models/budgeting_app/budget_update.rs | 10 + .../src/models/budgeting_app/category.rs | 4 + .../src/models/budgeting_app/category_transfer.rs | 7 + .../src/models/budgeting_app/date_time_utc.rs | 20 + .../actualbudget/src/models/budgeting_app/mod.rs | 7 + .../src/models/budgeting_app/transaction.rs | 11 + .../budgeting_app/transaction_categorisation.rs | 7 + backend/importers/actualbudget/src/models/mod.rs | 2 + backend/importers/actualbudget/src/transformers.rs | 221 +++ backend/migrations/.keep | 0 .../2024-08-31-084439_initial_setup/down.sql | 6 - .../2024-08-31-084439_initial_setup/up.sql | 70 - backend/src/databases.rs | 11 - backend/src/main.rs | 57 - backend/src/models/account.rs | 15 - backend/src/models/budget.rs | 44 - backend/src/models/budget_update.rs | 17 - backend/src/models/category.rs | 9 - backend/src/models/category_balance.rs | 7 - backend/src/models/category_budget.rs | 9 - backend/src/models/category_transfer.rs | 11 - backend/src/models/date_time_utc.rs | 131 -- backend/src/models/mod.rs | 10 - backend/src/models/transaction.rs | 17 - backend/src/models/transaction_categorisation.rs | 11 - backend/src/queries/accounts/get_all.rs | 7 - backend/src/queries/accounts/insert.rs | 14 - backend/src/queries/accounts/mod.rs | 5 - backend/src/queries/budget_updates/get_all.rs | 13 - .../queries/budget_updates/get_first_with_date.rs | 19 - .../get_most_recent_with_category_id.rs | 18 - backend/src/queries/budget_updates/mod.rs | 7 - backend/src/queries/categories/get_all.rs | 8 - backend/src/queries/categories/get_all_ids.rs | 8 - backend/src/queries/categories/mod.rs | 5 - backend/src/queries/category_balances/get_all.rs | 43 - backend/src/queries/category_balances/mod.rs | 3 - .../get_most_recent_per_category_id.rs | 42 - backend/src/queries/category_budgets/mod.rs | 3 - backend/src/queries/category_transfers/mod.rs | 5 - .../sum_quantity_per_from_category_id.rs | 17 - .../sum_quantity_per_to_category_id.rs | 17 - backend/src/queries/mod.rs | 7 - .../src/queries/transaction_categorisations/mod.rs | 3 - .../sum_quantity_per_category_id.rs | 17 - .../sum_quantity_per_to_category_id.rs | 0 backend/src/routes/account.rs | 57 - backend/src/routes/balance.rs | 17 - backend/src/routes/budget.rs | 18 - backend/src/routes/budget_update.rs | 18 - backend/src/routes/category.rs | 16 - backend/src/routes/mod.rs | 5 - backend/src/schema.rs | 72 - backend/src/utils/calculate_budgets_accrual.rs | 159 -- backend/src/utils/calculate_category_balance.rs | 141 -- backend/src/utils/find_by_id_or.rs | 53 - backend/src/utils/mod.rs | 7 - importers/actualbudget/Cargo.lock | 356 ---- importers/actualbudget/Cargo.toml | 8 - importers/actualbudget/src/getters.rs | 61 - importers/actualbudget/src/inserters.rs | 120 -- importers/actualbudget/src/main.rs | 57 - .../src/models/actualbudget/account.rs | 15 - .../src/models/actualbudget/category.rs | 15 - .../actualbudget/src/models/actualbudget/date.rs | 69 - .../actualbudget/src/models/actualbudget/mod.rs | 5 - .../src/models/actualbudget/transaction.rs | 43 - .../src/models/actualbudget/zero_budget.rs | 30 - .../src/models/budgeting_app/DateTimeUtc.rs | 57 - .../src/models/budgeting_app/account.rs | 8 - .../src/models/budgeting_app/budget_update.rs | 10 - .../src/models/budgeting_app/category.rs | 4 - .../src/models/budgeting_app/category_transfer.rs | 7 - .../src/models/budgeting_app/date_time_utc.rs | 20 - .../actualbudget/src/models/budgeting_app/mod.rs | 7 - .../src/models/budgeting_app/transaction.rs | 11 - .../budgeting_app/transaction_categorisation.rs | 7 - importers/actualbudget/src/models/mod.rs | 2 - importers/actualbudget/src/transformers.rs | 221 --- 147 files changed, 4419 insertions(+), 4419 deletions(-) delete mode 100644 backend/Cargo.lock delete mode 100644 backend/Cargo.toml delete mode 100644 backend/Rocket.toml create mode 100644 backend/app/2024-08-31-084439_initial_setup/down.sql create mode 100644 backend/app/2024-08-31-084439_initial_setup/up.sql create mode 100644 backend/app/Cargo.lock create mode 100644 backend/app/Cargo.toml create mode 100644 backend/app/Rocket.toml create mode 100644 backend/app/diesel.toml create mode 100644 backend/app/src/databases.rs create mode 100644 backend/app/src/main.rs create mode 100644 backend/app/src/models/account.rs create mode 100644 backend/app/src/models/budget.rs create mode 100644 backend/app/src/models/budget_update.rs create mode 100644 backend/app/src/models/category.rs create mode 100644 backend/app/src/models/category_balance.rs create mode 100644 backend/app/src/models/category_budget.rs create mode 100644 backend/app/src/models/category_transfer.rs create mode 100644 backend/app/src/models/date_time_utc.rs create mode 100644 backend/app/src/models/mod.rs create mode 100644 backend/app/src/models/transaction.rs create mode 100644 backend/app/src/models/transaction_categorisation.rs create mode 100644 backend/app/src/queries/accounts/get_all.rs create mode 100644 backend/app/src/queries/accounts/insert.rs create mode 100644 backend/app/src/queries/accounts/mod.rs create mode 100644 backend/app/src/queries/budget_updates/get_all.rs create mode 100644 backend/app/src/queries/budget_updates/get_first_with_date.rs create mode 100644 backend/app/src/queries/budget_updates/get_most_recent_with_category_id.rs create mode 100644 backend/app/src/queries/budget_updates/mod.rs create mode 100644 backend/app/src/queries/categories/get_all.rs create mode 100644 backend/app/src/queries/categories/get_all_ids.rs create mode 100644 backend/app/src/queries/categories/mod.rs create mode 100644 backend/app/src/queries/category_balances/get_all.rs create mode 100644 backend/app/src/queries/category_balances/mod.rs create mode 100644 backend/app/src/queries/category_budgets/get_most_recent_per_category_id.rs create mode 100644 backend/app/src/queries/category_budgets/mod.rs create mode 100644 backend/app/src/queries/category_transfers/mod.rs create mode 100644 backend/app/src/queries/category_transfers/sum_quantity_per_from_category_id.rs create mode 100644 backend/app/src/queries/category_transfers/sum_quantity_per_to_category_id.rs create mode 100644 backend/app/src/queries/mod.rs create mode 100644 backend/app/src/queries/transaction_categorisations/mod.rs create mode 100644 backend/app/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs create mode 100644 backend/app/src/queries/transaction_categorisations/sum_quantity_per_to_category_id.rs create mode 100644 backend/app/src/routes/account.rs create mode 100644 backend/app/src/routes/balance.rs create mode 100644 backend/app/src/routes/budget.rs create mode 100644 backend/app/src/routes/budget_update.rs create mode 100644 backend/app/src/routes/category.rs create mode 100644 backend/app/src/routes/mod.rs create mode 100644 backend/app/src/schema.rs create mode 100644 backend/app/src/utils/calculate_budgets_accrual.rs create mode 100644 backend/app/src/utils/calculate_category_balance.rs create mode 100644 backend/app/src/utils/find_by_id_or.rs create mode 100644 backend/app/src/utils/mod.rs delete mode 100644 backend/diesel.toml create mode 100644 backend/importers/actualbudget/Cargo.lock create mode 100644 backend/importers/actualbudget/Cargo.toml create mode 100644 backend/importers/actualbudget/src/getters.rs create mode 100644 backend/importers/actualbudget/src/inserters.rs create mode 100644 backend/importers/actualbudget/src/main.rs create mode 100644 backend/importers/actualbudget/src/models/actualbudget/account.rs create mode 100644 backend/importers/actualbudget/src/models/actualbudget/category.rs create mode 100644 backend/importers/actualbudget/src/models/actualbudget/date.rs create mode 100644 backend/importers/actualbudget/src/models/actualbudget/mod.rs create mode 100644 backend/importers/actualbudget/src/models/actualbudget/transaction.rs create mode 100644 backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/account.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/category.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/mod.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/transaction.rs create mode 100644 backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs create mode 100644 backend/importers/actualbudget/src/models/mod.rs create mode 100644 backend/importers/actualbudget/src/transformers.rs delete mode 100644 backend/migrations/.keep delete mode 100644 backend/migrations/2024-08-31-084439_initial_setup/down.sql delete mode 100644 backend/migrations/2024-08-31-084439_initial_setup/up.sql delete mode 100644 backend/src/databases.rs delete mode 100644 backend/src/main.rs delete mode 100644 backend/src/models/account.rs delete mode 100644 backend/src/models/budget.rs delete mode 100644 backend/src/models/budget_update.rs delete mode 100644 backend/src/models/category.rs delete mode 100644 backend/src/models/category_balance.rs delete mode 100644 backend/src/models/category_budget.rs delete mode 100644 backend/src/models/category_transfer.rs delete mode 100644 backend/src/models/date_time_utc.rs delete mode 100644 backend/src/models/mod.rs delete mode 100644 backend/src/models/transaction.rs delete mode 100644 backend/src/models/transaction_categorisation.rs delete mode 100644 backend/src/queries/accounts/get_all.rs delete mode 100644 backend/src/queries/accounts/insert.rs delete mode 100644 backend/src/queries/accounts/mod.rs delete mode 100644 backend/src/queries/budget_updates/get_all.rs delete mode 100644 backend/src/queries/budget_updates/get_first_with_date.rs delete mode 100644 backend/src/queries/budget_updates/get_most_recent_with_category_id.rs delete mode 100644 backend/src/queries/budget_updates/mod.rs delete mode 100644 backend/src/queries/categories/get_all.rs delete mode 100644 backend/src/queries/categories/get_all_ids.rs delete mode 100644 backend/src/queries/categories/mod.rs delete mode 100644 backend/src/queries/category_balances/get_all.rs delete mode 100644 backend/src/queries/category_balances/mod.rs delete mode 100644 backend/src/queries/category_budgets/get_most_recent_per_category_id.rs delete mode 100644 backend/src/queries/category_budgets/mod.rs delete mode 100644 backend/src/queries/category_transfers/mod.rs delete mode 100644 backend/src/queries/category_transfers/sum_quantity_per_from_category_id.rs delete mode 100644 backend/src/queries/category_transfers/sum_quantity_per_to_category_id.rs delete mode 100644 backend/src/queries/mod.rs delete mode 100644 backend/src/queries/transaction_categorisations/mod.rs delete mode 100644 backend/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs delete mode 100644 backend/src/queries/transaction_categorisations/sum_quantity_per_to_category_id.rs delete mode 100644 backend/src/routes/account.rs delete mode 100644 backend/src/routes/balance.rs delete mode 100644 backend/src/routes/budget.rs delete mode 100644 backend/src/routes/budget_update.rs delete mode 100644 backend/src/routes/category.rs delete mode 100644 backend/src/routes/mod.rs delete mode 100644 backend/src/schema.rs delete mode 100644 backend/src/utils/calculate_budgets_accrual.rs delete mode 100644 backend/src/utils/calculate_category_balance.rs delete mode 100644 backend/src/utils/find_by_id_or.rs delete mode 100644 backend/src/utils/mod.rs delete mode 100644 importers/actualbudget/Cargo.lock delete mode 100644 importers/actualbudget/Cargo.toml delete mode 100644 importers/actualbudget/src/getters.rs delete mode 100644 importers/actualbudget/src/inserters.rs delete mode 100644 importers/actualbudget/src/main.rs delete mode 100644 importers/actualbudget/src/models/actualbudget/account.rs delete mode 100644 importers/actualbudget/src/models/actualbudget/category.rs delete mode 100644 importers/actualbudget/src/models/actualbudget/date.rs delete mode 100644 importers/actualbudget/src/models/actualbudget/mod.rs delete mode 100644 importers/actualbudget/src/models/actualbudget/transaction.rs delete mode 100644 importers/actualbudget/src/models/actualbudget/zero_budget.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/account.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/budget_update.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/category.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/category_transfer.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/date_time_utc.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/mod.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/transaction.rs delete mode 100644 importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs delete mode 100644 importers/actualbudget/src/models/mod.rs delete mode 100644 importers/actualbudget/src/transformers.rs diff --git a/backend/Cargo.lock b/backend/Cargo.lock deleted file mode 100644 index f453ebe..0000000 --- a/backend/Cargo.lock +++ /dev/null @@ -1,2011 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "backtrace" -version = "0.3.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "budgeting_app_backend" -version = "0.1.0" -dependencies = [ - "cfg-if", - "chrono", - "diesel", - "diesel_migrations", - "dotenvy", - "rocket", - "rocket_sync_db_pools", - "rusqlite", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytemuck" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" - -[[package]] -name = "cc" -version = "1.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.6", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "devise" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d" -dependencies = [ - "devise_codegen", - "devise_core", -] - -[[package]] -name = "devise_codegen" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" -dependencies = [ - "devise_core", - "quote", -] - -[[package]] -name = "devise_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" -dependencies = [ - "bitflags", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "diesel" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e13bab2796f412722112327f3e575601a3e9cdcbe426f0d30dbf43f3f5dc71" -dependencies = [ - "diesel_derives", - "libsqlite3-sys", - "r2d2", - "time", -] - -[[package]] -name = "diesel_derives" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" -dependencies = [ - "diesel_table_macro_syntax", - "dsl_auto_type", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "diesel_migrations" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" -dependencies = [ - "diesel", - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" -dependencies = [ - "syn", -] - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dsl_auto_type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" -dependencies = [ - "darling", - "either", - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic 0.6.0", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" -dependencies = [ - "equivalent", - "hashbrown", - "serde", -] - -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] -name = "is-terminal" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" -dependencies = [ - "hermit-abi 0.4.0", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "migrations_internals" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" -dependencies = [ - "serde", - "toml", -] - -[[package]] -name = "migrations_macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin", - "tokio", - "tokio-util", - "version_check", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.9", - "libc", -] - -[[package]] -name = "object" -version = "0.36.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - -[[package]] -name = "rocket" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" -dependencies = [ - "async-stream", - "async-trait", - "atomic 0.5.3", - "binascii", - "bytes", - "either", - "figment", - "futures", - "indexmap", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot", - "pin-project-lite", - "rand", - "ref-cast", - "rocket_codegen", - "rocket_http", - "serde", - "serde_json", - "state", - "tempfile", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "ubyte", - "version_check", - "yansi", -] - -[[package]] -name = "rocket_codegen" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" -dependencies = [ - "devise", - "glob", - "indexmap", - "proc-macro2", - "quote", - "rocket_http", - "syn", - "unicode-xid", - "version_check", -] - -[[package]] -name = "rocket_http" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" -dependencies = [ - "cookie", - "either", - "futures", - "http 0.2.12", - "hyper", - "indexmap", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", - "serde", - "smallvec", - "stable-pattern", - "state", - "time", - "tokio", - "uncased", -] - -[[package]] -name = "rocket_sync_db_pools" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f32721ed79509adac4328e97f817a8f55a47c4b64799f6fd6cc3adb6e42ff" -dependencies = [ - "diesel", - "r2d2", - "rocket", - "rocket_sync_db_pools_codegen", - "serde", - "tokio", - "version_check", -] - -[[package]] -name = "rocket_sync_db_pools_codegen" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc890925dc79370c28eb15c9957677093fdb7e8c44966d189f38cedb995ee68" -dependencies = [ - "devise", - "quote", -] - -[[package]] -name = "rusqlite" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" -dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustix" -version = "0.38.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.209" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.209" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.127" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" -dependencies = [ - "serde", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "stable-pattern" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" -dependencies = [ - "memchr", -] - -[[package]] -name = "state" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" -dependencies = [ - "loom", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "2.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "ubyte" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" -dependencies = [ - "serde", -] - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "serde", - "version_check", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-xid" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" -dependencies = [ - "memchr", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -dependencies = [ - "is-terminal", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/backend/Cargo.toml b/backend/Cargo.toml deleted file mode 100644 index af1c8ef..0000000 --- a/backend/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "budgeting_app_backend" -version = "0.1.0" -edition = "2021" - -[dependencies] -chrono = { version = "0.4.38", features = ["serde"] } -cfg-if = "1.0.0" -diesel = { version = "2.2.0", features = ["sqlite"] } -diesel_migrations = "2.2.0" -dotenvy = "0.15.7" -rocket = { version = "0.5.1", features = ["json"] } -rocket_sync_db_pools = { version = "0.1.0", features = ["diesel_sqlite_pool"] } -rusqlite = { version = "0.32.1", features = ["bundled"] } -serde = "1.0.209" diff --git a/backend/Rocket.toml b/backend/Rocket.toml deleted file mode 100644 index bb43355..0000000 --- a/backend/Rocket.toml +++ /dev/null @@ -1,2 +0,0 @@ -[global.databases] -user_data = { url = "/home/joeac/src/budgeting-app/backend/user_data.sqlite" } diff --git a/backend/app/2024-08-31-084439_initial_setup/down.sql b/backend/app/2024-08-31-084439_initial_setup/down.sql new file mode 100644 index 0000000..8cf4b1e --- /dev/null +++ b/backend/app/2024-08-31-084439_initial_setup/down.sql @@ -0,0 +1,6 @@ +DROP TABLE accounts; +DROP TABLE budget_updates; +DROP TABLE categories; +DROP TABLE transaction_categorisations; +DROP TABLE category_transfers; +DROP TABLE transactions; diff --git a/backend/app/2024-08-31-084439_initial_setup/up.sql b/backend/app/2024-08-31-084439_initial_setup/up.sql new file mode 100644 index 0000000..3584ae7 --- /dev/null +++ b/backend/app/2024-08-31-084439_initial_setup/up.sql @@ -0,0 +1,70 @@ +PRAGMA foreign_keys = ON; + +CREATE TABLE accounts( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + opening_balance INTEGER NOT NULL, + opening_date TEXT NOT NULL +); + +CREATE TABLE budget_updates( + id INTEGER NOT NULL PRIMARY KEY, + category_id INTEGER NOT NULL, + date TEXT NOT NULL, + new_budget INTEGER NOT NULL, + new_period INTEGER NOT NULL, + FOREIGN KEY (category_id) + REFERENCES categories (id) + ON UPDATE CASCADE + ON DELETE RESTRICT +); + +CREATE TABLE categories( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT NOT NULL +); + +CREATE TABLE category_transfers( + id INTEGER NOT NULL PRIMARY KEY, + description TEXT NOT NULL, + quantity INTEGER NOT NULL, + from_category_id INTEGER NOT NULL, + to_category_id INTEGER NOT NULL, + FOREIGN KEY (from_category_id) + REFERENCES categories (id) + ON UPDATE CASCADE + ON DELETE RESTRICT, + FOREIGN KEY (to_category_id) + REFERENCES categories (id) + ON UPDATE CASCADE + ON DELETE RESTRICT +); + +CREATE TABLE transactions( + id INTEGER NOT NULL PRIMARY KEY, + description TEXT NOT NULL, + payee TEXT NOT NULL, + quantity INTEGER NOT NULL, + date TEXT NOT NULL, + account_id INTEGER NOT NULL, + FOREIGN KEY (account_id) + REFERENCES accounts (id) + ON UPDATE CASCADE + ON DELETE RESTRICT +); + +CREATE TABLE transaction_categorisations( + id INTEGER NOT NULL PRIMARY KEY, + description TEXT NOT NULL, + quantity INTEGER NOT NULL, + transaction_id INTEGER NOT NULL, + category_id INTEGER NOT NULL, + FOREIGN KEY (transaction_id) + REFERENCES transactions (id) + ON UPDATE CASCADE + ON DELETE RESTRICT, + FOREIGN KEY (category_id) + REFERENCES categories (id) + ON UPDATE CASCADE + ON DELETE RESTRICT +); diff --git a/backend/app/Cargo.lock b/backend/app/Cargo.lock new file mode 100644 index 0000000..f453ebe --- /dev/null +++ b/backend/app/Cargo.lock @@ -0,0 +1,2011 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "budgeting_app_backend" +version = "0.1.0" +dependencies = [ + "cfg-if", + "chrono", + "diesel", + "diesel_migrations", + "dotenvy", + "rocket", + "rocket_sync_db_pools", + "rusqlite", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "devise" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" +dependencies = [ + "devise_core", + "quote", +] + +[[package]] +name = "devise_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" +dependencies = [ + "bitflags", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "diesel" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e13bab2796f412722112327f3e575601a3e9cdcbe426f0d30dbf43f3f5dc71" +dependencies = [ + "diesel_derives", + "libsqlite3-sys", + "r2d2", + "time", +] + +[[package]] +name = "diesel_derives" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" +dependencies = [ + "syn", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic 0.6.0", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "migrations_internals" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rocket" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" +dependencies = [ + "async-stream", + "async-trait", + "atomic 0.5.3", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap", + "log", + "memchr", + "multer", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand", + "ref-cast", + "rocket_codegen", + "rocket_http", + "serde", + "serde_json", + "state", + "tempfile", + "time", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", + "yansi", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" +dependencies = [ + "devise", + "glob", + "indexmap", + "proc-macro2", + "quote", + "rocket_http", + "syn", + "unicode-xid", + "version_check", +] + +[[package]] +name = "rocket_http" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" +dependencies = [ + "cookie", + "either", + "futures", + "http 0.2.12", + "hyper", + "indexmap", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state", + "time", + "tokio", + "uncased", +] + +[[package]] +name = "rocket_sync_db_pools" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83f32721ed79509adac4328e97f817a8f55a47c4b64799f6fd6cc3adb6e42ff" +dependencies = [ + "diesel", + "r2d2", + "rocket", + "rocket_sync_db_pools_codegen", + "serde", + "tokio", + "version_check", +] + +[[package]] +name = "rocket_sync_db_pools_codegen" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc890925dc79370c28eb15c9957677093fdb7e8c44966d189f38cedb995ee68" +dependencies = [ + "devise", + "quote", +] + +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "ubyte" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/backend/app/Cargo.toml b/backend/app/Cargo.toml new file mode 100644 index 0000000..af1c8ef --- /dev/null +++ b/backend/app/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "budgeting_app_backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { version = "0.4.38", features = ["serde"] } +cfg-if = "1.0.0" +diesel = { version = "2.2.0", features = ["sqlite"] } +diesel_migrations = "2.2.0" +dotenvy = "0.15.7" +rocket = { version = "0.5.1", features = ["json"] } +rocket_sync_db_pools = { version = "0.1.0", features = ["diesel_sqlite_pool"] } +rusqlite = { version = "0.32.1", features = ["bundled"] } +serde = "1.0.209" diff --git a/backend/app/Rocket.toml b/backend/app/Rocket.toml new file mode 100644 index 0000000..bb43355 --- /dev/null +++ b/backend/app/Rocket.toml @@ -0,0 +1,2 @@ +[global.databases] +user_data = { url = "/home/joeac/src/budgeting-app/backend/user_data.sqlite" } diff --git a/backend/app/diesel.toml b/backend/app/diesel.toml new file mode 100644 index 0000000..a0d61bf --- /dev/null +++ b/backend/app/diesel.toml @@ -0,0 +1,9 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" +custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] + +[migrations_directory] +dir = "migrations" diff --git a/backend/app/src/databases.rs b/backend/app/src/databases.rs new file mode 100644 index 0000000..7202e78 --- /dev/null +++ b/backend/app/src/databases.rs @@ -0,0 +1,11 @@ +use rocket_sync_db_pools::{database, diesel}; + +cfg_if::cfg_if! { + if #[cfg(test)] { + #[database("user_data.test")] + pub struct UserDataDb(diesel::SqliteConnection); + } else { + #[database("user_data")] + pub struct UserDataDb(diesel::SqliteConnection); + } +} diff --git a/backend/app/src/main.rs b/backend/app/src/main.rs new file mode 100644 index 0000000..c3e02e5 --- /dev/null +++ b/backend/app/src/main.rs @@ -0,0 +1,57 @@ +use diesel::SqliteConnection; +use diesel_migrations::MigrationHarness; +use diesel_migrations::{embed_migrations, EmbeddedMigrations}; +use rocket::fairing::AdHoc; +use rocket::http::ContentType; +use rocket::{launch, routes, Build, Rocket}; + +use crate::databases::UserDataDb; + +pub mod databases; +pub mod models; +pub mod queries; +pub mod routes; +pub mod schema; +pub mod utils; + +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); + +fn run_database_migrations(connection: &mut SqliteConnection) { + connection + .run_pending_migrations(MIGRATIONS) + .expect("Error running database migrations"); +} + +#[launch] +async fn rocket() -> Rocket { + rocket::build() + .attach(UserDataDb::fairing()) + .attach(AdHoc::on_liftoff("Database Migration", |rocket| { + Box::pin(async move { + let user_data_db = UserDataDb::get_one(rocket).await.expect( + "Failed to get connection to user data database in order to run migrations", + ); + user_data_db.run(run_database_migrations).await; + }) + })) + .attach(AdHoc::on_response("Add CORS headers", |_req, res| { + Box::pin(async move { + res.set_header(ContentType::JSON); + res.set_header(rocket::http::Header::new( + "Access-Control-Allow-Origin", + "http://localhost:5173", + )); + }) + })) + .mount( + "/", + routes![ + routes::account::get_all_accounts, + routes::account::create_account, + routes::balance::get_all_category_balances, + routes::budget::get_current_category_budget_per_category, + routes::budget_update::get_all_budget_updates, + routes::category::get_all_categories, + ], + ) +} diff --git a/backend/app/src/models/account.rs b/backend/app/src/models/account.rs new file mode 100644 index 0000000..c10bd5f --- /dev/null +++ b/backend/app/src/models/account.rs @@ -0,0 +1,15 @@ +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; + +use super::date_time_utc::DateTimeUtc; + +#[derive( + Insertable, Queryable, Identifiable, Selectable, Debug, PartialEq, Clone, Serialize, Deserialize, +)] +#[diesel(table_name = crate::schema::accounts)] +pub struct Account { + pub id: i32, + pub name: String, + pub opening_balance: i32, + pub opening_date: DateTimeUtc, +} diff --git a/backend/app/src/models/budget.rs b/backend/app/src/models/budget.rs new file mode 100644 index 0000000..cc18f2e --- /dev/null +++ b/backend/app/src/models/budget.rs @@ -0,0 +1,44 @@ +use serde::Serialize; + +use super::budget_update::BudgetUpdate; + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct Budget { + pub quantity: i32, + pub period: i32, +} + +impl From<&BudgetUpdate> for Budget { + fn from(budget_update: &BudgetUpdate) -> Self { + Self { + quantity: budget_update.new_budget, + period: budget_update.new_period, + } + } +} + +#[cfg(test)] +mod test { + use chrono::Utc; + + use crate::models::date_time_utc::DateTimeUtc; + + use super::*; + + #[test] + fn when_from_budget_update_then_returns_budget() { + let quantity = 2; + let period = 3; + let budget_update = BudgetUpdate { + id: 0, + category_id: 1, + date: DateTimeUtc::new(Utc::now()), + new_budget: quantity, + new_period: period, + }; + + let budget = Budget::from(&budget_update); + + assert_eq!(budget, Budget { quantity, period }); + } +} diff --git a/backend/app/src/models/budget_update.rs b/backend/app/src/models/budget_update.rs new file mode 100644 index 0000000..c63e957 --- /dev/null +++ b/backend/app/src/models/budget_update.rs @@ -0,0 +1,17 @@ +use diesel::prelude::*; +use serde::Serialize; + +use crate::models::category::Category; + +use super::date_time_utc::DateTimeUtc; + +#[derive(Clone, Serialize, Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)] +#[diesel(table_name = crate::schema::budget_updates)] +#[diesel(belongs_to(Category))] +pub struct BudgetUpdate { + pub id: i32, + pub category_id: i32, + pub date: DateTimeUtc, + pub new_budget: i32, + pub new_period: i32, +} diff --git a/backend/app/src/models/category.rs b/backend/app/src/models/category.rs new file mode 100644 index 0000000..5cb2609 --- /dev/null +++ b/backend/app/src/models/category.rs @@ -0,0 +1,9 @@ +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq, Serialize, Deserialize)] +#[diesel(table_name = crate::schema::categories)] +pub struct Category { + pub id: i32, + pub name: String, +} diff --git a/backend/app/src/models/category_balance.rs b/backend/app/src/models/category_balance.rs new file mode 100644 index 0000000..796b40b --- /dev/null +++ b/backend/app/src/models/category_balance.rs @@ -0,0 +1,7 @@ +use serde::Serialize; + +#[derive(Serialize)] +pub struct CategoryBalance { + pub category_id: i32, + pub balance: i32, +} diff --git a/backend/app/src/models/category_budget.rs b/backend/app/src/models/category_budget.rs new file mode 100644 index 0000000..9796261 --- /dev/null +++ b/backend/app/src/models/category_budget.rs @@ -0,0 +1,9 @@ +use serde::Serialize; + +use super::budget::Budget; + +#[derive(Serialize)] +pub struct CategoryBudget { + pub category_id: i32, + pub budget: Option, +} diff --git a/backend/app/src/models/category_transfer.rs b/backend/app/src/models/category_transfer.rs new file mode 100644 index 0000000..6cbda0e --- /dev/null +++ b/backend/app/src/models/category_transfer.rs @@ -0,0 +1,11 @@ +use diesel::prelude::*; + +#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)] +#[diesel(table_name = crate::schema::category_transfers)] +pub struct CategoryTransfer { + pub id: i32, + pub description: String, + pub quantity: i32, + pub from_category_id: i32, + pub to_category_id: i32, +} diff --git a/backend/app/src/models/date_time_utc.rs b/backend/app/src/models/date_time_utc.rs new file mode 100644 index 0000000..03d5802 --- /dev/null +++ b/backend/app/src/models/date_time_utc.rs @@ -0,0 +1,131 @@ +use chrono::{DateTime, Datelike, Utc}; +use diesel::{ + backend::Backend, + deserialize::{self, FromSql, FromSqlRow}, + expression::AsExpression, + serialize::{self, ToSql}, + sql_types::Text, + sqlite::Sqlite, +}; +use serde::{Deserialize, Serialize}; + +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Serialize, + Deserialize, + Copy, + Clone, + Debug, + AsExpression, + FromSqlRow, +)] +#[diesel(sql_type=Text)] +pub struct DateTimeUtc(DateTime); + +impl DateTimeUtc { + pub fn day_diff(&self, other: &DateTimeUtc) -> u32 { + self.0 + .num_days_from_ce() + .abs_diff(other.0.num_days_from_ce()) + } + + pub fn to_day_str(&self) -> String { + self.0 + .to_rfc3339() + .split_at_checked(10) + .map(|(day_str, _)| String::from(day_str)) + .unwrap_or_else(|| String::from("")) + } + + pub fn new(date_time_utc: DateTime) -> Self { + Self(date_time_utc) + } + + pub fn now() -> Self { + Self(Utc::now()) + } +} + +impl ToSql for DateTimeUtc { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, Sqlite>, + ) -> serialize::Result { + out.set_value(self.to_day_str()); + Ok(diesel::serialize::IsNull::No) + } +} + +impl FromSql for DateTimeUtc { + fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { + let str: String = FromSql::::from_sql(bytes)?; + let date_time = str.as_str().parse::>()?; + Ok(DateTimeUtc(date_time)) + } +} + +#[cfg(test)] +mod test { + use chrono::TimeZone; + + use super::*; + + #[test] + fn given_midnight_when_day_diff_next_midnight_less_1s_then_return_0() { + let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc()); + let end = DateTimeUtc::new( + Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59) + .unwrap() + .to_utc(), + ); + + let day_diff = start.day_diff(&end); + let neg_day_diff = end.day_diff(&start); + + assert_eq!(day_diff, 0); + assert_eq!(day_diff, neg_day_diff); + } + + #[test] + fn given_midnight_when_day_diff_next_midnight_then_return_1() { + let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc()); + let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc()); + + let day_diff = start.day_diff(&end); + let neg_day_diff = end.day_diff(&start); + + assert_eq!(day_diff, 1); + assert_eq!(day_diff, neg_day_diff); + } + + #[test] + fn given_midnight_less_1s_when_day_diff_midnight_then_return_1() { + let start = DateTimeUtc::new( + Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59) + .unwrap() + .to_utc(), + ); + let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc()); + + let day_diff = start.day_diff(&end); + let neg_day_diff = end.day_diff(&start); + + assert_eq!(day_diff, 1); + assert_eq!(day_diff, neg_day_diff); + } + + #[test] + fn given_23_nov_1970_when_to_day_str_then_return_iso_day_str() { + let date_time_utc = DateTimeUtc::new( + Utc.with_ymd_and_hms(1970, 11, 23, 0, 0, 0) + .unwrap() + .to_utc(), + ); + let day_str = date_time_utc.to_day_str(); + + assert_eq!(day_str, "1970-11-23"); + } +} diff --git a/backend/app/src/models/mod.rs b/backend/app/src/models/mod.rs new file mode 100644 index 0000000..b790fcb --- /dev/null +++ b/backend/app/src/models/mod.rs @@ -0,0 +1,10 @@ +pub mod account; +pub mod budget; +pub mod budget_update; +pub mod category; +pub mod category_balance; +pub mod category_budget; +pub mod category_transfer; +pub mod date_time_utc; +pub mod transaction; +pub mod transaction_categorisation; diff --git a/backend/app/src/models/transaction.rs b/backend/app/src/models/transaction.rs new file mode 100644 index 0000000..36f33f8 --- /dev/null +++ b/backend/app/src/models/transaction.rs @@ -0,0 +1,17 @@ +use diesel::prelude::*; + +use crate::models::account::Account; + +use super::date_time_utc::DateTimeUtc; + +#[derive(Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)] +#[diesel(table_name = crate::schema::transactions)] +#[diesel(belongs_to(Account))] +pub struct Transaction { + pub id: i32, + pub description: String, + pub payee: String, + pub quantity: i32, + pub date: DateTimeUtc, + pub account_id: i32, +} diff --git a/backend/app/src/models/transaction_categorisation.rs b/backend/app/src/models/transaction_categorisation.rs new file mode 100644 index 0000000..d2867f1 --- /dev/null +++ b/backend/app/src/models/transaction_categorisation.rs @@ -0,0 +1,11 @@ +use diesel::prelude::*; + +#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)] +#[diesel(table_name = crate::schema::transaction_categorisations)] +pub struct TransactionCategorisations { + pub id: i32, + pub description: String, + pub quantity: i32, + pub transaction_id: i32, + pub category_id: i32, +} diff --git a/backend/app/src/queries/accounts/get_all.rs b/backend/app/src/queries/accounts/get_all.rs new file mode 100644 index 0000000..12941ac --- /dev/null +++ b/backend/app/src/queries/accounts/get_all.rs @@ -0,0 +1,7 @@ +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +use crate::{models::account::Account, schema::accounts::dsl::accounts}; + +pub fn get_all_accounts(connection: &mut SqliteConnection) -> Result, Error> { + accounts.select(Account::as_select()).load(connection) +} diff --git a/backend/app/src/queries/accounts/insert.rs b/backend/app/src/queries/accounts/insert.rs new file mode 100644 index 0000000..c20cf60 --- /dev/null +++ b/backend/app/src/queries/accounts/insert.rs @@ -0,0 +1,14 @@ +use diesel::prelude::*; +use diesel::result::Error; + +use crate::models::account::Account; +use crate::schema::accounts::dsl::accounts as accounts_table; + +pub fn insert_account( + accounts: &[Account], + connection: &mut SqliteConnection, +) -> Result { + diesel::insert_into(accounts_table) + .values(accounts) + .execute(connection) +} diff --git a/backend/app/src/queries/accounts/mod.rs b/backend/app/src/queries/accounts/mod.rs new file mode 100644 index 0000000..94fa360 --- /dev/null +++ b/backend/app/src/queries/accounts/mod.rs @@ -0,0 +1,5 @@ +mod get_all; +mod insert; + +pub use get_all::get_all_accounts; +pub use insert::insert_account; diff --git a/backend/app/src/queries/budget_updates/get_all.rs b/backend/app/src/queries/budget_updates/get_all.rs new file mode 100644 index 0000000..97f2f2f --- /dev/null +++ b/backend/app/src/queries/budget_updates/get_all.rs @@ -0,0 +1,13 @@ +use crate::{ + models::budget_update::BudgetUpdate, + schema::budget_updates::dsl::budget_updates as budget_updates_table, +}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +pub fn get_all_budget_updates( + connection: &mut SqliteConnection, +) -> Result, Error> { + budget_updates_table + .select(BudgetUpdate::as_select()) + .load::(connection) +} diff --git a/backend/app/src/queries/budget_updates/get_first_with_date.rs b/backend/app/src/queries/budget_updates/get_first_with_date.rs new file mode 100644 index 0000000..a2824be --- /dev/null +++ b/backend/app/src/queries/budget_updates/get_first_with_date.rs @@ -0,0 +1,19 @@ +use crate::{ + models::budget_update::BudgetUpdate, + schema::budget_updates::{ + self as budget_updates_schema, dsl::budget_updates as budget_updates_table, + }, +}; +use diesel::{ + result::Error, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection, +}; + +pub fn get_first_budget_update_with_date( + date: &str, + connection: &mut SqliteConnection, +) -> Result { + budget_updates_table + .select(BudgetUpdate::as_select()) + .filter(budget_updates_schema::date.eq(date)) + .first(connection) +} diff --git a/backend/app/src/queries/budget_updates/get_most_recent_with_category_id.rs b/backend/app/src/queries/budget_updates/get_most_recent_with_category_id.rs new file mode 100644 index 0000000..4753092 --- /dev/null +++ b/backend/app/src/queries/budget_updates/get_most_recent_with_category_id.rs @@ -0,0 +1,18 @@ +use crate::{ + models::date_time_utc::DateTimeUtc, + schema::budget_updates::{ + self as budget_updates_schema, dsl::budget_updates as budget_updates_table, + }, +}; +use diesel::{dsl::max, result::Error, ExpressionMethods, QueryDsl, RunQueryDsl, SqliteConnection}; + +pub fn get_most_recent_budget_update_date_with_category_id( + category_id: i32, + connection: &mut SqliteConnection, +) -> Result, Error> { + budget_updates_table + .select(max(budget_updates_schema::date)) + .filter(budget_updates_schema::category_id.eq(category_id)) + .filter(budget_updates_schema::date.le(DateTimeUtc::now().to_day_str())) + .first(connection) +} diff --git a/backend/app/src/queries/budget_updates/mod.rs b/backend/app/src/queries/budget_updates/mod.rs new file mode 100644 index 0000000..3b514dd --- /dev/null +++ b/backend/app/src/queries/budget_updates/mod.rs @@ -0,0 +1,7 @@ +mod get_all; +mod get_first_with_date; +mod get_most_recent_with_category_id; + +pub use get_all::get_all_budget_updates; +pub use get_first_with_date::get_first_budget_update_with_date; +pub use get_most_recent_with_category_id::get_most_recent_budget_update_date_with_category_id; diff --git a/backend/app/src/queries/categories/get_all.rs b/backend/app/src/queries/categories/get_all.rs new file mode 100644 index 0000000..db5a073 --- /dev/null +++ b/backend/app/src/queries/categories/get_all.rs @@ -0,0 +1,8 @@ +use crate::{models::category::Category, schema::categories::dsl::categories as categories_table}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +pub fn get_all_categories(connection: &mut SqliteConnection) -> Result, Error> { + categories_table + .select(Category::as_select()) + .load(connection) +} diff --git a/backend/app/src/queries/categories/get_all_ids.rs b/backend/app/src/queries/categories/get_all_ids.rs new file mode 100644 index 0000000..7c7c912 --- /dev/null +++ b/backend/app/src/queries/categories/get_all_ids.rs @@ -0,0 +1,8 @@ +use crate::schema::categories::{self as categories_schema, dsl::categories as categories_table}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; + +pub fn get_all_category_ids(connection: &mut SqliteConnection) -> Result, Error> { + categories_table + .select(categories_schema::id) + .load(connection) +} diff --git a/backend/app/src/queries/categories/mod.rs b/backend/app/src/queries/categories/mod.rs new file mode 100644 index 0000000..80356b3 --- /dev/null +++ b/backend/app/src/queries/categories/mod.rs @@ -0,0 +1,5 @@ +mod get_all; +mod get_all_ids; + +pub use self::get_all::get_all_categories; +pub use self::get_all_ids::get_all_category_ids; diff --git a/backend/app/src/queries/category_balances/get_all.rs b/backend/app/src/queries/category_balances/get_all.rs new file mode 100644 index 0000000..713fbeb --- /dev/null +++ b/backend/app/src/queries/category_balances/get_all.rs @@ -0,0 +1,43 @@ +use crate::{ + models::category_balance::CategoryBalance, + queries::{ + budget_updates::get_all_budget_updates, + categories::get_all_categories, + category_transfers::{ + sum_category_transfer_quantity_per_from_category_id, + sum_category_transfer_quantity_per_to_category_id, + }, + transaction_categorisations::sum_transaction_categorisation_quantity_per_category_id, + }, + utils::calculate_category_balance, +}; +use diesel::{result::Error, SqliteConnection}; + +pub fn get_all_category_balances( + connection: &mut SqliteConnection, +) -> Result, Error> { + let categories = get_all_categories(connection)?; + let transaction_sums = sum_transaction_categorisation_quantity_per_category_id(connection)?; + let category_transfer_from_sums = + sum_category_transfer_quantity_per_from_category_id(connection)?; + let category_transfer_to_sums = sum_category_transfer_quantity_per_to_category_id(connection)?; + let budget_updates = get_all_budget_updates(connection)?; + + let mut balances = Vec::::new(); + for category in categories { + let balance = calculate_category_balance( + &category, + &transaction_sums, + &category_transfer_from_sums, + &category_transfer_to_sums, + &budget_updates, + ); + + balances.push(CategoryBalance { + category_id: category.id, + balance: balance.try_into().unwrap(), + }) + } + + Ok(balances) +} diff --git a/backend/app/src/queries/category_balances/mod.rs b/backend/app/src/queries/category_balances/mod.rs new file mode 100644 index 0000000..3fc1be2 --- /dev/null +++ b/backend/app/src/queries/category_balances/mod.rs @@ -0,0 +1,3 @@ +mod get_all; + +pub use get_all::get_all_category_balances; diff --git a/backend/app/src/queries/category_budgets/get_most_recent_per_category_id.rs b/backend/app/src/queries/category_budgets/get_most_recent_per_category_id.rs new file mode 100644 index 0000000..6b76efd --- /dev/null +++ b/backend/app/src/queries/category_budgets/get_most_recent_per_category_id.rs @@ -0,0 +1,42 @@ +use crate::{ + models::{budget::Budget, category_budget::CategoryBudget}, + queries::{ + budget_updates::{ + get_first_budget_update_with_date, get_most_recent_budget_update_date_with_category_id, + }, + categories::get_all_category_ids, + }, +}; +use diesel::{result::Error, SqliteConnection}; + +pub fn get_most_recent_category_budget_per_category_id( + connection: &mut SqliteConnection, +) -> Result, Error> { + let category_ids: Vec = get_all_category_ids(connection)?; + + let mut budgets = Vec::::new(); + for category_id in category_ids { + let most_recent_budget_update_date = + get_most_recent_budget_update_date_with_category_id(category_id, connection)?; + + if most_recent_budget_update_date.is_none() { + budgets.push(CategoryBudget { + category_id, + budget: Option::None, + }); + continue; + } + + let most_recent_budget_update = get_first_budget_update_with_date( + most_recent_budget_update_date.unwrap().as_str(), + connection, + )?; + + budgets.push(CategoryBudget { + category_id, + budget: Some(Budget::from(&most_recent_budget_update)), + }); + } + + Ok(budgets) +} diff --git a/backend/app/src/queries/category_budgets/mod.rs b/backend/app/src/queries/category_budgets/mod.rs new file mode 100644 index 0000000..29fc246 --- /dev/null +++ b/backend/app/src/queries/category_budgets/mod.rs @@ -0,0 +1,3 @@ +mod get_most_recent_per_category_id; + +pub use self::get_most_recent_per_category_id::get_most_recent_category_budget_per_category_id; diff --git a/backend/app/src/queries/category_transfers/mod.rs b/backend/app/src/queries/category_transfers/mod.rs new file mode 100644 index 0000000..85eb1c8 --- /dev/null +++ b/backend/app/src/queries/category_transfers/mod.rs @@ -0,0 +1,5 @@ +mod sum_quantity_per_from_category_id; +mod sum_quantity_per_to_category_id; + +pub use sum_quantity_per_from_category_id::sum_category_transfer_quantity_per_from_category_id; +pub use sum_quantity_per_to_category_id::sum_category_transfer_quantity_per_to_category_id; diff --git a/backend/app/src/queries/category_transfers/sum_quantity_per_from_category_id.rs b/backend/app/src/queries/category_transfers/sum_quantity_per_from_category_id.rs new file mode 100644 index 0000000..ae3a51a --- /dev/null +++ b/backend/app/src/queries/category_transfers/sum_quantity_per_from_category_id.rs @@ -0,0 +1,17 @@ +use diesel::{dsl::sum, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; + +use crate::schema::category_transfers::{ + self as category_transfers_schema, dsl::category_transfers as category_transfers_table, +}; + +pub fn sum_category_transfer_quantity_per_from_category_id( + connection: &mut SqliteConnection, +) -> Result)>, Error> { + category_transfers_table + .group_by(category_transfers_schema::from_category_id) + .select(( + category_transfers_schema::from_category_id, + sum(category_transfers_schema::quantity), + )) + .load::<(i32, Option)>(connection) +} diff --git a/backend/app/src/queries/category_transfers/sum_quantity_per_to_category_id.rs b/backend/app/src/queries/category_transfers/sum_quantity_per_to_category_id.rs new file mode 100644 index 0000000..fe6f3a7 --- /dev/null +++ b/backend/app/src/queries/category_transfers/sum_quantity_per_to_category_id.rs @@ -0,0 +1,17 @@ +use diesel::{dsl::sum, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; + +use crate::schema::category_transfers::{ + self as category_transfers_schema, dsl::category_transfers as category_transfers_table, +}; + +pub fn sum_category_transfer_quantity_per_to_category_id( + connection: &mut SqliteConnection, +) -> Result)>, Error> { + category_transfers_table + .group_by(category_transfers_schema::to_category_id) + .select(( + category_transfers_schema::to_category_id, + sum(category_transfers_schema::quantity), + )) + .load::<(i32, Option)>(connection) +} diff --git a/backend/app/src/queries/mod.rs b/backend/app/src/queries/mod.rs new file mode 100644 index 0000000..43328e2 --- /dev/null +++ b/backend/app/src/queries/mod.rs @@ -0,0 +1,7 @@ +pub mod accounts; +pub mod budget_updates; +pub mod categories; +pub mod category_balances; +pub mod category_budgets; +pub mod category_transfers; +pub mod transaction_categorisations; diff --git a/backend/app/src/queries/transaction_categorisations/mod.rs b/backend/app/src/queries/transaction_categorisations/mod.rs new file mode 100644 index 0000000..d4217cc --- /dev/null +++ b/backend/app/src/queries/transaction_categorisations/mod.rs @@ -0,0 +1,3 @@ +mod sum_quantity_per_category_id; + +pub use sum_quantity_per_category_id::sum_transaction_categorisation_quantity_per_category_id; diff --git a/backend/app/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs b/backend/app/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs new file mode 100644 index 0000000..7f93e0c --- /dev/null +++ b/backend/app/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs @@ -0,0 +1,17 @@ +use crate::schema::transaction_categorisations::{ + self as transaction_categorisations_schema, + dsl::transaction_categorisations as transaction_categorisations_table, +}; +use diesel::{dsl::sum, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; + +pub fn sum_transaction_categorisation_quantity_per_category_id( + connection: &mut SqliteConnection, +) -> Result)>, Error> { + transaction_categorisations_table + .group_by(transaction_categorisations_schema::category_id) + .select(( + transaction_categorisations_schema::category_id, + sum(transaction_categorisations_schema::quantity), + )) + .load::<(i32, Option)>(connection) +} diff --git a/backend/app/src/queries/transaction_categorisations/sum_quantity_per_to_category_id.rs b/backend/app/src/queries/transaction_categorisations/sum_quantity_per_to_category_id.rs new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/src/routes/account.rs b/backend/app/src/routes/account.rs new file mode 100644 index 0000000..7bbdb4f --- /dev/null +++ b/backend/app/src/routes/account.rs @@ -0,0 +1,57 @@ +use diesel::result::DatabaseErrorKind; +use rocket::http::Status; +use rocket::serde::json::Json; +use rocket::{get, post, Responder}; + +use crate::databases::UserDataDb; +use crate::models::account::Account; +use crate::queries::accounts::{get_all_accounts as get_all_accounts_query, insert_account}; + +#[get("/account")] +pub async fn get_all_accounts(user_data_db: UserDataDb) -> Result>, Status> { + user_data_db + .run(get_all_accounts_query) + .await + .map(Json) + .map_err(|_| Status { code: 500 }) +} + +#[derive(Responder)] +pub enum CreateAccountResponder { + #[response(status = 500, content_type = "text")] + InternalServerError(String), + + #[response(status = 202, content_type = "json")] + Created(Json), + + #[response(status = 400, content_type = "text")] + BadRequest(String), +} + +#[post("/account", data = "", format = "json")] +pub async fn create_account( + account: Json, + user_data_db: UserDataDb, +) -> CreateAccountResponder { + let account = account.into_inner(); + let accounts_vec = vec![account.clone()]; + + let num_rows_inserted = user_data_db + .run(move |connection| insert_account(&accounts_vec, connection)) + .await; + + match num_rows_inserted { + Ok(_) => CreateAccountResponder::Created(Json(account)), + Err(err) => match err { + diesel::result::Error::DatabaseError(DatabaseErrorKind::UniqueViolation, info) => { + let column = info.column_name().unwrap_or("account"); + CreateAccountResponder::BadRequest(format!( + "Error creating account: {column} was not unique." + )) + } + _ => CreateAccountResponder::InternalServerError(format!( + "Error creating account: {err}" + )), + }, + } +} diff --git a/backend/app/src/routes/balance.rs b/backend/app/src/routes/balance.rs new file mode 100644 index 0000000..f423c90 --- /dev/null +++ b/backend/app/src/routes/balance.rs @@ -0,0 +1,17 @@ +use rocket::{get, http::Status, serde::json::Json}; + +use crate::{ + databases::UserDataDb, models::category_balance::CategoryBalance, + queries::category_balances::get_all_category_balances as get_all_category_balances_query, +}; + +#[get("/balance/category")] +pub async fn get_all_category_balances( + user_data_db: UserDataDb, +) -> Result>, Status> { + user_data_db + .run(get_all_category_balances_query) + .await + .map(Json) + .map_err(|_| Status { code: 500 }) +} diff --git a/backend/app/src/routes/budget.rs b/backend/app/src/routes/budget.rs new file mode 100644 index 0000000..67aae8e --- /dev/null +++ b/backend/app/src/routes/budget.rs @@ -0,0 +1,18 @@ +use rocket::get; +use rocket::http::Status; +use rocket::serde::json::Json; + +use crate::databases::UserDataDb; +use crate::models::category_budget::CategoryBudget; +use crate::queries; + +#[get("/budget/category?current")] +pub async fn get_current_category_budget_per_category( + user_data_db: UserDataDb, +) -> Result>, Status> { + user_data_db + .run(queries::category_budgets::get_most_recent_category_budget_per_category_id) + .await + .map(Json) + .map_err(|_| Status { code: 500 }) +} diff --git a/backend/app/src/routes/budget_update.rs b/backend/app/src/routes/budget_update.rs new file mode 100644 index 0000000..7732c7b --- /dev/null +++ b/backend/app/src/routes/budget_update.rs @@ -0,0 +1,18 @@ +use rocket::get; +use rocket::http::Status; +use rocket::serde::json::Json; + +use crate::databases::UserDataDb; +use crate::models::budget_update::BudgetUpdate; +use crate::queries; + +#[get("/budget-update")] +pub async fn get_all_budget_updates( + user_data_db: UserDataDb, +) -> Result>, Status> { + user_data_db + .run(queries::budget_updates::get_all_budget_updates) + .await + .map(Json) + .map_err(|_| Status { code: 500 }) +} diff --git a/backend/app/src/routes/category.rs b/backend/app/src/routes/category.rs new file mode 100644 index 0000000..01fd8d1 --- /dev/null +++ b/backend/app/src/routes/category.rs @@ -0,0 +1,16 @@ +use rocket::get; +use rocket::http::Status; +use rocket::serde::json::Json; + +use crate::databases::UserDataDb; +use crate::models::category::Category; +use crate::queries::categories::get_all_categories as get_all_categories_query; + +#[get("/category")] +pub async fn get_all_categories(user_data_db: UserDataDb) -> Result>, Status> { + user_data_db + .run(get_all_categories_query) + .await + .map(Json) + .map_err(|_| Status { code: 500 }) +} diff --git a/backend/app/src/routes/mod.rs b/backend/app/src/routes/mod.rs new file mode 100644 index 0000000..bf0ab4a --- /dev/null +++ b/backend/app/src/routes/mod.rs @@ -0,0 +1,5 @@ +pub mod account; +pub mod balance; +pub mod budget; +pub mod budget_update; +pub mod category; diff --git a/backend/app/src/schema.rs b/backend/app/src/schema.rs new file mode 100644 index 0000000..372f8f3 --- /dev/null +++ b/backend/app/src/schema.rs @@ -0,0 +1,72 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + accounts (id) { + id -> Integer, + name -> Text, + opening_balance -> Integer, + opening_date -> Text, + } +} + +diesel::table! { + budget_updates (id) { + id -> Integer, + category_id -> Integer, + date -> Text, + new_budget -> Integer, + new_period -> Integer, + } +} + +diesel::table! { + categories (id) { + id -> Integer, + name -> Text, + } +} + +diesel::table! { + category_transfers (id) { + id -> Integer, + description -> Text, + quantity -> Integer, + from_category_id -> Integer, + to_category_id -> Integer, + } +} + +diesel::table! { + transaction_categorisations (id) { + id -> Integer, + description -> Text, + quantity -> Integer, + transaction_id -> Integer, + category_id -> Integer, + } +} + +diesel::table! { + transactions (id) { + id -> Integer, + description -> Text, + payee -> Text, + quantity -> Integer, + date -> Text, + account_id -> Integer, + } +} + +diesel::joinable!(budget_updates -> categories (category_id)); +diesel::joinable!(transaction_categorisations -> categories (category_id)); +diesel::joinable!(transaction_categorisations -> transactions (transaction_id)); +diesel::joinable!(transactions -> accounts (account_id)); + +diesel::allow_tables_to_appear_in_same_query!( + accounts, + budget_updates, + categories, + category_transfers, + transaction_categorisations, + transactions, +); diff --git a/backend/app/src/utils/calculate_budgets_accrual.rs b/backend/app/src/utils/calculate_budgets_accrual.rs new file mode 100644 index 0000000..f14260d --- /dev/null +++ b/backend/app/src/utils/calculate_budgets_accrual.rs @@ -0,0 +1,159 @@ +use crate::models::{budget_update::BudgetUpdate, category::Category, date_time_utc::DateTimeUtc}; + +pub fn calculate_budgets_accrual(category: &Category, budget_updates: &[BudgetUpdate]) -> i64 { + let mut budget_updates = budget_updates + .iter() + .filter(|bu| bu.category_id == category.id) + .collect::>(); + budget_updates.sort_by_key(|bu| bu.date); + budget_updates.reverse(); + + let mut budgets_accrual = 0.0_f64; + let mut calculated_back_to_date = DateTimeUtc::now(); + + for budget_update in budget_updates { + let days_on_this_budget = calculated_back_to_date.day_diff(&budget_update.date); + if days_on_this_budget == 0_u32 { + continue; + } + let budget_per_day = budget_update.new_budget as f64 / budget_update.new_period as f64; + budgets_accrual += days_on_this_budget as f64 * budget_per_day; + calculated_back_to_date = budget_update.date; + } + + budgets_accrual.floor() as i64 +} + +#[cfg(test)] +mod test { + use chrono::{Days, Timelike, Utc}; + + use crate::models::{ + budget_update::BudgetUpdate, category::Category, date_time_utc::DateTimeUtc, + }; + + use super::calculate_budgets_accrual; + + #[test] + fn when_no_budget_updates_then_return_zero() { + let category = Category { + id: 0, + name: String::from("Groceries"), + }; + let budget_updates = []; + + let result = calculate_budgets_accrual(&category, &budget_updates); + + assert_eq!(result, 0_i64); + } + + #[test] + fn when_one_budget_update_for_another_category_then_return_zero() { + let category = Category { + id: 0, + name: String::from("Groceries"), + }; + let budget_updates = [BudgetUpdate { + id: 0, + category_id: 1, + date: DateTimeUtc::now(), + new_budget: 100, + new_period: 1, + }]; + + let result = calculate_budgets_accrual(&category, &budget_updates); + + assert_eq!(result, 0_i64); + } + + #[test] + fn when_one_budget_update_then_return_quantity_times_days_in_past() { + let category = Category { + id: 0, + name: String::from("Groceries"), + }; + let budget_updates = [BudgetUpdate { + id: 0, + category_id: 0, + date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), + new_budget: 100, + new_period: 1, + }]; + + let result = calculate_budgets_accrual(&category, &budget_updates); + + assert_eq!(result, 12_000); + } + + #[test] + fn when_two_budget_updates_then_calculates_as_expected() { + let category = Category { + id: 0, + name: String::from("Groceries"), + }; + let budget_updates = [ + BudgetUpdate { + id: 0, + category_id: 0, + date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(60)).unwrap()), + new_budget: 200, + new_period: 1, + }, + BudgetUpdate { + id: 0, + category_id: 0, + date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), + new_budget: 100, + new_period: 1, + }, + ]; + + let result = calculate_budgets_accrual(&category, &budget_updates); + + assert_eq!(result, 18_000); + } + + #[test] + fn when_two_budget_updates_on_same_day_then_uses_latest_only() { + let category = Category { + id: 0, + name: String::from("Groceries"), + }; + let budget_updates = [ + BudgetUpdate { + id: 0, + category_id: 0, + date: DateTimeUtc::new( + Utc::now() + .checked_sub_days(Days::new(120)) + .unwrap() + .with_hour(13) + .unwrap(), + ), + new_budget: 200, + new_period: 1, + }, + BudgetUpdate { + id: 0, + category_id: 0, + date: DateTimeUtc::new( + Utc::now() + .checked_sub_days(Days::new(120)) + .unwrap() + .with_hour(12) + .unwrap(), + ), + new_budget: 100, + new_period: 1, + }, + ]; + let mut budget_updates_reversed = budget_updates.clone(); + budget_updates_reversed.reverse(); + + let result_leftways = calculate_budgets_accrual(&category, &budget_updates); + let result_rightways = calculate_budgets_accrual(&category, &budget_updates_reversed); + + assert_eq!(result_leftways, 24_000); + assert_eq!(result_rightways, 24_000); + } +} diff --git a/backend/app/src/utils/calculate_category_balance.rs b/backend/app/src/utils/calculate_category_balance.rs new file mode 100644 index 0000000..3c1c62f --- /dev/null +++ b/backend/app/src/utils/calculate_category_balance.rs @@ -0,0 +1,141 @@ +use crate::models::{budget_update::BudgetUpdate, category::Category}; + +use super::{calculate_budgets_accrual, find_by_id_or}; + +pub fn calculate_category_balance( + category: &Category, + transaction_sums: &[(i32, Option)], + category_transfer_from_sums: &[(i32, Option)], + category_transfer_to_sums: &[(i32, Option)], + budget_updates: &[BudgetUpdate], +) -> i64 { + let transaction_sum = find_by_id_or(transaction_sums, category.id, 0); + let category_transfer_from_sum = find_by_id_or(category_transfer_from_sums, category.id, 0); + let category_transfer_to_sum = find_by_id_or(category_transfer_to_sums, category.id, 0); + let budget_accruals_sum = calculate_budgets_accrual(category, budget_updates); + + transaction_sum + budget_accruals_sum + category_transfer_to_sum - category_transfer_from_sum +} + +#[cfg(test)] +mod test { + use chrono::{Days, Utc}; + + use crate::{ + models::{budget_update::BudgetUpdate, category::Category, date_time_utc::DateTimeUtc}, + utils::calculate_category_balance, + }; + + #[test] + fn when_no_data_then_returns_zero() { + let category = Category { + id: 0, + name: String::new(), + }; + + let result = calculate_category_balance(&category, &[], &[], &[], &[]); + + assert_eq!(result, 0_i64); + } + + #[test] + fn when_data_only_for_another_category_then_returns_zero() { + let category = Category { + id: 0, + name: String::new(), + }; + let transaction_sums = [(1, Some(100))]; + let category_transfer_from_sums = [(1, Some(91))]; + let category_transfer_to_sums = [(2, Some(79))]; + let budget_updates = [BudgetUpdate { + id: 0, + category_id: 1, + date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), + new_budget: 77, + new_period: 1, + }]; + + let result = calculate_category_balance( + &category, + &transaction_sums, + &category_transfer_from_sums, + &category_transfer_to_sums, + &budget_updates, + ); + + assert_eq!(result, 0_i64); + } + + #[test] + fn when_transaction_sum_is_none_then_returns_zero() { + let category = Category { + id: 0, + name: String::new(), + }; + let transaction_sums = [(0, None)]; + let category_transfer_from_sums = []; + let category_transfer_to_sums = []; + let budget_updates = []; + + let result = calculate_category_balance( + &category, + &transaction_sums, + &category_transfer_from_sums, + &category_transfer_to_sums, + &budget_updates, + ); + + assert_eq!(result, 0_i64); + } + + #[test] + fn when_category_transfers_are_none_then_returns_zero() { + let category = Category { + id: 0, + name: String::new(), + }; + let transaction_sums = []; + let category_transfer_from_sums = [(0, None)]; + let category_transfer_to_sums = [(0, None)]; + let budget_updates = []; + + let result = calculate_category_balance( + &category, + &transaction_sums, + &category_transfer_from_sums, + &category_transfer_to_sums, + &budget_updates, + ); + + assert_eq!(result, 0_i64); + } + + #[test] + fn when_some_data_then_calculates_as_expected() { + let category = Category { + id: 0, + name: String::new(), + }; + let transaction_sums = [(0, Some(100))]; + let category_transfer_from_sums = [(0, Some(91))]; + let category_transfer_to_sums = [(0, Some(79))]; + let budget_updates = [BudgetUpdate { + id: 0, + category_id: 0, + date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), + new_budget: 77, + new_period: 1, + }]; + + let result = calculate_category_balance( + &category, + &transaction_sums, + &category_transfer_from_sums, + &category_transfer_to_sums, + &budget_updates, + ); + + let expected_result = 100 + (-91) + 79 + 77 * 120; + assert_eq!(result, expected_result); + } +} diff --git a/backend/app/src/utils/find_by_id_or.rs b/backend/app/src/utils/find_by_id_or.rs new file mode 100644 index 0000000..f5add30 --- /dev/null +++ b/backend/app/src/utils/find_by_id_or.rs @@ -0,0 +1,53 @@ +pub fn find_by_id_or( + arr: &[(Id, Option)], + id: Id, + default: Val, +) -> Val { + arr.iter() + .find(|(elem_id, _)| *elem_id == id) + .map_or(default, |row| match row { + (_, Some(quantity)) => *quantity, + (_, _) => default, + }) +} + +#[cfg(test)] +mod test { + use super::find_by_id_or; + + #[test] + fn when_no_elements_then_return_default() { + let arr = []; + + let result = find_by_id_or(&arr, 1, 42); + + assert_eq!(result, 42); + } + + #[test] + fn when_id_not_in_elements_then_return_default() { + let arr = [(0, Some(100)), (2, Some(200))]; + + let result = find_by_id_or(&arr, 1, 42); + + assert_eq!(result, 42); + } + + #[test] + fn when_value_is_none_then_return_default() { + let arr = [(1, None)]; + + let result = find_by_id_or(&arr, 1, 42); + + assert_eq!(result, 42); + } + + #[test] + fn when_value_is_some_then_return_value() { + let arr = [(1, Some(67))]; + + let result = find_by_id_or(&arr, 1, 42); + + assert_eq!(result, 67); + } +} diff --git a/backend/app/src/utils/mod.rs b/backend/app/src/utils/mod.rs new file mode 100644 index 0000000..84a27af --- /dev/null +++ b/backend/app/src/utils/mod.rs @@ -0,0 +1,7 @@ +mod calculate_budgets_accrual; +mod calculate_category_balance; +mod find_by_id_or; + +pub use calculate_budgets_accrual::calculate_budgets_accrual; +pub use calculate_category_balance::calculate_category_balance; +pub use find_by_id_or::find_by_id_or; diff --git a/backend/diesel.toml b/backend/diesel.toml deleted file mode 100644 index a0d61bf..0000000 --- a/backend/diesel.toml +++ /dev/null @@ -1,9 +0,0 @@ -# For documentation on how to configure this file, -# see https://diesel.rs/guides/configuring-diesel-cli - -[print_schema] -file = "src/schema.rs" -custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] - -[migrations_directory] -dir = "migrations" diff --git a/backend/importers/actualbudget/Cargo.lock b/backend/importers/actualbudget/Cargo.lock new file mode 100644 index 0000000..63a53b9 --- /dev/null +++ b/backend/importers/actualbudget/Cargo.lock @@ -0,0 +1,356 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actualbudget" +version = "0.1.0" +dependencies = [ + "chrono", + "sqlite", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "sqlite" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfe6fb16f2bee6452feeb4d12bfa404fbcd3cfc121b2950e501d1ae9cae718e" +dependencies = [ + "sqlite3-sys", +] + +[[package]] +name = "sqlite3-src" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "174d4a6df77c27db281fb23de1a6d968f3aaaa4807c2a1afa8056b971f947b4a" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "sqlite3-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3901ada7090c3c3584dc92ec7ef1b7091868d13bfe6d7de9f0bcaffee7d0ade5" +dependencies = [ + "sqlite3-src", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/backend/importers/actualbudget/Cargo.toml b/backend/importers/actualbudget/Cargo.toml new file mode 100644 index 0000000..7cc3e1b --- /dev/null +++ b/backend/importers/actualbudget/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "actualbudget" +version = "0.1.0" +edition = "2021" + +[dependencies] +sqlite = "0.36.1" +chrono = { version = "0.4.38", features = ["serde"] } diff --git a/backend/importers/actualbudget/src/getters.rs b/backend/importers/actualbudget/src/getters.rs new file mode 100644 index 0000000..88a660d --- /dev/null +++ b/backend/importers/actualbudget/src/getters.rs @@ -0,0 +1,61 @@ +use crate::models::actualbudget::{ + account::Account, category::Category, date::Date, transaction::Transaction, + zero_budget::ZeroBudget, +}; +use sqlite::Connection; + +pub fn get_first_transaction_date(connection: &Connection) -> Date { + let mut statement = connection + .prepare("select min(date) as min_date from transactions;") + .unwrap(); + match statement.next() { + Ok(sqlite::State::Row) => Date::from_date_row(&statement, "min_date"), + err => panic!("Could not find first transaction date: {:?}", err), + } +} + +pub fn get_accounts(connection: &Connection) -> Vec { + let mut statement = connection.prepare("select * from accounts;").unwrap(); + let mut accounts = Vec::::new(); + while let Ok(sqlite::State::Row) = statement.next() { + accounts.push(Account::from_row(&statement)); + } + accounts +} + +pub fn get_categories(connection: &Connection) -> Vec { + let mut statement = connection.prepare("select * from categories;").unwrap(); + let mut categories = Vec::::new(); + while let Ok(sqlite::State::Row) = statement.next() { + categories.push(Category::from_row(&statement)); + } + categories +} + +pub fn get_zero_budgets(connection: &Connection) -> Vec { + let mut statement = connection.prepare("select * from zero_budgets;").unwrap(); + let mut zero_budgets = Vec::::new(); + while let Ok(sqlite::State::Row) = statement.next() { + zero_budgets.push(ZeroBudget::from_row(&statement)); + } + zero_budgets +} + +pub fn get_category_transfers_account(connection: &Connection) -> Account { + let mut statement = connection + .prepare("select * from accounts where name = 'Category transfers';") + .unwrap(); + match statement.next() { + Ok(sqlite::State::Row) => Account::from_row(&statement), + _ => panic!("Could not determine category transfers account"), + } +} + +pub fn get_transactions(connection: &Connection) -> Vec { + let mut statement = connection.prepare("select * from transactions;").unwrap(); + let mut transactions = Vec::::new(); + while let Ok(sqlite::State::Row) = statement.next() { + transactions.push(Transaction::from_row(&statement)); + } + transactions +} diff --git a/backend/importers/actualbudget/src/inserters.rs b/backend/importers/actualbudget/src/inserters.rs new file mode 100644 index 0000000..179de97 --- /dev/null +++ b/backend/importers/actualbudget/src/inserters.rs @@ -0,0 +1,120 @@ +use crate::models::budgeting_app::{ + account::Account, budget_update::BudgetUpdate, category::Category, + category_transfer::CategoryTransfer, transaction::Transaction, + transaction_categorisation::TransactionCategorisation, +}; +use sqlite::Connection; + +pub fn insert_accounts(connection: &Connection, accounts: &[Account]) { + for account in accounts { + let str = format!( + " + insert into + accounts(id, name, opening_balance, opening_date) + values({0}, '{1}', {2}, '{3}'); + ", + account.id, + escape_str(&account.name), + account.opening_balance, + account.opening_date + ); + connection.execute(str).unwrap(); + } +} + +pub fn insert_categories(connection: &Connection, categories: &[Category]) { + for category in categories { + let str = format!( + " + insert into + categories(id, name) + values({0}, '{1}'); + ", + category.id, + escape_str(&category.name), + ); + connection.execute(str).unwrap(); + } +} + +pub fn insert_budget_updates(connection: &Connection, budget_updates: &[BudgetUpdate]) { + for budget_update in budget_updates { + let str = format!( + " + insert into + budget_updates(id, category_id, date, new_budget, new_period) + values({0}, {1}, '{2}', {3}, {4}); + ", + budget_update.id, + budget_update.category_id, + budget_update.date, + budget_update.new_budget, + budget_update.new_period, + ); + connection.execute(str).unwrap(); + } +} + +pub fn insert_transactions(connection: &Connection, transactions: &[Transaction]) { + for transaction in transactions { + let str = format!( + " + insert into + transactions(id, description, payee, quantity, date, account_id) + values({0}, '{1}', '{2}', {3}, '{4}', {5}); + ", + transaction.id, + escape_str(&transaction.description), + escape_str(&transaction.payee), + transaction.quantity, + transaction.date, + transaction.account_id, + ); + connection + .execute(str.clone()) + .unwrap_or_else(|_| panic!("{}", &str.clone())); + } +} + +pub fn insert_transaction_categorisations( + connection: &Connection, + transaction_categorisations: &[TransactionCategorisation], +) { + for transaction_categorisation in transaction_categorisations { + let str = format!( + " + insert into + transaction_categorisations(id, description, quantity, transaction_id, category_id) + values({0}, '{1}', {2}, {3}, {4}); + ", + transaction_categorisation.id, + escape_str(&transaction_categorisation.description), + transaction_categorisation.quantity, + transaction_categorisation.transaction_id, + transaction_categorisation.category_id, + ); + connection.execute(str).unwrap(); + } +} + +pub fn insert_category_transfers(connection: &Connection, category_transfers: &[CategoryTransfer]) { + for category_transfer in category_transfers { + let str = format!( + " + insert into + category_transfers(id, description, quantity, from_category_id, to_category_id) + values({0}, '{1}', {2}, {3}, {4}); + ", + category_transfer.id, + escape_str(&category_transfer.description), + category_transfer.quantity, + category_transfer.from_category_id, + category_transfer.to_category_id, + ); + connection.execute(str).unwrap(); + } +} + +fn escape_str(str: &str) -> String { + str.replace("'", "''") +} diff --git a/backend/importers/actualbudget/src/main.rs b/backend/importers/actualbudget/src/main.rs new file mode 100644 index 0000000..495216e --- /dev/null +++ b/backend/importers/actualbudget/src/main.rs @@ -0,0 +1,57 @@ +pub mod getters; +pub mod inserters; +pub mod models; +pub mod transformers; + +use getters::*; +use inserters::*; +use sqlite::Connection; + +fn main() { + let connection_src = sqlite::open("in.sqlite").unwrap(); + let connection_dest = sqlite::open("out.sqlite").unwrap(); + + let actualbudget_first_transaction_date = get_first_transaction_date(&connection_src); + let actualbudget_accounts = get_accounts(&connection_src); + let actualbudget_categories = get_categories(&connection_src); + let actualbudget_zero_budgets = get_zero_budgets(&connection_src); + let actualbudget_category_transfers_account = get_category_transfers_account(&connection_src); + let actualbudget_transactions = get_transactions(&connection_src); + + let first_transaction_date = transformers::transform_date(&actualbudget_first_transaction_date); + let accounts = + transformers::transform_accounts(&actualbudget_accounts, &first_transaction_date); + let categories = transformers::transform_categories(&actualbudget_categories); + let budget_updates = transformers::transform_budget_updates(&actualbudget_zero_budgets); + let (transactions, transaction_categorisations) = transformers::transform_transactions( + &actualbudget_transactions, + &actualbudget_category_transfers_account, + ); + let category_transfers = transformers::transform_category_transfers( + &actualbudget_transactions, + &actualbudget_category_transfers_account, + ); + + clear_destination(&connection_dest); + insert_accounts(&connection_dest, &accounts); + insert_categories(&connection_dest, &categories); + insert_budget_updates(&connection_dest, &budget_updates); + insert_transactions(&connection_dest, &transactions); + insert_transaction_categorisations(&connection_dest, &transaction_categorisations); + insert_category_transfers(&connection_dest, &category_transfers); +} + +fn clear_destination(connection: &Connection) { + connection + .execute( + " + delete from accounts; + delete from budget_updates; + delete from categories; + delete from transaction_categorisations; + delete from category_transfers; + delete from transactions; + ", + ) + .unwrap(); +} diff --git a/backend/importers/actualbudget/src/models/actualbudget/account.rs b/backend/importers/actualbudget/src/models/actualbudget/account.rs new file mode 100644 index 0000000..727c082 --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget/account.rs @@ -0,0 +1,15 @@ +use sqlite::Statement; + +pub struct Account { + pub id: String, + pub name: String, +} + +impl Account { + pub fn from_row(statement: &Statement) -> Account { + Account { + id: statement.read("id").unwrap(), + name: statement.read("name").unwrap(), + } + } +} diff --git a/backend/importers/actualbudget/src/models/actualbudget/category.rs b/backend/importers/actualbudget/src/models/actualbudget/category.rs new file mode 100644 index 0000000..21fdf80 --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget/category.rs @@ -0,0 +1,15 @@ +use sqlite::Statement; + +pub struct Category { + pub id: String, + pub name: String, +} + +impl Category { + pub fn from_row(statement: &Statement) -> Category { + Category { + id: statement.read("id").unwrap(), + name: statement.read("name").unwrap(), + } + } +} diff --git a/backend/importers/actualbudget/src/models/actualbudget/date.rs b/backend/importers/actualbudget/src/models/actualbudget/date.rs new file mode 100644 index 0000000..bd37c46 --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget/date.rs @@ -0,0 +1,69 @@ +use sqlite::Statement; + +#[derive(Clone, PartialEq, Eq)] +pub struct Date { + value: i64, + last_date_part: DatePart, +} + +#[derive(Clone, PartialOrd, PartialEq, Eq)] +enum DatePart { + MonthOfYear, + DayOfMonth, +} + +// actualbudget represents dates as numbers which "look like" the corresponding +// ISO string when written in decimal. E.g. 20240901 represents 1 Sep 2024. It +// also uses month specifiers, e.g. 202409 to represent Sep 2024. In this case, +// we interpret this as the first day of the month, e.g. 2024-09-01. + +impl Date { + pub fn from_date_row(statement: &Statement, column: &str) -> Date { + Date { + value: statement.read::(column).unwrap(), + last_date_part: DatePart::DayOfMonth, + } + } + + pub fn from_month_row(statement: &Statement, column: &str) -> Date { + Date { + value: statement.read::(column).unwrap() * 100 + 1, + last_date_part: DatePart::MonthOfYear, + } + } + + pub fn to_iso(&self) -> String { + format!("{0}-{1}-{2}", self.year(), self.month(), self.day()) + } + + pub fn year(&self) -> String { + self.value.to_string()[0..4].to_string() + } + + pub fn month(&self) -> String { + self.value.to_string()[4..6].to_string() + } + + pub fn day(&self) -> String { + self.dayified_value().to_string()[6..8].to_string() + } + + fn dayified_value(&self) -> i64 { + match self.last_date_part { + DatePart::DayOfMonth => self.value, + DatePart::MonthOfYear => self.value * 100 + 1, + } + } +} + +impl Ord for Date { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.dayified_value().cmp(&other.dayified_value()) + } +} + +impl PartialOrd for Date { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/backend/importers/actualbudget/src/models/actualbudget/mod.rs b/backend/importers/actualbudget/src/models/actualbudget/mod.rs new file mode 100644 index 0000000..5ca472f --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget/mod.rs @@ -0,0 +1,5 @@ +pub mod account; +pub mod category; +pub mod date; +pub mod transaction; +pub mod zero_budget; diff --git a/backend/importers/actualbudget/src/models/actualbudget/transaction.rs b/backend/importers/actualbudget/src/models/actualbudget/transaction.rs new file mode 100644 index 0000000..0a21cfe --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget/transaction.rs @@ -0,0 +1,43 @@ +use sqlite::Statement; + +use super::date::Date; + +#[derive(Clone)] +pub struct Transaction { + pub id: String, + pub is_parent: bool, + pub is_child: bool, + pub account_id: String, + pub category_id: String, + pub amount: i64, + pub payee: String, + pub notes: String, + pub date: Date, + pub parent_id: Option, +} + +impl Transaction { + pub fn from_row(statement: &Statement) -> Transaction { + Transaction { + id: statement.read("id").unwrap(), + is_parent: statement.read::("isParent").unwrap() == 1, + is_child: statement.read::("isChild").unwrap() == 1, + account_id: statement.read("acct").unwrap(), + category_id: statement + .read::, &str>("category") + .map(Option::::unwrap_or_default) + .unwrap(), + amount: statement.read("amount").unwrap(), + payee: statement + .read::, &str>("description") + .map(Option::::unwrap_or_default) + .unwrap(), + notes: statement + .read::, &str>("notes") + .map(Option::::unwrap_or_default) + .unwrap(), + date: Date::from_date_row(statement, "date"), + parent_id: statement.read("parent_id").unwrap(), + } + } +} diff --git a/backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs b/backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs new file mode 100644 index 0000000..87c88e7 --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs @@ -0,0 +1,30 @@ +use sqlite::Statement; + +use super::date::Date; + +#[derive(Clone)] +pub struct ZeroBudget { + pub id: String, + pub month: Date, + pub category_id: String, + pub amount: i64, + pub do_carry_over: bool, +} + +impl ZeroBudget { + pub fn from_row(statement: &Statement) -> ZeroBudget { + ZeroBudget { + id: statement.read("id").unwrap(), + month: Date::from_month_row(statement, "month"), + category_id: statement.read("category").unwrap(), + amount: statement.read("amount").unwrap(), + do_carry_over: match statement.read::("carryover").unwrap() { + 0 => false, + 1 => true, + carryover => { + panic!("carryover column was neither 0 nor 1, but instead {carryover}") + } + }, + } + } +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs b/backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs new file mode 100644 index 0000000..e44fa8c --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs @@ -0,0 +1,57 @@ +use chrono::{DateTime, Datelike, Utc}; +use diesel::{ + deserialize::{FromSql, FromSqlRow}, + expression::AsExpression, + serialize::ToSql, + sql_types::Text, + sqlite::Sqlite, +}; +use serde::{Deserialize, Serialize}; + +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Serialize, + Deserialize, + Copy, + Clone, + Debug, + AsExpression, + FromSqlRow, +)] +#[diesel(sql_type=Text)] +pub struct DateTimeUtc(DateTime); + +impl DateTimeUtc { + pub fn day_diff(&self, other: &DateTimeUtc) -> u32 { + self.0 + .num_days_from_ce() + .abs_diff(other.0.num_days_from_ce()) + } + + pub fn new(date_time_utc: DateTime) -> Self { + Self(date_time_utc) + } +} + +impl ToSql for DateTimeUtc { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, Sqlite>, + ) -> diesel::serialize::Result { + out.set_value((*self).0.to_rfc3339()); + Ok(diesel::serialize::IsNull::No) + } +} + +impl FromSql for DateTimeUtc { + fn from_sql( + bytes: ::RawValue<'_>, + ) -> diesel::deserialize::Result { + let str: String = FromSql::::from_sql(bytes)?; + let date_time = str.as_str().parse::>()?; + Ok(DateTimeUtc(date_time)) + } +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/account.rs b/backend/importers/actualbudget/src/models/budgeting_app/account.rs new file mode 100644 index 0000000..165b55c --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/account.rs @@ -0,0 +1,8 @@ +use super::date_time_utc::DateTimeUtc; + +pub struct Account { + pub id: i32, + pub name: String, + pub opening_balance: i32, + pub opening_date: DateTimeUtc, +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs b/backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs new file mode 100644 index 0000000..9958446 --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs @@ -0,0 +1,10 @@ +use super::date_time_utc::DateTimeUtc; + +#[derive(Clone)] +pub struct BudgetUpdate { + pub id: i32, + pub category_id: i32, + pub date: DateTimeUtc, + pub new_budget: i32, + pub new_period: i32, +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/category.rs b/backend/importers/actualbudget/src/models/budgeting_app/category.rs new file mode 100644 index 0000000..3f51dfd --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/category.rs @@ -0,0 +1,4 @@ +pub struct Category { + pub id: i32, + pub name: String, +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs b/backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs new file mode 100644 index 0000000..0097429 --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs @@ -0,0 +1,7 @@ +pub struct CategoryTransfer { + pub id: i32, + pub description: String, + pub quantity: i32, + pub from_category_id: i32, + pub to_category_id: i32, +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs b/backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs new file mode 100644 index 0000000..69cdfe2 --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs @@ -0,0 +1,20 @@ +use std::{fmt::Display, str::FromStr}; + +use chrono::{DateTime, Utc}; + +#[derive(Clone)] +pub struct DateTimeUtc(DateTime); + +impl DateTimeUtc { + pub fn new(date: &str) -> Self { + let mut date_time = String::from(date); + date_time.push_str("T00:00:00.000Z"); + Self(DateTime::::from_str(&date_time).unwrap()) + } +} + +impl Display for DateTimeUtc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/mod.rs b/backend/importers/actualbudget/src/models/budgeting_app/mod.rs new file mode 100644 index 0000000..bca0f99 --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/mod.rs @@ -0,0 +1,7 @@ +pub mod account; +pub mod budget_update; +pub mod category; +pub mod category_transfer; +pub mod date_time_utc; +pub mod transaction; +pub mod transaction_categorisation; diff --git a/backend/importers/actualbudget/src/models/budgeting_app/transaction.rs b/backend/importers/actualbudget/src/models/budgeting_app/transaction.rs new file mode 100644 index 0000000..e679e1e --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/transaction.rs @@ -0,0 +1,11 @@ +use super::date_time_utc::DateTimeUtc; + +#[derive(Clone)] +pub struct Transaction { + pub id: i32, + pub description: String, + pub payee: String, + pub quantity: i32, + pub date: DateTimeUtc, + pub account_id: i32, +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs b/backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs new file mode 100644 index 0000000..ca7e7f5 --- /dev/null +++ b/backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs @@ -0,0 +1,7 @@ +pub struct TransactionCategorisation { + pub id: i32, + pub description: String, + pub quantity: i32, + pub transaction_id: i32, + pub category_id: i32, +} diff --git a/backend/importers/actualbudget/src/models/mod.rs b/backend/importers/actualbudget/src/models/mod.rs new file mode 100644 index 0000000..cf1b71a --- /dev/null +++ b/backend/importers/actualbudget/src/models/mod.rs @@ -0,0 +1,2 @@ +pub mod actualbudget; +pub mod budgeting_app; diff --git a/backend/importers/actualbudget/src/transformers.rs b/backend/importers/actualbudget/src/transformers.rs new file mode 100644 index 0000000..678ea05 --- /dev/null +++ b/backend/importers/actualbudget/src/transformers.rs @@ -0,0 +1,221 @@ +use core::panic; +use std::hash::{DefaultHasher, Hash, Hasher}; + +use crate::models::{ + actualbudget, + budgeting_app::{ + account::Account, budget_update::BudgetUpdate, category::Category, + category_transfer::CategoryTransfer, date_time_utc::DateTimeUtc, transaction::Transaction, + transaction_categorisation::TransactionCategorisation, + }, +}; + +pub fn transform_date(actualbudget_date: &actualbudget::date::Date) -> DateTimeUtc { + DateTimeUtc::new(&actualbudget_date.to_iso()) +} + +pub fn transform_accounts( + actualbudget_accounts: &[actualbudget::account::Account], + first_transaction_date: &DateTimeUtc, +) -> Vec { + let mut accounts = Vec::::new(); + for account in actualbudget_accounts { + accounts.push(transform_account(account, first_transaction_date)); + } + accounts +} + +fn transform_account( + actualbudget_account: &actualbudget::account::Account, + opening_date: &DateTimeUtc, +) -> Account { + Account { + id: transform_id(&actualbudget_account.id.clone()), + name: actualbudget_account.name.clone(), + opening_balance: 0, + opening_date: opening_date.clone(), + } +} + +pub fn transform_categories( + actualbudget_categories: &[actualbudget::category::Category], +) -> Vec { + let mut categories = Vec::::new(); + for actualbudget_category in actualbudget_categories { + categories.push(Category { + id: transform_id(&actualbudget_category.id.clone()), + name: actualbudget_category.name.clone(), + }) + } + categories +} + +fn transform_id(actualbudget_id: &str) -> i32 { + let mut hasher = DefaultHasher::new(); + actualbudget_id.hash(&mut hasher); + (hasher.finish() % std::convert::TryInto::::try_into(i32::MAX).unwrap()) + .try_into() + .unwrap() +} + +pub fn transform_budget_updates( + actualbudget_zero_budgets: &[actualbudget::zero_budget::ZeroBudget], +) -> Vec { + let mut budget_updates = Vec::::new(); + for zero_budget in actualbudget_zero_budgets { + let first_zero_budget_in_category = get_first_zero_budget_in_category( + actualbudget_zero_budgets, + &zero_budget.category_id.clone(), + ); + if first_zero_budget_in_category.is_some_and(|zb| zb.id == zero_budget.id) { + continue; + } + budget_updates.push(BudgetUpdate { + id: transform_id(&zero_budget.id.clone()), + category_id: transform_id(&zero_budget.category_id.clone()), + date: transform_date(&zero_budget.month.clone()), + new_budget: zero_budget.amount.try_into().unwrap(), + new_period: 30, + }) + } + budget_updates +} + +fn get_first_zero_budget_in_category( + actualbudget_zero_budgets: &[actualbudget::zero_budget::ZeroBudget], + actualbudget_category_id: &str, +) -> Option { + let mut category_zero_budgets = actualbudget_zero_budgets.to_vec(); + category_zero_budgets.retain(|zb| zb.category_id == actualbudget_category_id); + category_zero_budgets + .iter() + .min_by_key(|zb| zb.month.clone()) + .cloned() +} + +pub fn transform_transactions( + actualbudget_transactions: &[actualbudget::transaction::Transaction], + actualbudget_category_transfers_account: &actualbudget::account::Account, +) -> (Vec, Vec) { + let mut transactions = Vec::::new(); + let mut transaction_categorisations = Vec::::new(); + + let mut actualbudget_transactions = actualbudget_transactions.to_vec(); + actualbudget_transactions + .retain(|t| t.category_id.clone() != actualbudget_category_transfers_account.id.clone()); + let mut parent_actualbudget_transactions = actualbudget_transactions.to_vec(); + parent_actualbudget_transactions.retain(|t| t.is_parent); + let mut child_actualbudget_transactions = actualbudget_transactions.to_vec(); + child_actualbudget_transactions.retain(|t| t.is_child); + let mut atomic_actualbudget_transactions = actualbudget_transactions.to_vec(); + atomic_actualbudget_transactions.retain(|t| !t.is_parent && !t.is_child); + + for atomic_actualbudget_transaction in atomic_actualbudget_transactions { + let transaction_id = transform_id(&atomic_actualbudget_transaction.id); + + transactions.push(Transaction { + id: transaction_id, + description: atomic_actualbudget_transaction.notes, + payee: atomic_actualbudget_transaction.payee, + quantity: atomic_actualbudget_transaction.amount.try_into().unwrap(), + date: transform_date(&atomic_actualbudget_transaction.date), + account_id: transform_id(&atomic_actualbudget_transaction.account_id), + }); + + transaction_categorisations.push(TransactionCategorisation { + id: transaction_id, + description: String::new(), + quantity: atomic_actualbudget_transaction.amount.try_into().unwrap(), + transaction_id, + category_id: transform_id(&atomic_actualbudget_transaction.category_id), + }); + } + + for parent_actualbudget_transaction in parent_actualbudget_transactions { + let mut child_actualbudget_transactions = child_actualbudget_transactions.to_vec(); + child_actualbudget_transactions.retain(|t| { + t.parent_id + .clone() + .is_some_and(|parent_id| parent_id == parent_actualbudget_transaction.id.clone()) + }); + + let transaction_id = transform_id(&parent_actualbudget_transaction.id); + + transactions.push(Transaction { + id: transaction_id, + description: parent_actualbudget_transaction.notes, + payee: parent_actualbudget_transaction.payee, + quantity: parent_actualbudget_transaction.amount.try_into().unwrap(), + date: transform_date(&parent_actualbudget_transaction.date), + account_id: transform_id(&parent_actualbudget_transaction.account_id), + }); + + for child_actualbudget_transaction in child_actualbudget_transactions { + transaction_categorisations.push(TransactionCategorisation { + id: transform_id(&child_actualbudget_transaction.id), + description: child_actualbudget_transaction.notes, + quantity: child_actualbudget_transaction.amount.try_into().unwrap(), + transaction_id, + category_id: transform_id(&child_actualbudget_transaction.category_id), + }); + } + } + + (transactions, transaction_categorisations) +} + +pub fn transform_category_transfers( + actualbudget_transactions: &[actualbudget::transaction::Transaction], + actualbudget_category_transfers_account: &actualbudget::account::Account, +) -> Vec { + let mut category_transfers = Vec::::new(); + + let mut actualbudget_category_transfers = actualbudget_transactions.to_vec(); + actualbudget_category_transfers + .retain(|t| t.category_id.clone() == actualbudget_category_transfers_account.id.clone()); + let mut actualbudget_parent_category_transfers = actualbudget_category_transfers.to_vec(); + actualbudget_parent_category_transfers.retain(|t| t.is_parent); + let mut actualbudget_child_category_transfers = actualbudget_category_transfers.to_vec(); + actualbudget_child_category_transfers.retain(|t| t.is_child); + + for actualbudget_parent_category_transfer in actualbudget_parent_category_transfers { + let mut actualbudget_child_category_transfers = + actualbudget_child_category_transfers.to_vec(); + actualbudget_child_category_transfers.retain(|t| { + t.parent_id + .clone() + .is_some_and(|parent_id| parent_id == actualbudget_parent_category_transfer.id) + }); + + if actualbudget_child_category_transfers.len() != 2 { + panic!("Category transfers should always have exactly two children."); + } + + let category_1 = actualbudget_child_category_transfers[0].clone(); + let category_2 = actualbudget_child_category_transfers[1].clone(); + + // To avoid duplicates + if category_1.amount >= 0 { + continue; + } + + if actualbudget_child_category_transfers[0].amount + != -actualbudget_child_category_transfers[1].amount + { + panic!("Category transfer children should sum to zero."); + } + + category_transfers.push(CategoryTransfer { + id: transform_id(&actualbudget_parent_category_transfer.id), + description: actualbudget_parent_category_transfer.notes, + quantity: actualbudget_child_category_transfers[0] + .amount + .try_into() + .unwrap(), + from_category_id: transform_id(&category_1.id), + to_category_id: transform_id(&category_2.id), + }); + } + + category_transfers +} diff --git a/backend/migrations/.keep b/backend/migrations/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/backend/migrations/2024-08-31-084439_initial_setup/down.sql b/backend/migrations/2024-08-31-084439_initial_setup/down.sql deleted file mode 100644 index 8cf4b1e..0000000 --- a/backend/migrations/2024-08-31-084439_initial_setup/down.sql +++ /dev/null @@ -1,6 +0,0 @@ -DROP TABLE accounts; -DROP TABLE budget_updates; -DROP TABLE categories; -DROP TABLE transaction_categorisations; -DROP TABLE category_transfers; -DROP TABLE transactions; diff --git a/backend/migrations/2024-08-31-084439_initial_setup/up.sql b/backend/migrations/2024-08-31-084439_initial_setup/up.sql deleted file mode 100644 index 3584ae7..0000000 --- a/backend/migrations/2024-08-31-084439_initial_setup/up.sql +++ /dev/null @@ -1,70 +0,0 @@ -PRAGMA foreign_keys = ON; - -CREATE TABLE accounts( - id INTEGER NOT NULL PRIMARY KEY, - name TEXT NOT NULL, - opening_balance INTEGER NOT NULL, - opening_date TEXT NOT NULL -); - -CREATE TABLE budget_updates( - id INTEGER NOT NULL PRIMARY KEY, - category_id INTEGER NOT NULL, - date TEXT NOT NULL, - new_budget INTEGER NOT NULL, - new_period INTEGER NOT NULL, - FOREIGN KEY (category_id) - REFERENCES categories (id) - ON UPDATE CASCADE - ON DELETE RESTRICT -); - -CREATE TABLE categories( - id INTEGER NOT NULL PRIMARY KEY, - name TEXT NOT NULL -); - -CREATE TABLE category_transfers( - id INTEGER NOT NULL PRIMARY KEY, - description TEXT NOT NULL, - quantity INTEGER NOT NULL, - from_category_id INTEGER NOT NULL, - to_category_id INTEGER NOT NULL, - FOREIGN KEY (from_category_id) - REFERENCES categories (id) - ON UPDATE CASCADE - ON DELETE RESTRICT, - FOREIGN KEY (to_category_id) - REFERENCES categories (id) - ON UPDATE CASCADE - ON DELETE RESTRICT -); - -CREATE TABLE transactions( - id INTEGER NOT NULL PRIMARY KEY, - description TEXT NOT NULL, - payee TEXT NOT NULL, - quantity INTEGER NOT NULL, - date TEXT NOT NULL, - account_id INTEGER NOT NULL, - FOREIGN KEY (account_id) - REFERENCES accounts (id) - ON UPDATE CASCADE - ON DELETE RESTRICT -); - -CREATE TABLE transaction_categorisations( - id INTEGER NOT NULL PRIMARY KEY, - description TEXT NOT NULL, - quantity INTEGER NOT NULL, - transaction_id INTEGER NOT NULL, - category_id INTEGER NOT NULL, - FOREIGN KEY (transaction_id) - REFERENCES transactions (id) - ON UPDATE CASCADE - ON DELETE RESTRICT, - FOREIGN KEY (category_id) - REFERENCES categories (id) - ON UPDATE CASCADE - ON DELETE RESTRICT -); diff --git a/backend/src/databases.rs b/backend/src/databases.rs deleted file mode 100644 index 7202e78..0000000 --- a/backend/src/databases.rs +++ /dev/null @@ -1,11 +0,0 @@ -use rocket_sync_db_pools::{database, diesel}; - -cfg_if::cfg_if! { - if #[cfg(test)] { - #[database("user_data.test")] - pub struct UserDataDb(diesel::SqliteConnection); - } else { - #[database("user_data")] - pub struct UserDataDb(diesel::SqliteConnection); - } -} diff --git a/backend/src/main.rs b/backend/src/main.rs deleted file mode 100644 index c3e02e5..0000000 --- a/backend/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -use diesel::SqliteConnection; -use diesel_migrations::MigrationHarness; -use diesel_migrations::{embed_migrations, EmbeddedMigrations}; -use rocket::fairing::AdHoc; -use rocket::http::ContentType; -use rocket::{launch, routes, Build, Rocket}; - -use crate::databases::UserDataDb; - -pub mod databases; -pub mod models; -pub mod queries; -pub mod routes; -pub mod schema; -pub mod utils; - -pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); - -fn run_database_migrations(connection: &mut SqliteConnection) { - connection - .run_pending_migrations(MIGRATIONS) - .expect("Error running database migrations"); -} - -#[launch] -async fn rocket() -> Rocket { - rocket::build() - .attach(UserDataDb::fairing()) - .attach(AdHoc::on_liftoff("Database Migration", |rocket| { - Box::pin(async move { - let user_data_db = UserDataDb::get_one(rocket).await.expect( - "Failed to get connection to user data database in order to run migrations", - ); - user_data_db.run(run_database_migrations).await; - }) - })) - .attach(AdHoc::on_response("Add CORS headers", |_req, res| { - Box::pin(async move { - res.set_header(ContentType::JSON); - res.set_header(rocket::http::Header::new( - "Access-Control-Allow-Origin", - "http://localhost:5173", - )); - }) - })) - .mount( - "/", - routes![ - routes::account::get_all_accounts, - routes::account::create_account, - routes::balance::get_all_category_balances, - routes::budget::get_current_category_budget_per_category, - routes::budget_update::get_all_budget_updates, - routes::category::get_all_categories, - ], - ) -} diff --git a/backend/src/models/account.rs b/backend/src/models/account.rs deleted file mode 100644 index c10bd5f..0000000 --- a/backend/src/models/account.rs +++ /dev/null @@ -1,15 +0,0 @@ -use diesel::prelude::*; -use serde::{Deserialize, Serialize}; - -use super::date_time_utc::DateTimeUtc; - -#[derive( - Insertable, Queryable, Identifiable, Selectable, Debug, PartialEq, Clone, Serialize, Deserialize, -)] -#[diesel(table_name = crate::schema::accounts)] -pub struct Account { - pub id: i32, - pub name: String, - pub opening_balance: i32, - pub opening_date: DateTimeUtc, -} diff --git a/backend/src/models/budget.rs b/backend/src/models/budget.rs deleted file mode 100644 index cc18f2e..0000000 --- a/backend/src/models/budget.rs +++ /dev/null @@ -1,44 +0,0 @@ -use serde::Serialize; - -use super::budget_update::BudgetUpdate; - -#[derive(Debug, Eq, PartialEq, Serialize)] -pub struct Budget { - pub quantity: i32, - pub period: i32, -} - -impl From<&BudgetUpdate> for Budget { - fn from(budget_update: &BudgetUpdate) -> Self { - Self { - quantity: budget_update.new_budget, - period: budget_update.new_period, - } - } -} - -#[cfg(test)] -mod test { - use chrono::Utc; - - use crate::models::date_time_utc::DateTimeUtc; - - use super::*; - - #[test] - fn when_from_budget_update_then_returns_budget() { - let quantity = 2; - let period = 3; - let budget_update = BudgetUpdate { - id: 0, - category_id: 1, - date: DateTimeUtc::new(Utc::now()), - new_budget: quantity, - new_period: period, - }; - - let budget = Budget::from(&budget_update); - - assert_eq!(budget, Budget { quantity, period }); - } -} diff --git a/backend/src/models/budget_update.rs b/backend/src/models/budget_update.rs deleted file mode 100644 index c63e957..0000000 --- a/backend/src/models/budget_update.rs +++ /dev/null @@ -1,17 +0,0 @@ -use diesel::prelude::*; -use serde::Serialize; - -use crate::models::category::Category; - -use super::date_time_utc::DateTimeUtc; - -#[derive(Clone, Serialize, Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)] -#[diesel(table_name = crate::schema::budget_updates)] -#[diesel(belongs_to(Category))] -pub struct BudgetUpdate { - pub id: i32, - pub category_id: i32, - pub date: DateTimeUtc, - pub new_budget: i32, - pub new_period: i32, -} diff --git a/backend/src/models/category.rs b/backend/src/models/category.rs deleted file mode 100644 index 5cb2609..0000000 --- a/backend/src/models/category.rs +++ /dev/null @@ -1,9 +0,0 @@ -use diesel::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq, Serialize, Deserialize)] -#[diesel(table_name = crate::schema::categories)] -pub struct Category { - pub id: i32, - pub name: String, -} diff --git a/backend/src/models/category_balance.rs b/backend/src/models/category_balance.rs deleted file mode 100644 index 796b40b..0000000 --- a/backend/src/models/category_balance.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::Serialize; - -#[derive(Serialize)] -pub struct CategoryBalance { - pub category_id: i32, - pub balance: i32, -} diff --git a/backend/src/models/category_budget.rs b/backend/src/models/category_budget.rs deleted file mode 100644 index 9796261..0000000 --- a/backend/src/models/category_budget.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::Serialize; - -use super::budget::Budget; - -#[derive(Serialize)] -pub struct CategoryBudget { - pub category_id: i32, - pub budget: Option, -} diff --git a/backend/src/models/category_transfer.rs b/backend/src/models/category_transfer.rs deleted file mode 100644 index 6cbda0e..0000000 --- a/backend/src/models/category_transfer.rs +++ /dev/null @@ -1,11 +0,0 @@ -use diesel::prelude::*; - -#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)] -#[diesel(table_name = crate::schema::category_transfers)] -pub struct CategoryTransfer { - pub id: i32, - pub description: String, - pub quantity: i32, - pub from_category_id: i32, - pub to_category_id: i32, -} diff --git a/backend/src/models/date_time_utc.rs b/backend/src/models/date_time_utc.rs deleted file mode 100644 index 03d5802..0000000 --- a/backend/src/models/date_time_utc.rs +++ /dev/null @@ -1,131 +0,0 @@ -use chrono::{DateTime, Datelike, Utc}; -use diesel::{ - backend::Backend, - deserialize::{self, FromSql, FromSqlRow}, - expression::AsExpression, - serialize::{self, ToSql}, - sql_types::Text, - sqlite::Sqlite, -}; -use serde::{Deserialize, Serialize}; - -#[derive( - Eq, - PartialEq, - Ord, - PartialOrd, - Serialize, - Deserialize, - Copy, - Clone, - Debug, - AsExpression, - FromSqlRow, -)] -#[diesel(sql_type=Text)] -pub struct DateTimeUtc(DateTime); - -impl DateTimeUtc { - pub fn day_diff(&self, other: &DateTimeUtc) -> u32 { - self.0 - .num_days_from_ce() - .abs_diff(other.0.num_days_from_ce()) - } - - pub fn to_day_str(&self) -> String { - self.0 - .to_rfc3339() - .split_at_checked(10) - .map(|(day_str, _)| String::from(day_str)) - .unwrap_or_else(|| String::from("")) - } - - pub fn new(date_time_utc: DateTime) -> Self { - Self(date_time_utc) - } - - pub fn now() -> Self { - Self(Utc::now()) - } -} - -impl ToSql for DateTimeUtc { - fn to_sql<'b>( - &'b self, - out: &mut diesel::serialize::Output<'b, '_, Sqlite>, - ) -> serialize::Result { - out.set_value(self.to_day_str()); - Ok(diesel::serialize::IsNull::No) - } -} - -impl FromSql for DateTimeUtc { - fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { - let str: String = FromSql::::from_sql(bytes)?; - let date_time = str.as_str().parse::>()?; - Ok(DateTimeUtc(date_time)) - } -} - -#[cfg(test)] -mod test { - use chrono::TimeZone; - - use super::*; - - #[test] - fn given_midnight_when_day_diff_next_midnight_less_1s_then_return_0() { - let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc()); - let end = DateTimeUtc::new( - Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59) - .unwrap() - .to_utc(), - ); - - let day_diff = start.day_diff(&end); - let neg_day_diff = end.day_diff(&start); - - assert_eq!(day_diff, 0); - assert_eq!(day_diff, neg_day_diff); - } - - #[test] - fn given_midnight_when_day_diff_next_midnight_then_return_1() { - let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc()); - let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc()); - - let day_diff = start.day_diff(&end); - let neg_day_diff = end.day_diff(&start); - - assert_eq!(day_diff, 1); - assert_eq!(day_diff, neg_day_diff); - } - - #[test] - fn given_midnight_less_1s_when_day_diff_midnight_then_return_1() { - let start = DateTimeUtc::new( - Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59) - .unwrap() - .to_utc(), - ); - let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc()); - - let day_diff = start.day_diff(&end); - let neg_day_diff = end.day_diff(&start); - - assert_eq!(day_diff, 1); - assert_eq!(day_diff, neg_day_diff); - } - - #[test] - fn given_23_nov_1970_when_to_day_str_then_return_iso_day_str() { - let date_time_utc = DateTimeUtc::new( - Utc.with_ymd_and_hms(1970, 11, 23, 0, 0, 0) - .unwrap() - .to_utc(), - ); - let day_str = date_time_utc.to_day_str(); - - assert_eq!(day_str, "1970-11-23"); - } -} diff --git a/backend/src/models/mod.rs b/backend/src/models/mod.rs deleted file mode 100644 index b790fcb..0000000 --- a/backend/src/models/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod account; -pub mod budget; -pub mod budget_update; -pub mod category; -pub mod category_balance; -pub mod category_budget; -pub mod category_transfer; -pub mod date_time_utc; -pub mod transaction; -pub mod transaction_categorisation; diff --git a/backend/src/models/transaction.rs b/backend/src/models/transaction.rs deleted file mode 100644 index 36f33f8..0000000 --- a/backend/src/models/transaction.rs +++ /dev/null @@ -1,17 +0,0 @@ -use diesel::prelude::*; - -use crate::models::account::Account; - -use super::date_time_utc::DateTimeUtc; - -#[derive(Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)] -#[diesel(table_name = crate::schema::transactions)] -#[diesel(belongs_to(Account))] -pub struct Transaction { - pub id: i32, - pub description: String, - pub payee: String, - pub quantity: i32, - pub date: DateTimeUtc, - pub account_id: i32, -} diff --git a/backend/src/models/transaction_categorisation.rs b/backend/src/models/transaction_categorisation.rs deleted file mode 100644 index d2867f1..0000000 --- a/backend/src/models/transaction_categorisation.rs +++ /dev/null @@ -1,11 +0,0 @@ -use diesel::prelude::*; - -#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)] -#[diesel(table_name = crate::schema::transaction_categorisations)] -pub struct TransactionCategorisations { - pub id: i32, - pub description: String, - pub quantity: i32, - pub transaction_id: i32, - pub category_id: i32, -} diff --git a/backend/src/queries/accounts/get_all.rs b/backend/src/queries/accounts/get_all.rs deleted file mode 100644 index 12941ac..0000000 --- a/backend/src/queries/accounts/get_all.rs +++ /dev/null @@ -1,7 +0,0 @@ -use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; - -use crate::{models::account::Account, schema::accounts::dsl::accounts}; - -pub fn get_all_accounts(connection: &mut SqliteConnection) -> Result, Error> { - accounts.select(Account::as_select()).load(connection) -} diff --git a/backend/src/queries/accounts/insert.rs b/backend/src/queries/accounts/insert.rs deleted file mode 100644 index c20cf60..0000000 --- a/backend/src/queries/accounts/insert.rs +++ /dev/null @@ -1,14 +0,0 @@ -use diesel::prelude::*; -use diesel::result::Error; - -use crate::models::account::Account; -use crate::schema::accounts::dsl::accounts as accounts_table; - -pub fn insert_account( - accounts: &[Account], - connection: &mut SqliteConnection, -) -> Result { - diesel::insert_into(accounts_table) - .values(accounts) - .execute(connection) -} diff --git a/backend/src/queries/accounts/mod.rs b/backend/src/queries/accounts/mod.rs deleted file mode 100644 index 94fa360..0000000 --- a/backend/src/queries/accounts/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod get_all; -mod insert; - -pub use get_all::get_all_accounts; -pub use insert::insert_account; diff --git a/backend/src/queries/budget_updates/get_all.rs b/backend/src/queries/budget_updates/get_all.rs deleted file mode 100644 index 97f2f2f..0000000 --- a/backend/src/queries/budget_updates/get_all.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::{ - models::budget_update::BudgetUpdate, - schema::budget_updates::dsl::budget_updates as budget_updates_table, -}; -use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; - -pub fn get_all_budget_updates( - connection: &mut SqliteConnection, -) -> Result, Error> { - budget_updates_table - .select(BudgetUpdate::as_select()) - .load::(connection) -} diff --git a/backend/src/queries/budget_updates/get_first_with_date.rs b/backend/src/queries/budget_updates/get_first_with_date.rs deleted file mode 100644 index a2824be..0000000 --- a/backend/src/queries/budget_updates/get_first_with_date.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::{ - models::budget_update::BudgetUpdate, - schema::budget_updates::{ - self as budget_updates_schema, dsl::budget_updates as budget_updates_table, - }, -}; -use diesel::{ - result::Error, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection, -}; - -pub fn get_first_budget_update_with_date( - date: &str, - connection: &mut SqliteConnection, -) -> Result { - budget_updates_table - .select(BudgetUpdate::as_select()) - .filter(budget_updates_schema::date.eq(date)) - .first(connection) -} diff --git a/backend/src/queries/budget_updates/get_most_recent_with_category_id.rs b/backend/src/queries/budget_updates/get_most_recent_with_category_id.rs deleted file mode 100644 index 4753092..0000000 --- a/backend/src/queries/budget_updates/get_most_recent_with_category_id.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::{ - models::date_time_utc::DateTimeUtc, - schema::budget_updates::{ - self as budget_updates_schema, dsl::budget_updates as budget_updates_table, - }, -}; -use diesel::{dsl::max, result::Error, ExpressionMethods, QueryDsl, RunQueryDsl, SqliteConnection}; - -pub fn get_most_recent_budget_update_date_with_category_id( - category_id: i32, - connection: &mut SqliteConnection, -) -> Result, Error> { - budget_updates_table - .select(max(budget_updates_schema::date)) - .filter(budget_updates_schema::category_id.eq(category_id)) - .filter(budget_updates_schema::date.le(DateTimeUtc::now().to_day_str())) - .first(connection) -} diff --git a/backend/src/queries/budget_updates/mod.rs b/backend/src/queries/budget_updates/mod.rs deleted file mode 100644 index 3b514dd..0000000 --- a/backend/src/queries/budget_updates/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod get_all; -mod get_first_with_date; -mod get_most_recent_with_category_id; - -pub use get_all::get_all_budget_updates; -pub use get_first_with_date::get_first_budget_update_with_date; -pub use get_most_recent_with_category_id::get_most_recent_budget_update_date_with_category_id; diff --git a/backend/src/queries/categories/get_all.rs b/backend/src/queries/categories/get_all.rs deleted file mode 100644 index db5a073..0000000 --- a/backend/src/queries/categories/get_all.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::{models::category::Category, schema::categories::dsl::categories as categories_table}; -use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; - -pub fn get_all_categories(connection: &mut SqliteConnection) -> Result, Error> { - categories_table - .select(Category::as_select()) - .load(connection) -} diff --git a/backend/src/queries/categories/get_all_ids.rs b/backend/src/queries/categories/get_all_ids.rs deleted file mode 100644 index 7c7c912..0000000 --- a/backend/src/queries/categories/get_all_ids.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::schema::categories::{self as categories_schema, dsl::categories as categories_table}; -use diesel::{result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; - -pub fn get_all_category_ids(connection: &mut SqliteConnection) -> Result, Error> { - categories_table - .select(categories_schema::id) - .load(connection) -} diff --git a/backend/src/queries/categories/mod.rs b/backend/src/queries/categories/mod.rs deleted file mode 100644 index 80356b3..0000000 --- a/backend/src/queries/categories/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod get_all; -mod get_all_ids; - -pub use self::get_all::get_all_categories; -pub use self::get_all_ids::get_all_category_ids; diff --git a/backend/src/queries/category_balances/get_all.rs b/backend/src/queries/category_balances/get_all.rs deleted file mode 100644 index 713fbeb..0000000 --- a/backend/src/queries/category_balances/get_all.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::{ - models::category_balance::CategoryBalance, - queries::{ - budget_updates::get_all_budget_updates, - categories::get_all_categories, - category_transfers::{ - sum_category_transfer_quantity_per_from_category_id, - sum_category_transfer_quantity_per_to_category_id, - }, - transaction_categorisations::sum_transaction_categorisation_quantity_per_category_id, - }, - utils::calculate_category_balance, -}; -use diesel::{result::Error, SqliteConnection}; - -pub fn get_all_category_balances( - connection: &mut SqliteConnection, -) -> Result, Error> { - let categories = get_all_categories(connection)?; - let transaction_sums = sum_transaction_categorisation_quantity_per_category_id(connection)?; - let category_transfer_from_sums = - sum_category_transfer_quantity_per_from_category_id(connection)?; - let category_transfer_to_sums = sum_category_transfer_quantity_per_to_category_id(connection)?; - let budget_updates = get_all_budget_updates(connection)?; - - let mut balances = Vec::::new(); - for category in categories { - let balance = calculate_category_balance( - &category, - &transaction_sums, - &category_transfer_from_sums, - &category_transfer_to_sums, - &budget_updates, - ); - - balances.push(CategoryBalance { - category_id: category.id, - balance: balance.try_into().unwrap(), - }) - } - - Ok(balances) -} diff --git a/backend/src/queries/category_balances/mod.rs b/backend/src/queries/category_balances/mod.rs deleted file mode 100644 index 3fc1be2..0000000 --- a/backend/src/queries/category_balances/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod get_all; - -pub use get_all::get_all_category_balances; diff --git a/backend/src/queries/category_budgets/get_most_recent_per_category_id.rs b/backend/src/queries/category_budgets/get_most_recent_per_category_id.rs deleted file mode 100644 index 6b76efd..0000000 --- a/backend/src/queries/category_budgets/get_most_recent_per_category_id.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{ - models::{budget::Budget, category_budget::CategoryBudget}, - queries::{ - budget_updates::{ - get_first_budget_update_with_date, get_most_recent_budget_update_date_with_category_id, - }, - categories::get_all_category_ids, - }, -}; -use diesel::{result::Error, SqliteConnection}; - -pub fn get_most_recent_category_budget_per_category_id( - connection: &mut SqliteConnection, -) -> Result, Error> { - let category_ids: Vec = get_all_category_ids(connection)?; - - let mut budgets = Vec::::new(); - for category_id in category_ids { - let most_recent_budget_update_date = - get_most_recent_budget_update_date_with_category_id(category_id, connection)?; - - if most_recent_budget_update_date.is_none() { - budgets.push(CategoryBudget { - category_id, - budget: Option::None, - }); - continue; - } - - let most_recent_budget_update = get_first_budget_update_with_date( - most_recent_budget_update_date.unwrap().as_str(), - connection, - )?; - - budgets.push(CategoryBudget { - category_id, - budget: Some(Budget::from(&most_recent_budget_update)), - }); - } - - Ok(budgets) -} diff --git a/backend/src/queries/category_budgets/mod.rs b/backend/src/queries/category_budgets/mod.rs deleted file mode 100644 index 29fc246..0000000 --- a/backend/src/queries/category_budgets/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod get_most_recent_per_category_id; - -pub use self::get_most_recent_per_category_id::get_most_recent_category_budget_per_category_id; diff --git a/backend/src/queries/category_transfers/mod.rs b/backend/src/queries/category_transfers/mod.rs deleted file mode 100644 index 85eb1c8..0000000 --- a/backend/src/queries/category_transfers/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod sum_quantity_per_from_category_id; -mod sum_quantity_per_to_category_id; - -pub use sum_quantity_per_from_category_id::sum_category_transfer_quantity_per_from_category_id; -pub use sum_quantity_per_to_category_id::sum_category_transfer_quantity_per_to_category_id; diff --git a/backend/src/queries/category_transfers/sum_quantity_per_from_category_id.rs b/backend/src/queries/category_transfers/sum_quantity_per_from_category_id.rs deleted file mode 100644 index ae3a51a..0000000 --- a/backend/src/queries/category_transfers/sum_quantity_per_from_category_id.rs +++ /dev/null @@ -1,17 +0,0 @@ -use diesel::{dsl::sum, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; - -use crate::schema::category_transfers::{ - self as category_transfers_schema, dsl::category_transfers as category_transfers_table, -}; - -pub fn sum_category_transfer_quantity_per_from_category_id( - connection: &mut SqliteConnection, -) -> Result)>, Error> { - category_transfers_table - .group_by(category_transfers_schema::from_category_id) - .select(( - category_transfers_schema::from_category_id, - sum(category_transfers_schema::quantity), - )) - .load::<(i32, Option)>(connection) -} diff --git a/backend/src/queries/category_transfers/sum_quantity_per_to_category_id.rs b/backend/src/queries/category_transfers/sum_quantity_per_to_category_id.rs deleted file mode 100644 index fe6f3a7..0000000 --- a/backend/src/queries/category_transfers/sum_quantity_per_to_category_id.rs +++ /dev/null @@ -1,17 +0,0 @@ -use diesel::{dsl::sum, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; - -use crate::schema::category_transfers::{ - self as category_transfers_schema, dsl::category_transfers as category_transfers_table, -}; - -pub fn sum_category_transfer_quantity_per_to_category_id( - connection: &mut SqliteConnection, -) -> Result)>, Error> { - category_transfers_table - .group_by(category_transfers_schema::to_category_id) - .select(( - category_transfers_schema::to_category_id, - sum(category_transfers_schema::quantity), - )) - .load::<(i32, Option)>(connection) -} diff --git a/backend/src/queries/mod.rs b/backend/src/queries/mod.rs deleted file mode 100644 index 43328e2..0000000 --- a/backend/src/queries/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod accounts; -pub mod budget_updates; -pub mod categories; -pub mod category_balances; -pub mod category_budgets; -pub mod category_transfers; -pub mod transaction_categorisations; diff --git a/backend/src/queries/transaction_categorisations/mod.rs b/backend/src/queries/transaction_categorisations/mod.rs deleted file mode 100644 index d4217cc..0000000 --- a/backend/src/queries/transaction_categorisations/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod sum_quantity_per_category_id; - -pub use sum_quantity_per_category_id::sum_transaction_categorisation_quantity_per_category_id; diff --git a/backend/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs b/backend/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs deleted file mode 100644 index 7f93e0c..0000000 --- a/backend/src/queries/transaction_categorisations/sum_quantity_per_category_id.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::schema::transaction_categorisations::{ - self as transaction_categorisations_schema, - dsl::transaction_categorisations as transaction_categorisations_table, -}; -use diesel::{dsl::sum, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; - -pub fn sum_transaction_categorisation_quantity_per_category_id( - connection: &mut SqliteConnection, -) -> Result)>, Error> { - transaction_categorisations_table - .group_by(transaction_categorisations_schema::category_id) - .select(( - transaction_categorisations_schema::category_id, - sum(transaction_categorisations_schema::quantity), - )) - .load::<(i32, Option)>(connection) -} diff --git a/backend/src/queries/transaction_categorisations/sum_quantity_per_to_category_id.rs b/backend/src/queries/transaction_categorisations/sum_quantity_per_to_category_id.rs deleted file mode 100644 index e69de29..0000000 diff --git a/backend/src/routes/account.rs b/backend/src/routes/account.rs deleted file mode 100644 index 7bbdb4f..0000000 --- a/backend/src/routes/account.rs +++ /dev/null @@ -1,57 +0,0 @@ -use diesel::result::DatabaseErrorKind; -use rocket::http::Status; -use rocket::serde::json::Json; -use rocket::{get, post, Responder}; - -use crate::databases::UserDataDb; -use crate::models::account::Account; -use crate::queries::accounts::{get_all_accounts as get_all_accounts_query, insert_account}; - -#[get("/account")] -pub async fn get_all_accounts(user_data_db: UserDataDb) -> Result>, Status> { - user_data_db - .run(get_all_accounts_query) - .await - .map(Json) - .map_err(|_| Status { code: 500 }) -} - -#[derive(Responder)] -pub enum CreateAccountResponder { - #[response(status = 500, content_type = "text")] - InternalServerError(String), - - #[response(status = 202, content_type = "json")] - Created(Json), - - #[response(status = 400, content_type = "text")] - BadRequest(String), -} - -#[post("/account", data = "", format = "json")] -pub async fn create_account( - account: Json, - user_data_db: UserDataDb, -) -> CreateAccountResponder { - let account = account.into_inner(); - let accounts_vec = vec![account.clone()]; - - let num_rows_inserted = user_data_db - .run(move |connection| insert_account(&accounts_vec, connection)) - .await; - - match num_rows_inserted { - Ok(_) => CreateAccountResponder::Created(Json(account)), - Err(err) => match err { - diesel::result::Error::DatabaseError(DatabaseErrorKind::UniqueViolation, info) => { - let column = info.column_name().unwrap_or("account"); - CreateAccountResponder::BadRequest(format!( - "Error creating account: {column} was not unique." - )) - } - _ => CreateAccountResponder::InternalServerError(format!( - "Error creating account: {err}" - )), - }, - } -} diff --git a/backend/src/routes/balance.rs b/backend/src/routes/balance.rs deleted file mode 100644 index f423c90..0000000 --- a/backend/src/routes/balance.rs +++ /dev/null @@ -1,17 +0,0 @@ -use rocket::{get, http::Status, serde::json::Json}; - -use crate::{ - databases::UserDataDb, models::category_balance::CategoryBalance, - queries::category_balances::get_all_category_balances as get_all_category_balances_query, -}; - -#[get("/balance/category")] -pub async fn get_all_category_balances( - user_data_db: UserDataDb, -) -> Result>, Status> { - user_data_db - .run(get_all_category_balances_query) - .await - .map(Json) - .map_err(|_| Status { code: 500 }) -} diff --git a/backend/src/routes/budget.rs b/backend/src/routes/budget.rs deleted file mode 100644 index 67aae8e..0000000 --- a/backend/src/routes/budget.rs +++ /dev/null @@ -1,18 +0,0 @@ -use rocket::get; -use rocket::http::Status; -use rocket::serde::json::Json; - -use crate::databases::UserDataDb; -use crate::models::category_budget::CategoryBudget; -use crate::queries; - -#[get("/budget/category?current")] -pub async fn get_current_category_budget_per_category( - user_data_db: UserDataDb, -) -> Result>, Status> { - user_data_db - .run(queries::category_budgets::get_most_recent_category_budget_per_category_id) - .await - .map(Json) - .map_err(|_| Status { code: 500 }) -} diff --git a/backend/src/routes/budget_update.rs b/backend/src/routes/budget_update.rs deleted file mode 100644 index 7732c7b..0000000 --- a/backend/src/routes/budget_update.rs +++ /dev/null @@ -1,18 +0,0 @@ -use rocket::get; -use rocket::http::Status; -use rocket::serde::json::Json; - -use crate::databases::UserDataDb; -use crate::models::budget_update::BudgetUpdate; -use crate::queries; - -#[get("/budget-update")] -pub async fn get_all_budget_updates( - user_data_db: UserDataDb, -) -> Result>, Status> { - user_data_db - .run(queries::budget_updates::get_all_budget_updates) - .await - .map(Json) - .map_err(|_| Status { code: 500 }) -} diff --git a/backend/src/routes/category.rs b/backend/src/routes/category.rs deleted file mode 100644 index 01fd8d1..0000000 --- a/backend/src/routes/category.rs +++ /dev/null @@ -1,16 +0,0 @@ -use rocket::get; -use rocket::http::Status; -use rocket::serde::json::Json; - -use crate::databases::UserDataDb; -use crate::models::category::Category; -use crate::queries::categories::get_all_categories as get_all_categories_query; - -#[get("/category")] -pub async fn get_all_categories(user_data_db: UserDataDb) -> Result>, Status> { - user_data_db - .run(get_all_categories_query) - .await - .map(Json) - .map_err(|_| Status { code: 500 }) -} diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs deleted file mode 100644 index bf0ab4a..0000000 --- a/backend/src/routes/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod account; -pub mod balance; -pub mod budget; -pub mod budget_update; -pub mod category; diff --git a/backend/src/schema.rs b/backend/src/schema.rs deleted file mode 100644 index 372f8f3..0000000 --- a/backend/src/schema.rs +++ /dev/null @@ -1,72 +0,0 @@ -// @generated automatically by Diesel CLI. - -diesel::table! { - accounts (id) { - id -> Integer, - name -> Text, - opening_balance -> Integer, - opening_date -> Text, - } -} - -diesel::table! { - budget_updates (id) { - id -> Integer, - category_id -> Integer, - date -> Text, - new_budget -> Integer, - new_period -> Integer, - } -} - -diesel::table! { - categories (id) { - id -> Integer, - name -> Text, - } -} - -diesel::table! { - category_transfers (id) { - id -> Integer, - description -> Text, - quantity -> Integer, - from_category_id -> Integer, - to_category_id -> Integer, - } -} - -diesel::table! { - transaction_categorisations (id) { - id -> Integer, - description -> Text, - quantity -> Integer, - transaction_id -> Integer, - category_id -> Integer, - } -} - -diesel::table! { - transactions (id) { - id -> Integer, - description -> Text, - payee -> Text, - quantity -> Integer, - date -> Text, - account_id -> Integer, - } -} - -diesel::joinable!(budget_updates -> categories (category_id)); -diesel::joinable!(transaction_categorisations -> categories (category_id)); -diesel::joinable!(transaction_categorisations -> transactions (transaction_id)); -diesel::joinable!(transactions -> accounts (account_id)); - -diesel::allow_tables_to_appear_in_same_query!( - accounts, - budget_updates, - categories, - category_transfers, - transaction_categorisations, - transactions, -); diff --git a/backend/src/utils/calculate_budgets_accrual.rs b/backend/src/utils/calculate_budgets_accrual.rs deleted file mode 100644 index f14260d..0000000 --- a/backend/src/utils/calculate_budgets_accrual.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::models::{budget_update::BudgetUpdate, category::Category, date_time_utc::DateTimeUtc}; - -pub fn calculate_budgets_accrual(category: &Category, budget_updates: &[BudgetUpdate]) -> i64 { - let mut budget_updates = budget_updates - .iter() - .filter(|bu| bu.category_id == category.id) - .collect::>(); - budget_updates.sort_by_key(|bu| bu.date); - budget_updates.reverse(); - - let mut budgets_accrual = 0.0_f64; - let mut calculated_back_to_date = DateTimeUtc::now(); - - for budget_update in budget_updates { - let days_on_this_budget = calculated_back_to_date.day_diff(&budget_update.date); - if days_on_this_budget == 0_u32 { - continue; - } - let budget_per_day = budget_update.new_budget as f64 / budget_update.new_period as f64; - budgets_accrual += days_on_this_budget as f64 * budget_per_day; - calculated_back_to_date = budget_update.date; - } - - budgets_accrual.floor() as i64 -} - -#[cfg(test)] -mod test { - use chrono::{Days, Timelike, Utc}; - - use crate::models::{ - budget_update::BudgetUpdate, category::Category, date_time_utc::DateTimeUtc, - }; - - use super::calculate_budgets_accrual; - - #[test] - fn when_no_budget_updates_then_return_zero() { - let category = Category { - id: 0, - name: String::from("Groceries"), - }; - let budget_updates = []; - - let result = calculate_budgets_accrual(&category, &budget_updates); - - assert_eq!(result, 0_i64); - } - - #[test] - fn when_one_budget_update_for_another_category_then_return_zero() { - let category = Category { - id: 0, - name: String::from("Groceries"), - }; - let budget_updates = [BudgetUpdate { - id: 0, - category_id: 1, - date: DateTimeUtc::now(), - new_budget: 100, - new_period: 1, - }]; - - let result = calculate_budgets_accrual(&category, &budget_updates); - - assert_eq!(result, 0_i64); - } - - #[test] - fn when_one_budget_update_then_return_quantity_times_days_in_past() { - let category = Category { - id: 0, - name: String::from("Groceries"), - }; - let budget_updates = [BudgetUpdate { - id: 0, - category_id: 0, - date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), - new_budget: 100, - new_period: 1, - }]; - - let result = calculate_budgets_accrual(&category, &budget_updates); - - assert_eq!(result, 12_000); - } - - #[test] - fn when_two_budget_updates_then_calculates_as_expected() { - let category = Category { - id: 0, - name: String::from("Groceries"), - }; - let budget_updates = [ - BudgetUpdate { - id: 0, - category_id: 0, - date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(60)).unwrap()), - new_budget: 200, - new_period: 1, - }, - BudgetUpdate { - id: 0, - category_id: 0, - date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), - new_budget: 100, - new_period: 1, - }, - ]; - - let result = calculate_budgets_accrual(&category, &budget_updates); - - assert_eq!(result, 18_000); - } - - #[test] - fn when_two_budget_updates_on_same_day_then_uses_latest_only() { - let category = Category { - id: 0, - name: String::from("Groceries"), - }; - let budget_updates = [ - BudgetUpdate { - id: 0, - category_id: 0, - date: DateTimeUtc::new( - Utc::now() - .checked_sub_days(Days::new(120)) - .unwrap() - .with_hour(13) - .unwrap(), - ), - new_budget: 200, - new_period: 1, - }, - BudgetUpdate { - id: 0, - category_id: 0, - date: DateTimeUtc::new( - Utc::now() - .checked_sub_days(Days::new(120)) - .unwrap() - .with_hour(12) - .unwrap(), - ), - new_budget: 100, - new_period: 1, - }, - ]; - let mut budget_updates_reversed = budget_updates.clone(); - budget_updates_reversed.reverse(); - - let result_leftways = calculate_budgets_accrual(&category, &budget_updates); - let result_rightways = calculate_budgets_accrual(&category, &budget_updates_reversed); - - assert_eq!(result_leftways, 24_000); - assert_eq!(result_rightways, 24_000); - } -} diff --git a/backend/src/utils/calculate_category_balance.rs b/backend/src/utils/calculate_category_balance.rs deleted file mode 100644 index 3c1c62f..0000000 --- a/backend/src/utils/calculate_category_balance.rs +++ /dev/null @@ -1,141 +0,0 @@ -use crate::models::{budget_update::BudgetUpdate, category::Category}; - -use super::{calculate_budgets_accrual, find_by_id_or}; - -pub fn calculate_category_balance( - category: &Category, - transaction_sums: &[(i32, Option)], - category_transfer_from_sums: &[(i32, Option)], - category_transfer_to_sums: &[(i32, Option)], - budget_updates: &[BudgetUpdate], -) -> i64 { - let transaction_sum = find_by_id_or(transaction_sums, category.id, 0); - let category_transfer_from_sum = find_by_id_or(category_transfer_from_sums, category.id, 0); - let category_transfer_to_sum = find_by_id_or(category_transfer_to_sums, category.id, 0); - let budget_accruals_sum = calculate_budgets_accrual(category, budget_updates); - - transaction_sum + budget_accruals_sum + category_transfer_to_sum - category_transfer_from_sum -} - -#[cfg(test)] -mod test { - use chrono::{Days, Utc}; - - use crate::{ - models::{budget_update::BudgetUpdate, category::Category, date_time_utc::DateTimeUtc}, - utils::calculate_category_balance, - }; - - #[test] - fn when_no_data_then_returns_zero() { - let category = Category { - id: 0, - name: String::new(), - }; - - let result = calculate_category_balance(&category, &[], &[], &[], &[]); - - assert_eq!(result, 0_i64); - } - - #[test] - fn when_data_only_for_another_category_then_returns_zero() { - let category = Category { - id: 0, - name: String::new(), - }; - let transaction_sums = [(1, Some(100))]; - let category_transfer_from_sums = [(1, Some(91))]; - let category_transfer_to_sums = [(2, Some(79))]; - let budget_updates = [BudgetUpdate { - id: 0, - category_id: 1, - date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), - new_budget: 77, - new_period: 1, - }]; - - let result = calculate_category_balance( - &category, - &transaction_sums, - &category_transfer_from_sums, - &category_transfer_to_sums, - &budget_updates, - ); - - assert_eq!(result, 0_i64); - } - - #[test] - fn when_transaction_sum_is_none_then_returns_zero() { - let category = Category { - id: 0, - name: String::new(), - }; - let transaction_sums = [(0, None)]; - let category_transfer_from_sums = []; - let category_transfer_to_sums = []; - let budget_updates = []; - - let result = calculate_category_balance( - &category, - &transaction_sums, - &category_transfer_from_sums, - &category_transfer_to_sums, - &budget_updates, - ); - - assert_eq!(result, 0_i64); - } - - #[test] - fn when_category_transfers_are_none_then_returns_zero() { - let category = Category { - id: 0, - name: String::new(), - }; - let transaction_sums = []; - let category_transfer_from_sums = [(0, None)]; - let category_transfer_to_sums = [(0, None)]; - let budget_updates = []; - - let result = calculate_category_balance( - &category, - &transaction_sums, - &category_transfer_from_sums, - &category_transfer_to_sums, - &budget_updates, - ); - - assert_eq!(result, 0_i64); - } - - #[test] - fn when_some_data_then_calculates_as_expected() { - let category = Category { - id: 0, - name: String::new(), - }; - let transaction_sums = [(0, Some(100))]; - let category_transfer_from_sums = [(0, Some(91))]; - let category_transfer_to_sums = [(0, Some(79))]; - let budget_updates = [BudgetUpdate { - id: 0, - category_id: 0, - date: DateTimeUtc::new(Utc::now().checked_sub_days(Days::new(120)).unwrap()), - new_budget: 77, - new_period: 1, - }]; - - let result = calculate_category_balance( - &category, - &transaction_sums, - &category_transfer_from_sums, - &category_transfer_to_sums, - &budget_updates, - ); - - let expected_result = 100 + (-91) + 79 + 77 * 120; - assert_eq!(result, expected_result); - } -} diff --git a/backend/src/utils/find_by_id_or.rs b/backend/src/utils/find_by_id_or.rs deleted file mode 100644 index f5add30..0000000 --- a/backend/src/utils/find_by_id_or.rs +++ /dev/null @@ -1,53 +0,0 @@ -pub fn find_by_id_or( - arr: &[(Id, Option)], - id: Id, - default: Val, -) -> Val { - arr.iter() - .find(|(elem_id, _)| *elem_id == id) - .map_or(default, |row| match row { - (_, Some(quantity)) => *quantity, - (_, _) => default, - }) -} - -#[cfg(test)] -mod test { - use super::find_by_id_or; - - #[test] - fn when_no_elements_then_return_default() { - let arr = []; - - let result = find_by_id_or(&arr, 1, 42); - - assert_eq!(result, 42); - } - - #[test] - fn when_id_not_in_elements_then_return_default() { - let arr = [(0, Some(100)), (2, Some(200))]; - - let result = find_by_id_or(&arr, 1, 42); - - assert_eq!(result, 42); - } - - #[test] - fn when_value_is_none_then_return_default() { - let arr = [(1, None)]; - - let result = find_by_id_or(&arr, 1, 42); - - assert_eq!(result, 42); - } - - #[test] - fn when_value_is_some_then_return_value() { - let arr = [(1, Some(67))]; - - let result = find_by_id_or(&arr, 1, 42); - - assert_eq!(result, 67); - } -} diff --git a/backend/src/utils/mod.rs b/backend/src/utils/mod.rs deleted file mode 100644 index 84a27af..0000000 --- a/backend/src/utils/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod calculate_budgets_accrual; -mod calculate_category_balance; -mod find_by_id_or; - -pub use calculate_budgets_accrual::calculate_budgets_accrual; -pub use calculate_category_balance::calculate_category_balance; -pub use find_by_id_or::find_by_id_or; diff --git a/importers/actualbudget/Cargo.lock b/importers/actualbudget/Cargo.lock deleted file mode 100644 index 63a53b9..0000000 --- a/importers/actualbudget/Cargo.lock +++ /dev/null @@ -1,356 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actualbudget" -version = "0.1.0" -dependencies = [ - "chrono", - "sqlite", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cc" -version = "1.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "sqlite" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfe6fb16f2bee6452feeb4d12bfa404fbcd3cfc121b2950e501d1ae9cae718e" -dependencies = [ - "sqlite3-sys", -] - -[[package]] -name = "sqlite3-src" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "174d4a6df77c27db281fb23de1a6d968f3aaaa4807c2a1afa8056b971f947b4a" -dependencies = [ - "cc", - "pkg-config", -] - -[[package]] -name = "sqlite3-sys" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3901ada7090c3c3584dc92ec7ef1b7091868d13bfe6d7de9f0bcaffee7d0ade5" -dependencies = [ - "sqlite3-src", -] - -[[package]] -name = "syn" -version = "2.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/importers/actualbudget/Cargo.toml b/importers/actualbudget/Cargo.toml deleted file mode 100644 index 7cc3e1b..0000000 --- a/importers/actualbudget/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "actualbudget" -version = "0.1.0" -edition = "2021" - -[dependencies] -sqlite = "0.36.1" -chrono = { version = "0.4.38", features = ["serde"] } diff --git a/importers/actualbudget/src/getters.rs b/importers/actualbudget/src/getters.rs deleted file mode 100644 index 88a660d..0000000 --- a/importers/actualbudget/src/getters.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::models::actualbudget::{ - account::Account, category::Category, date::Date, transaction::Transaction, - zero_budget::ZeroBudget, -}; -use sqlite::Connection; - -pub fn get_first_transaction_date(connection: &Connection) -> Date { - let mut statement = connection - .prepare("select min(date) as min_date from transactions;") - .unwrap(); - match statement.next() { - Ok(sqlite::State::Row) => Date::from_date_row(&statement, "min_date"), - err => panic!("Could not find first transaction date: {:?}", err), - } -} - -pub fn get_accounts(connection: &Connection) -> Vec { - let mut statement = connection.prepare("select * from accounts;").unwrap(); - let mut accounts = Vec::::new(); - while let Ok(sqlite::State::Row) = statement.next() { - accounts.push(Account::from_row(&statement)); - } - accounts -} - -pub fn get_categories(connection: &Connection) -> Vec { - let mut statement = connection.prepare("select * from categories;").unwrap(); - let mut categories = Vec::::new(); - while let Ok(sqlite::State::Row) = statement.next() { - categories.push(Category::from_row(&statement)); - } - categories -} - -pub fn get_zero_budgets(connection: &Connection) -> Vec { - let mut statement = connection.prepare("select * from zero_budgets;").unwrap(); - let mut zero_budgets = Vec::::new(); - while let Ok(sqlite::State::Row) = statement.next() { - zero_budgets.push(ZeroBudget::from_row(&statement)); - } - zero_budgets -} - -pub fn get_category_transfers_account(connection: &Connection) -> Account { - let mut statement = connection - .prepare("select * from accounts where name = 'Category transfers';") - .unwrap(); - match statement.next() { - Ok(sqlite::State::Row) => Account::from_row(&statement), - _ => panic!("Could not determine category transfers account"), - } -} - -pub fn get_transactions(connection: &Connection) -> Vec { - let mut statement = connection.prepare("select * from transactions;").unwrap(); - let mut transactions = Vec::::new(); - while let Ok(sqlite::State::Row) = statement.next() { - transactions.push(Transaction::from_row(&statement)); - } - transactions -} diff --git a/importers/actualbudget/src/inserters.rs b/importers/actualbudget/src/inserters.rs deleted file mode 100644 index 179de97..0000000 --- a/importers/actualbudget/src/inserters.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::models::budgeting_app::{ - account::Account, budget_update::BudgetUpdate, category::Category, - category_transfer::CategoryTransfer, transaction::Transaction, - transaction_categorisation::TransactionCategorisation, -}; -use sqlite::Connection; - -pub fn insert_accounts(connection: &Connection, accounts: &[Account]) { - for account in accounts { - let str = format!( - " - insert into - accounts(id, name, opening_balance, opening_date) - values({0}, '{1}', {2}, '{3}'); - ", - account.id, - escape_str(&account.name), - account.opening_balance, - account.opening_date - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_categories(connection: &Connection, categories: &[Category]) { - for category in categories { - let str = format!( - " - insert into - categories(id, name) - values({0}, '{1}'); - ", - category.id, - escape_str(&category.name), - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_budget_updates(connection: &Connection, budget_updates: &[BudgetUpdate]) { - for budget_update in budget_updates { - let str = format!( - " - insert into - budget_updates(id, category_id, date, new_budget, new_period) - values({0}, {1}, '{2}', {3}, {4}); - ", - budget_update.id, - budget_update.category_id, - budget_update.date, - budget_update.new_budget, - budget_update.new_period, - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_transactions(connection: &Connection, transactions: &[Transaction]) { - for transaction in transactions { - let str = format!( - " - insert into - transactions(id, description, payee, quantity, date, account_id) - values({0}, '{1}', '{2}', {3}, '{4}', {5}); - ", - transaction.id, - escape_str(&transaction.description), - escape_str(&transaction.payee), - transaction.quantity, - transaction.date, - transaction.account_id, - ); - connection - .execute(str.clone()) - .unwrap_or_else(|_| panic!("{}", &str.clone())); - } -} - -pub fn insert_transaction_categorisations( - connection: &Connection, - transaction_categorisations: &[TransactionCategorisation], -) { - for transaction_categorisation in transaction_categorisations { - let str = format!( - " - insert into - transaction_categorisations(id, description, quantity, transaction_id, category_id) - values({0}, '{1}', {2}, {3}, {4}); - ", - transaction_categorisation.id, - escape_str(&transaction_categorisation.description), - transaction_categorisation.quantity, - transaction_categorisation.transaction_id, - transaction_categorisation.category_id, - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_category_transfers(connection: &Connection, category_transfers: &[CategoryTransfer]) { - for category_transfer in category_transfers { - let str = format!( - " - insert into - category_transfers(id, description, quantity, from_category_id, to_category_id) - values({0}, '{1}', {2}, {3}, {4}); - ", - category_transfer.id, - escape_str(&category_transfer.description), - category_transfer.quantity, - category_transfer.from_category_id, - category_transfer.to_category_id, - ); - connection.execute(str).unwrap(); - } -} - -fn escape_str(str: &str) -> String { - str.replace("'", "''") -} diff --git a/importers/actualbudget/src/main.rs b/importers/actualbudget/src/main.rs deleted file mode 100644 index 495216e..0000000 --- a/importers/actualbudget/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -pub mod getters; -pub mod inserters; -pub mod models; -pub mod transformers; - -use getters::*; -use inserters::*; -use sqlite::Connection; - -fn main() { - let connection_src = sqlite::open("in.sqlite").unwrap(); - let connection_dest = sqlite::open("out.sqlite").unwrap(); - - let actualbudget_first_transaction_date = get_first_transaction_date(&connection_src); - let actualbudget_accounts = get_accounts(&connection_src); - let actualbudget_categories = get_categories(&connection_src); - let actualbudget_zero_budgets = get_zero_budgets(&connection_src); - let actualbudget_category_transfers_account = get_category_transfers_account(&connection_src); - let actualbudget_transactions = get_transactions(&connection_src); - - let first_transaction_date = transformers::transform_date(&actualbudget_first_transaction_date); - let accounts = - transformers::transform_accounts(&actualbudget_accounts, &first_transaction_date); - let categories = transformers::transform_categories(&actualbudget_categories); - let budget_updates = transformers::transform_budget_updates(&actualbudget_zero_budgets); - let (transactions, transaction_categorisations) = transformers::transform_transactions( - &actualbudget_transactions, - &actualbudget_category_transfers_account, - ); - let category_transfers = transformers::transform_category_transfers( - &actualbudget_transactions, - &actualbudget_category_transfers_account, - ); - - clear_destination(&connection_dest); - insert_accounts(&connection_dest, &accounts); - insert_categories(&connection_dest, &categories); - insert_budget_updates(&connection_dest, &budget_updates); - insert_transactions(&connection_dest, &transactions); - insert_transaction_categorisations(&connection_dest, &transaction_categorisations); - insert_category_transfers(&connection_dest, &category_transfers); -} - -fn clear_destination(connection: &Connection) { - connection - .execute( - " - delete from accounts; - delete from budget_updates; - delete from categories; - delete from transaction_categorisations; - delete from category_transfers; - delete from transactions; - ", - ) - .unwrap(); -} diff --git a/importers/actualbudget/src/models/actualbudget/account.rs b/importers/actualbudget/src/models/actualbudget/account.rs deleted file mode 100644 index 727c082..0000000 --- a/importers/actualbudget/src/models/actualbudget/account.rs +++ /dev/null @@ -1,15 +0,0 @@ -use sqlite::Statement; - -pub struct Account { - pub id: String, - pub name: String, -} - -impl Account { - pub fn from_row(statement: &Statement) -> Account { - Account { - id: statement.read("id").unwrap(), - name: statement.read("name").unwrap(), - } - } -} diff --git a/importers/actualbudget/src/models/actualbudget/category.rs b/importers/actualbudget/src/models/actualbudget/category.rs deleted file mode 100644 index 21fdf80..0000000 --- a/importers/actualbudget/src/models/actualbudget/category.rs +++ /dev/null @@ -1,15 +0,0 @@ -use sqlite::Statement; - -pub struct Category { - pub id: String, - pub name: String, -} - -impl Category { - pub fn from_row(statement: &Statement) -> Category { - Category { - id: statement.read("id").unwrap(), - name: statement.read("name").unwrap(), - } - } -} diff --git a/importers/actualbudget/src/models/actualbudget/date.rs b/importers/actualbudget/src/models/actualbudget/date.rs deleted file mode 100644 index bd37c46..0000000 --- a/importers/actualbudget/src/models/actualbudget/date.rs +++ /dev/null @@ -1,69 +0,0 @@ -use sqlite::Statement; - -#[derive(Clone, PartialEq, Eq)] -pub struct Date { - value: i64, - last_date_part: DatePart, -} - -#[derive(Clone, PartialOrd, PartialEq, Eq)] -enum DatePart { - MonthOfYear, - DayOfMonth, -} - -// actualbudget represents dates as numbers which "look like" the corresponding -// ISO string when written in decimal. E.g. 20240901 represents 1 Sep 2024. It -// also uses month specifiers, e.g. 202409 to represent Sep 2024. In this case, -// we interpret this as the first day of the month, e.g. 2024-09-01. - -impl Date { - pub fn from_date_row(statement: &Statement, column: &str) -> Date { - Date { - value: statement.read::(column).unwrap(), - last_date_part: DatePart::DayOfMonth, - } - } - - pub fn from_month_row(statement: &Statement, column: &str) -> Date { - Date { - value: statement.read::(column).unwrap() * 100 + 1, - last_date_part: DatePart::MonthOfYear, - } - } - - pub fn to_iso(&self) -> String { - format!("{0}-{1}-{2}", self.year(), self.month(), self.day()) - } - - pub fn year(&self) -> String { - self.value.to_string()[0..4].to_string() - } - - pub fn month(&self) -> String { - self.value.to_string()[4..6].to_string() - } - - pub fn day(&self) -> String { - self.dayified_value().to_string()[6..8].to_string() - } - - fn dayified_value(&self) -> i64 { - match self.last_date_part { - DatePart::DayOfMonth => self.value, - DatePart::MonthOfYear => self.value * 100 + 1, - } - } -} - -impl Ord for Date { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.dayified_value().cmp(&other.dayified_value()) - } -} - -impl PartialOrd for Date { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} diff --git a/importers/actualbudget/src/models/actualbudget/mod.rs b/importers/actualbudget/src/models/actualbudget/mod.rs deleted file mode 100644 index 5ca472f..0000000 --- a/importers/actualbudget/src/models/actualbudget/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod account; -pub mod category; -pub mod date; -pub mod transaction; -pub mod zero_budget; diff --git a/importers/actualbudget/src/models/actualbudget/transaction.rs b/importers/actualbudget/src/models/actualbudget/transaction.rs deleted file mode 100644 index 0a21cfe..0000000 --- a/importers/actualbudget/src/models/actualbudget/transaction.rs +++ /dev/null @@ -1,43 +0,0 @@ -use sqlite::Statement; - -use super::date::Date; - -#[derive(Clone)] -pub struct Transaction { - pub id: String, - pub is_parent: bool, - pub is_child: bool, - pub account_id: String, - pub category_id: String, - pub amount: i64, - pub payee: String, - pub notes: String, - pub date: Date, - pub parent_id: Option, -} - -impl Transaction { - pub fn from_row(statement: &Statement) -> Transaction { - Transaction { - id: statement.read("id").unwrap(), - is_parent: statement.read::("isParent").unwrap() == 1, - is_child: statement.read::("isChild").unwrap() == 1, - account_id: statement.read("acct").unwrap(), - category_id: statement - .read::, &str>("category") - .map(Option::::unwrap_or_default) - .unwrap(), - amount: statement.read("amount").unwrap(), - payee: statement - .read::, &str>("description") - .map(Option::::unwrap_or_default) - .unwrap(), - notes: statement - .read::, &str>("notes") - .map(Option::::unwrap_or_default) - .unwrap(), - date: Date::from_date_row(statement, "date"), - parent_id: statement.read("parent_id").unwrap(), - } - } -} diff --git a/importers/actualbudget/src/models/actualbudget/zero_budget.rs b/importers/actualbudget/src/models/actualbudget/zero_budget.rs deleted file mode 100644 index 87c88e7..0000000 --- a/importers/actualbudget/src/models/actualbudget/zero_budget.rs +++ /dev/null @@ -1,30 +0,0 @@ -use sqlite::Statement; - -use super::date::Date; - -#[derive(Clone)] -pub struct ZeroBudget { - pub id: String, - pub month: Date, - pub category_id: String, - pub amount: i64, - pub do_carry_over: bool, -} - -impl ZeroBudget { - pub fn from_row(statement: &Statement) -> ZeroBudget { - ZeroBudget { - id: statement.read("id").unwrap(), - month: Date::from_month_row(statement, "month"), - category_id: statement.read("category").unwrap(), - amount: statement.read("amount").unwrap(), - do_carry_over: match statement.read::("carryover").unwrap() { - 0 => false, - 1 => true, - carryover => { - panic!("carryover column was neither 0 nor 1, but instead {carryover}") - } - }, - } - } -} diff --git a/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs b/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs deleted file mode 100644 index e44fa8c..0000000 --- a/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs +++ /dev/null @@ -1,57 +0,0 @@ -use chrono::{DateTime, Datelike, Utc}; -use diesel::{ - deserialize::{FromSql, FromSqlRow}, - expression::AsExpression, - serialize::ToSql, - sql_types::Text, - sqlite::Sqlite, -}; -use serde::{Deserialize, Serialize}; - -#[derive( - Eq, - PartialEq, - Ord, - PartialOrd, - Serialize, - Deserialize, - Copy, - Clone, - Debug, - AsExpression, - FromSqlRow, -)] -#[diesel(sql_type=Text)] -pub struct DateTimeUtc(DateTime); - -impl DateTimeUtc { - pub fn day_diff(&self, other: &DateTimeUtc) -> u32 { - self.0 - .num_days_from_ce() - .abs_diff(other.0.num_days_from_ce()) - } - - pub fn new(date_time_utc: DateTime) -> Self { - Self(date_time_utc) - } -} - -impl ToSql for DateTimeUtc { - fn to_sql<'b>( - &'b self, - out: &mut diesel::serialize::Output<'b, '_, Sqlite>, - ) -> diesel::serialize::Result { - out.set_value((*self).0.to_rfc3339()); - Ok(diesel::serialize::IsNull::No) - } -} - -impl FromSql for DateTimeUtc { - fn from_sql( - bytes: ::RawValue<'_>, - ) -> diesel::deserialize::Result { - let str: String = FromSql::::from_sql(bytes)?; - let date_time = str.as_str().parse::>()?; - Ok(DateTimeUtc(date_time)) - } -} diff --git a/importers/actualbudget/src/models/budgeting_app/account.rs b/importers/actualbudget/src/models/budgeting_app/account.rs deleted file mode 100644 index 165b55c..0000000 --- a/importers/actualbudget/src/models/budgeting_app/account.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::date_time_utc::DateTimeUtc; - -pub struct Account { - pub id: i32, - pub name: String, - pub opening_balance: i32, - pub opening_date: DateTimeUtc, -} diff --git a/importers/actualbudget/src/models/budgeting_app/budget_update.rs b/importers/actualbudget/src/models/budgeting_app/budget_update.rs deleted file mode 100644 index 9958446..0000000 --- a/importers/actualbudget/src/models/budgeting_app/budget_update.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::date_time_utc::DateTimeUtc; - -#[derive(Clone)] -pub struct BudgetUpdate { - pub id: i32, - pub category_id: i32, - pub date: DateTimeUtc, - pub new_budget: i32, - pub new_period: i32, -} diff --git a/importers/actualbudget/src/models/budgeting_app/category.rs b/importers/actualbudget/src/models/budgeting_app/category.rs deleted file mode 100644 index 3f51dfd..0000000 --- a/importers/actualbudget/src/models/budgeting_app/category.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub struct Category { - pub id: i32, - pub name: String, -} diff --git a/importers/actualbudget/src/models/budgeting_app/category_transfer.rs b/importers/actualbudget/src/models/budgeting_app/category_transfer.rs deleted file mode 100644 index 0097429..0000000 --- a/importers/actualbudget/src/models/budgeting_app/category_transfer.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub struct CategoryTransfer { - pub id: i32, - pub description: String, - pub quantity: i32, - pub from_category_id: i32, - pub to_category_id: i32, -} diff --git a/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs b/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs deleted file mode 100644 index 69cdfe2..0000000 --- a/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::{fmt::Display, str::FromStr}; - -use chrono::{DateTime, Utc}; - -#[derive(Clone)] -pub struct DateTimeUtc(DateTime); - -impl DateTimeUtc { - pub fn new(date: &str) -> Self { - let mut date_time = String::from(date); - date_time.push_str("T00:00:00.000Z"); - Self(DateTime::::from_str(&date_time).unwrap()) - } -} - -impl Display for DateTimeUtc { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} diff --git a/importers/actualbudget/src/models/budgeting_app/mod.rs b/importers/actualbudget/src/models/budgeting_app/mod.rs deleted file mode 100644 index bca0f99..0000000 --- a/importers/actualbudget/src/models/budgeting_app/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod account; -pub mod budget_update; -pub mod category; -pub mod category_transfer; -pub mod date_time_utc; -pub mod transaction; -pub mod transaction_categorisation; diff --git a/importers/actualbudget/src/models/budgeting_app/transaction.rs b/importers/actualbudget/src/models/budgeting_app/transaction.rs deleted file mode 100644 index e679e1e..0000000 --- a/importers/actualbudget/src/models/budgeting_app/transaction.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::date_time_utc::DateTimeUtc; - -#[derive(Clone)] -pub struct Transaction { - pub id: i32, - pub description: String, - pub payee: String, - pub quantity: i32, - pub date: DateTimeUtc, - pub account_id: i32, -} diff --git a/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs b/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs deleted file mode 100644 index ca7e7f5..0000000 --- a/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub struct TransactionCategorisation { - pub id: i32, - pub description: String, - pub quantity: i32, - pub transaction_id: i32, - pub category_id: i32, -} diff --git a/importers/actualbudget/src/models/mod.rs b/importers/actualbudget/src/models/mod.rs deleted file mode 100644 index cf1b71a..0000000 --- a/importers/actualbudget/src/models/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod actualbudget; -pub mod budgeting_app; diff --git a/importers/actualbudget/src/transformers.rs b/importers/actualbudget/src/transformers.rs deleted file mode 100644 index 678ea05..0000000 --- a/importers/actualbudget/src/transformers.rs +++ /dev/null @@ -1,221 +0,0 @@ -use core::panic; -use std::hash::{DefaultHasher, Hash, Hasher}; - -use crate::models::{ - actualbudget, - budgeting_app::{ - account::Account, budget_update::BudgetUpdate, category::Category, - category_transfer::CategoryTransfer, date_time_utc::DateTimeUtc, transaction::Transaction, - transaction_categorisation::TransactionCategorisation, - }, -}; - -pub fn transform_date(actualbudget_date: &actualbudget::date::Date) -> DateTimeUtc { - DateTimeUtc::new(&actualbudget_date.to_iso()) -} - -pub fn transform_accounts( - actualbudget_accounts: &[actualbudget::account::Account], - first_transaction_date: &DateTimeUtc, -) -> Vec { - let mut accounts = Vec::::new(); - for account in actualbudget_accounts { - accounts.push(transform_account(account, first_transaction_date)); - } - accounts -} - -fn transform_account( - actualbudget_account: &actualbudget::account::Account, - opening_date: &DateTimeUtc, -) -> Account { - Account { - id: transform_id(&actualbudget_account.id.clone()), - name: actualbudget_account.name.clone(), - opening_balance: 0, - opening_date: opening_date.clone(), - } -} - -pub fn transform_categories( - actualbudget_categories: &[actualbudget::category::Category], -) -> Vec { - let mut categories = Vec::::new(); - for actualbudget_category in actualbudget_categories { - categories.push(Category { - id: transform_id(&actualbudget_category.id.clone()), - name: actualbudget_category.name.clone(), - }) - } - categories -} - -fn transform_id(actualbudget_id: &str) -> i32 { - let mut hasher = DefaultHasher::new(); - actualbudget_id.hash(&mut hasher); - (hasher.finish() % std::convert::TryInto::::try_into(i32::MAX).unwrap()) - .try_into() - .unwrap() -} - -pub fn transform_budget_updates( - actualbudget_zero_budgets: &[actualbudget::zero_budget::ZeroBudget], -) -> Vec { - let mut budget_updates = Vec::::new(); - for zero_budget in actualbudget_zero_budgets { - let first_zero_budget_in_category = get_first_zero_budget_in_category( - actualbudget_zero_budgets, - &zero_budget.category_id.clone(), - ); - if first_zero_budget_in_category.is_some_and(|zb| zb.id == zero_budget.id) { - continue; - } - budget_updates.push(BudgetUpdate { - id: transform_id(&zero_budget.id.clone()), - category_id: transform_id(&zero_budget.category_id.clone()), - date: transform_date(&zero_budget.month.clone()), - new_budget: zero_budget.amount.try_into().unwrap(), - new_period: 30, - }) - } - budget_updates -} - -fn get_first_zero_budget_in_category( - actualbudget_zero_budgets: &[actualbudget::zero_budget::ZeroBudget], - actualbudget_category_id: &str, -) -> Option { - let mut category_zero_budgets = actualbudget_zero_budgets.to_vec(); - category_zero_budgets.retain(|zb| zb.category_id == actualbudget_category_id); - category_zero_budgets - .iter() - .min_by_key(|zb| zb.month.clone()) - .cloned() -} - -pub fn transform_transactions( - actualbudget_transactions: &[actualbudget::transaction::Transaction], - actualbudget_category_transfers_account: &actualbudget::account::Account, -) -> (Vec, Vec) { - let mut transactions = Vec::::new(); - let mut transaction_categorisations = Vec::::new(); - - let mut actualbudget_transactions = actualbudget_transactions.to_vec(); - actualbudget_transactions - .retain(|t| t.category_id.clone() != actualbudget_category_transfers_account.id.clone()); - let mut parent_actualbudget_transactions = actualbudget_transactions.to_vec(); - parent_actualbudget_transactions.retain(|t| t.is_parent); - let mut child_actualbudget_transactions = actualbudget_transactions.to_vec(); - child_actualbudget_transactions.retain(|t| t.is_child); - let mut atomic_actualbudget_transactions = actualbudget_transactions.to_vec(); - atomic_actualbudget_transactions.retain(|t| !t.is_parent && !t.is_child); - - for atomic_actualbudget_transaction in atomic_actualbudget_transactions { - let transaction_id = transform_id(&atomic_actualbudget_transaction.id); - - transactions.push(Transaction { - id: transaction_id, - description: atomic_actualbudget_transaction.notes, - payee: atomic_actualbudget_transaction.payee, - quantity: atomic_actualbudget_transaction.amount.try_into().unwrap(), - date: transform_date(&atomic_actualbudget_transaction.date), - account_id: transform_id(&atomic_actualbudget_transaction.account_id), - }); - - transaction_categorisations.push(TransactionCategorisation { - id: transaction_id, - description: String::new(), - quantity: atomic_actualbudget_transaction.amount.try_into().unwrap(), - transaction_id, - category_id: transform_id(&atomic_actualbudget_transaction.category_id), - }); - } - - for parent_actualbudget_transaction in parent_actualbudget_transactions { - let mut child_actualbudget_transactions = child_actualbudget_transactions.to_vec(); - child_actualbudget_transactions.retain(|t| { - t.parent_id - .clone() - .is_some_and(|parent_id| parent_id == parent_actualbudget_transaction.id.clone()) - }); - - let transaction_id = transform_id(&parent_actualbudget_transaction.id); - - transactions.push(Transaction { - id: transaction_id, - description: parent_actualbudget_transaction.notes, - payee: parent_actualbudget_transaction.payee, - quantity: parent_actualbudget_transaction.amount.try_into().unwrap(), - date: transform_date(&parent_actualbudget_transaction.date), - account_id: transform_id(&parent_actualbudget_transaction.account_id), - }); - - for child_actualbudget_transaction in child_actualbudget_transactions { - transaction_categorisations.push(TransactionCategorisation { - id: transform_id(&child_actualbudget_transaction.id), - description: child_actualbudget_transaction.notes, - quantity: child_actualbudget_transaction.amount.try_into().unwrap(), - transaction_id, - category_id: transform_id(&child_actualbudget_transaction.category_id), - }); - } - } - - (transactions, transaction_categorisations) -} - -pub fn transform_category_transfers( - actualbudget_transactions: &[actualbudget::transaction::Transaction], - actualbudget_category_transfers_account: &actualbudget::account::Account, -) -> Vec { - let mut category_transfers = Vec::::new(); - - let mut actualbudget_category_transfers = actualbudget_transactions.to_vec(); - actualbudget_category_transfers - .retain(|t| t.category_id.clone() == actualbudget_category_transfers_account.id.clone()); - let mut actualbudget_parent_category_transfers = actualbudget_category_transfers.to_vec(); - actualbudget_parent_category_transfers.retain(|t| t.is_parent); - let mut actualbudget_child_category_transfers = actualbudget_category_transfers.to_vec(); - actualbudget_child_category_transfers.retain(|t| t.is_child); - - for actualbudget_parent_category_transfer in actualbudget_parent_category_transfers { - let mut actualbudget_child_category_transfers = - actualbudget_child_category_transfers.to_vec(); - actualbudget_child_category_transfers.retain(|t| { - t.parent_id - .clone() - .is_some_and(|parent_id| parent_id == actualbudget_parent_category_transfer.id) - }); - - if actualbudget_child_category_transfers.len() != 2 { - panic!("Category transfers should always have exactly two children."); - } - - let category_1 = actualbudget_child_category_transfers[0].clone(); - let category_2 = actualbudget_child_category_transfers[1].clone(); - - // To avoid duplicates - if category_1.amount >= 0 { - continue; - } - - if actualbudget_child_category_transfers[0].amount - != -actualbudget_child_category_transfers[1].amount - { - panic!("Category transfer children should sum to zero."); - } - - category_transfers.push(CategoryTransfer { - id: transform_id(&actualbudget_parent_category_transfer.id), - description: actualbudget_parent_category_transfer.notes, - quantity: actualbudget_child_category_transfers[0] - .amount - .try_into() - .unwrap(), - from_category_id: transform_id(&category_1.id), - to_category_id: transform_id(&category_2.id), - }); - } - - category_transfers -} -- cgit v1.2.3