Răsfoiți Sursa

Import items rather than spell out fully-qualified paths inline

Writing a module-qualified path at the call site (kuatia_core::Account,
std::collections::hash_map::Entry::Vacant, impl std::fmt::Display) hides
which items a module actually depends on and adds a second, longer name
for something the reader must confirm resolves where they expect. It is
noisiest where a sibling from the same module is already imported and the
inline path is the odd one out.

Import the items and refer to them by their short names across the
workspace: pull Account/Plan/ReservationId/Entry into their use blocks in
the commit engine, event_dedup_key/AnyRow/AutoId in the storage backends,
and adopt the conventional `use std::fmt;` / `use std::error::Error;`
form for the Display and Error trait impls. Genuine one-off references
that would need a fresh import for a single use are left as they are.

No behavior change; this is import hygiene only.
Cesar Rodas 2 săptămâni în urmă
părinte
comite
90f7bea3b4

+ 9 - 7
crates/kuatia-core/src/posting_resolution.rs

@@ -16,6 +16,8 @@
 //! they are reachable without standing up a store.
 //! they are reachable without standing up a store.
 
 
 use std::collections::HashMap;
 use std::collections::HashMap;
+use std::error::Error;
+use std::fmt;
 
 
 use kuatia_types::{
 use kuatia_types::{
     Account, AccountId, AssetId, Cent, Envelope, EnvelopeBuilder, NewPosting, OverflowError,
     Account, AccountId, AssetId, Cent, Envelope, EnvelopeBuilder, NewPosting, OverflowError,
@@ -41,8 +43,8 @@ pub struct InsufficientFunds {
     pub requested: Cent,
     pub requested: Cent,
 }
 }
 
 
-impl std::fmt::Display for InsufficientFunds {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Display for InsufficientFunds {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(
         write!(
             f,
             f,
             "insufficient funds: available {}, requested {}",
             "insufficient funds: available {}, requested {}",
@@ -51,7 +53,7 @@ impl std::fmt::Display for InsufficientFunds {
     }
     }
 }
 }
 
 
-impl std::error::Error for InsufficientFunds {}
+impl Error for InsufficientFunds {}
 
 
 /// Failure from resolution pass 2 ([`resolve_envelope`]).
 /// Failure from resolution pass 2 ([`resolve_envelope`]).
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -62,8 +64,8 @@ pub enum ResolveError {
     Overflow,
     Overflow,
 }
 }
 
 
-impl std::fmt::Display for ResolveError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Display for ResolveError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
         match self {
             Self::Selection(e) => write!(f, "selection: {e}"),
             Self::Selection(e) => write!(f, "selection: {e}"),
             Self::Overflow => write!(f, "monetary amount overflow"),
             Self::Overflow => write!(f, "monetary amount overflow"),
@@ -71,8 +73,8 @@ impl std::fmt::Display for ResolveError {
     }
     }
 }
 }
 
 
-impl std::error::Error for ResolveError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+impl Error for ResolveError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
         match self {
         match self {
             Self::Selection(e) => Some(e),
             Self::Selection(e) => Some(e),
             Self::Overflow => None,
             Self::Overflow => None,

+ 5 - 3
crates/kuatia-core/src/validate.rs

@@ -7,6 +7,8 @@
 //! [`PlanInput`]; this module never touches storage.
 //! [`PlanInput`]; this module never touches storage.
 
 
 use std::collections::{HashMap, HashSet};
 use std::collections::{HashMap, HashSet};
+use std::error::Error;
+use std::fmt;
 
 
 use crate::hash::{account_hash, envelope_id};
 use crate::hash::{account_hash, envelope_id};
 use kuatia_types::*;
 use kuatia_types::*;
@@ -129,8 +131,8 @@ pub enum ValidationError {
     Overflow,
     Overflow,
 }
 }
 
 
-impl std::fmt::Display for ValidationError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Display for ValidationError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
         match self {
             Self::EmptyTransfer => write!(f, "transfer has no postings"),
             Self::EmptyTransfer => write!(f, "transfer has no postings"),
             Self::DuplicateConsumedPosting(id) => write!(f, "duplicate consumed posting {id:?}"),
             Self::DuplicateConsumedPosting(id) => write!(f, "duplicate consumed posting {id:?}"),
@@ -200,7 +202,7 @@ impl std::fmt::Display for ValidationError {
     }
     }
 }
 }
 
 
-impl std::error::Error for ValidationError {}
+impl Error for ValidationError {}
 
 
 impl From<OverflowError> for ValidationError {
 impl From<OverflowError> for ValidationError {
     fn from(_: OverflowError) -> Self {
     fn from(_: OverflowError) -> Self {

+ 11 - 12
crates/kuatia-dashboard/src/data.rs

@@ -3,6 +3,8 @@
 //! Everything here is read-only. Monetary values stay as raw [`Cent`] (minor
 //! Everything here is read-only. Monetary values stay as raw [`Cent`] (minor
 //! units); presentation formats them.
 //! units); presentation formats them.
 
 
+use std::cmp::Reverse;
+use std::fmt;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
 use axum::{
 use axum::{
@@ -10,6 +12,7 @@ use axum::{
     http::StatusCode,
     http::StatusCode,
     response::{IntoResponse, Response},
     response::{IntoResponse, Response},
 };
 };
+use kuatia::error::LedgerError;
 use kuatia::ledger::Ledger;
 use kuatia::ledger::Ledger;
 use kuatia_core::{Account, AccountId, AssetId, Cent, PostingId, PostingState};
 use kuatia_core::{Account, AccountId, AssetId, Cent, PostingId, PostingState};
 use kuatia_storage::events::{LedgerEvent, LedgerEventKind};
 use kuatia_storage::events::{LedgerEvent, LedgerEventKind};
@@ -18,7 +21,7 @@ use serde::Serialize;
 use tera::Tera;
 use tera::Tera;
 
 
 use crate::assets::AssetMeta;
 use crate::assets::AssetMeta;
-use crate::seed::account_label;
+use crate::seed::{EXTERNAL, account_label};
 
 
 /// Shared handler state.
 /// Shared handler state.
 #[derive(Clone)]
 #[derive(Clone)]
@@ -219,10 +222,7 @@ pub async fn overview(state: &AppState) -> Result<OverviewDto, ApiError> {
     // mirrors everything in circulation.
     // mirrors everything in circulation.
     let mut issued = Vec::new();
     let mut issued = Vec::new();
     for asset in state.assets.iter() {
     for asset in state.assets.iter() {
-        let external = state
-            .ledger
-            .balance(&crate::seed::EXTERNAL, &asset.id)
-            .await?;
+        let external = state.ledger.balance(&EXTERNAL, &asset.id).await?;
         let issued_value = external
         let issued_value = external
             .checked_neg()
             .checked_neg()
             .map_err(|_| ApiError::internal("overflow"))?;
             .map_err(|_| ApiError::internal("overflow"))?;
@@ -281,7 +281,7 @@ pub async fn account_detail(state: &AppState, id: AccountId) -> Result<AccountDe
             status: posting_state_label(state).to_string(),
             status: posting_state_label(state).to_string(),
         })
         })
         .collect();
         .collect();
-    postings.sort_by_key(|p| std::cmp::Reverse(p.value));
+    postings.sort_by_key(|p| Reverse(p.value));
 
 
     let transfers = state
     let transfers = state
         .ledger
         .ledger
@@ -315,7 +315,7 @@ pub async fn transfers(state: &AppState, limit: Option<u32>) -> Result<Vec<Trans
     };
     };
     let page = state.ledger.query_transfers(&query).await?;
     let page = state.ledger.query_transfers(&query).await?;
     let mut out: Vec<TransferDto> = page.items.iter().map(transfer_dto).collect();
     let mut out: Vec<TransferDto> = page.items.iter().map(transfer_dto).collect();
-    out.sort_by_key(|t| std::cmp::Reverse(t.created_at));
+    out.sort_by_key(|t| Reverse(t.created_at));
     Ok(out)
     Ok(out)
 }
 }
 
 
@@ -345,13 +345,13 @@ impl ApiError {
     }
     }
 
 
     /// Build a 500 from any displayable error (used by the HTML render path).
     /// Build a 500 from any displayable error (used by the HTML render path).
-    pub fn from_display(err: impl std::fmt::Display) -> Self {
+    pub fn from_display(err: impl fmt::Display) -> Self {
         Self::internal(err.to_string())
         Self::internal(err.to_string())
     }
     }
 
 
     /// Build a 400 from a displayable error (used for a malformed account id in
     /// Build a 400 from a displayable error (used for a malformed account id in
     /// the URL).
     /// the URL).
-    pub fn bad_request(err: impl std::fmt::Display) -> Self {
+    pub fn bad_request(err: impl fmt::Display) -> Self {
         Self {
         Self {
             status: StatusCode::BAD_REQUEST,
             status: StatusCode::BAD_REQUEST,
             message: err.to_string(),
             message: err.to_string(),
@@ -359,9 +359,8 @@ impl ApiError {
     }
     }
 }
 }
 
 
-impl From<kuatia::error::LedgerError> for ApiError {
-    fn from(err: kuatia::error::LedgerError) -> Self {
-        use kuatia::error::LedgerError;
+impl From<LedgerError> for ApiError {
+    fn from(err: LedgerError) -> Self {
         let status = match err {
         let status = match err {
             LedgerError::AccountNotFound(_) => StatusCode::NOT_FOUND,
             LedgerError::AccountNotFound(_) => StatusCode::NOT_FOUND,
             _ => StatusCode::INTERNAL_SERVER_ERROR,
             _ => StatusCode::INTERNAL_SERVER_ERROR,

+ 2 - 1
crates/kuatia-dashboard/src/main.rs

@@ -16,6 +16,7 @@ mod data;
 mod seed;
 mod seed;
 mod ui;
 mod ui;
 
 
+use std::error::Error;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
 use axum::Router;
 use axum::Router;
@@ -59,7 +60,7 @@ struct Cli {
 }
 }
 
 
 #[tokio::main]
 #[tokio::main]
-async fn main() -> Result<(), Box<dyn std::error::Error>> {
+async fn main() -> Result<(), Box<dyn Error>> {
     tracing_subscriber::fmt()
     tracing_subscriber::fmt()
         .with_env_filter(
         .with_env_filter(
             tracing_subscriber::EnvFilter::try_from_default_env()
             tracing_subscriber::EnvFilter::try_from_default_env()

+ 7 - 6
crates/kuatia-dashboard/src/seed.rs

@@ -2,6 +2,7 @@
 //! them from an external boundary account, then runs payments and a
 //! them from an external boundary account, then runs payments and a
 //! multi-asset trade so the dashboard has something to visualize.
 //! multi-asset trade so the dashboard has something to visualize.
 
 
+use std::error::Error;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
 use kuatia::ledger::Ledger;
 use kuatia::ledger::Ledger;
@@ -45,7 +46,7 @@ pub fn account_label(id: AccountId) -> Option<&'static str> {
 /// connection its own separate database, so more than one would split the
 /// connection its own separate database, so more than one would split the
 /// ledger; one connection is also fine for a low-traffic dashboard on a file or
 /// ledger; one connection is also fine for a low-traffic dashboard on a file or
 /// Postgres backend.
 /// Postgres backend.
-pub async fn connect(db_url: &str) -> Result<Arc<Ledger>, Box<dyn std::error::Error>> {
+pub async fn connect(db_url: &str) -> Result<Arc<Ledger>, Box<dyn Error>> {
     sqlx::any::install_default_drivers();
     sqlx::any::install_default_drivers();
     let pool = sqlx::any::AnyPoolOptions::new()
     let pool = sqlx::any::AnyPoolOptions::new()
         .max_connections(1)
         .max_connections(1)
@@ -73,7 +74,7 @@ fn sqlite_creatable(db_url: &str) -> String {
 /// it seeded, `false` if the ledger was already populated (so re-running with
 /// it seeded, `false` if the ledger was already populated (so re-running with
 /// `--seed` against a persistent database is a safe no-op rather than a
 /// `--seed` against a persistent database is a safe no-op rather than a
 /// duplicate-id error).
 /// duplicate-id error).
-pub async fn seed_if_empty(ledger: &Arc<Ledger>) -> Result<bool, Box<dyn std::error::Error>> {
+pub async fn seed_if_empty(ledger: &Arc<Ledger>) -> Result<bool, Box<dyn Error>> {
     if !ledger.list_accounts().await?.is_empty() {
     if !ledger.list_accounts().await?.is_empty() {
         return Ok(false);
         return Ok(false);
     }
     }
@@ -82,7 +83,7 @@ pub async fn seed_if_empty(ledger: &Arc<Ledger>) -> Result<bool, Box<dyn std::er
 }
 }
 
 
 /// Populate the ledger with demo accounts and a spread of transfers.
 /// Populate the ledger with demo accounts and a spread of transfers.
-pub async fn populate(ledger: &Arc<Ledger>) -> Result<(), Box<dyn std::error::Error>> {
+pub async fn populate(ledger: &Arc<Ledger>) -> Result<(), Box<dyn Error>> {
     // Two-decimal assets (USD, EUR) and an 8-decimal asset (BTC).
     // Two-decimal assets (USD, EUR) and an 8-decimal asset (BTC).
     let fiat = Amount::new(2);
     let fiat = Amount::new(2);
     let btc = Amount::new(8);
     let btc = Amount::new(8);
@@ -130,7 +131,7 @@ async fn create(
     ledger: &Arc<Ledger>,
     ledger: &Arc<Ledger>,
     id: AccountId,
     id: AccountId,
     debit_must_not_exceed_credit: bool,
     debit_must_not_exceed_credit: bool,
-) -> Result<(), Box<dyn std::error::Error>> {
+) -> Result<(), Box<dyn Error>> {
     let account = if debit_must_not_exceed_credit {
     let account = if debit_must_not_exceed_credit {
         Account::debit_must_not_exceed_credit(id)
         Account::debit_must_not_exceed_credit(id)
     } else {
     } else {
@@ -145,7 +146,7 @@ async fn deposit(
     to: AccountId,
     to: AccountId,
     asset: AssetId,
     asset: AssetId,
     amount: Cent,
     amount: Cent,
-) -> Result<(), Box<dyn std::error::Error>> {
+) -> Result<(), Box<dyn Error>> {
     let transfer = TransferBuilder::new()
     let transfer = TransferBuilder::new()
         .deposit(to, asset, amount, EXTERNAL)?
         .deposit(to, asset, amount, EXTERNAL)?
         .build();
         .build();
@@ -159,7 +160,7 @@ async fn pay(
     to: AccountId,
     to: AccountId,
     asset: AssetId,
     asset: AssetId,
     amount: Cent,
     amount: Cent,
-) -> Result<(), Box<dyn std::error::Error>> {
+) -> Result<(), Box<dyn Error>> {
     let transfer = TransferBuilder::new().pay(from, to, asset, amount).build();
     let transfer = TransferBuilder::new().pay(from, to, asset, amount).build();
     ledger.commit(transfer).await?;
     ledger.commit(transfer).await?;
     Ok(())
     Ok(())

+ 4 - 4
crates/kuatia-dashboard/src/ui.rs

@@ -13,7 +13,7 @@ use axum::{
     response::{Html, IntoResponse, Response},
     response::{Html, IntoResponse, Response},
     routing::get,
     routing::get,
 };
 };
-use kuatia_core::{Amount, AssetId, Cent};
+use kuatia_core::{AccountId, Amount, AssetId, Cent};
 use serde::Serialize;
 use serde::Serialize;
 use tera::{Context, Tera};
 use tera::{Context, Tera};
 
 
@@ -261,13 +261,13 @@ fn civil_from_days(z: i64) -> (i64, u32, u32) {
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 
 
 /// Display an account id as its IBAN-style code in grouped (spaced) format.
 /// Display an account id as its IBAN-style code in grouped (spaced) format.
-fn acct_display(id: kuatia_core::AccountId) -> String {
+fn acct_display(id: AccountId) -> String {
     id.to_grouped()
     id.to_grouped()
 }
 }
 
 
 /// The detail route path for an account, keyed by the machine-format code:
 /// The detail route path for an account, keyed by the machine-format code:
 /// `/accounts/<code>`.
 /// `/accounts/<code>`.
-fn acct_link(id: kuatia_core::AccountId) -> String {
+fn acct_link(id: AccountId) -> String {
     format!("/accounts/{id}")
     format!("/accounts/{id}")
 }
 }
 
 
@@ -388,7 +388,7 @@ async fn accounts_ctx(state: &AppState) -> Result<Context, ApiError> {
     Ok(ctx)
     Ok(ctx)
 }
 }
 
 
-async fn account_ctx(state: &AppState, id: kuatia_core::AccountId) -> Result<Context, ApiError> {
+async fn account_ctx(state: &AppState, id: AccountId) -> Result<Context, ApiError> {
     let dto = data::account_detail(state, id).await?;
     let dto = data::account_detail(state, id).await?;
     let mut ctx = Context::new();
     let mut ctx = Context::new();
     ctx.insert("nav", "accounts");
     ctx.insert("nav", "accounts");

+ 4 - 3
crates/kuatia-money/src/lib.rs

@@ -13,6 +13,7 @@
 //! never silently round or overflow.
 //! never silently round or overflow.
 
 
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
+use std::error::Error;
 use std::fmt;
 use std::fmt;
 use std::str::FromStr;
 use std::str::FromStr;
 
 
@@ -148,7 +149,7 @@ impl fmt::Display for OverflowError {
     }
     }
 }
 }
 
 
-impl std::error::Error for OverflowError {}
+impl Error for OverflowError {}
 
 
 /// Returned when a string cannot be parsed into a [`Cent`].
 /// Returned when a string cannot be parsed into a [`Cent`].
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -160,7 +161,7 @@ impl fmt::Display for ParseCentError {
     }
     }
 }
 }
 
 
-impl std::error::Error for ParseCentError {}
+impl Error for ParseCentError {}
 
 
 impl Cent {
 impl Cent {
     /// The zero amount.
     /// The zero amount.
@@ -326,7 +327,7 @@ impl fmt::Display for ParseAmountError {
     }
     }
 }
 }
 
 
-impl std::error::Error for ParseAmountError {}
+impl Error for ParseAmountError {}
 
 
 impl Amount {
 impl Amount {
     /// Create an `Amount` formatter with the given number of decimal places.
     /// Create an `Amount` formatter with the given number of decimal places.

+ 9 - 7
crates/kuatia-storage-sql/src/lib.rs

@@ -15,12 +15,14 @@ use std::str::FromStr;
 use std::sync::atomic::{AtomicU8, Ordering};
 use std::sync::atomic::{AtomicU8, Ordering};
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
+use sqlx::any::AnyRow;
 use sqlx::{Any, Pool, Row};
 use sqlx::{Any, Pool, Row};
 
 
 use kuatia_storage::error::StoreError;
 use kuatia_storage::error::StoreError;
-use kuatia_storage::events::{EventStore, LedgerEvent};
+use kuatia_storage::events::{EventStore, LedgerEvent, event_dedup_key};
 use kuatia_storage::query::{filter_transfers, paginate};
 use kuatia_storage::query::{filter_transfers, paginate};
 use kuatia_storage::store::*;
 use kuatia_storage::store::*;
+use kuatia_types::autoid::AutoId;
 use kuatia_types::*;
 use kuatia_types::*;
 
 
 // Cached backend kind for `SqlStore::backend`.
 // Cached backend kind for `SqlStore::backend`.
@@ -36,7 +38,7 @@ const FOR_UPDATE: &str = " FOR UPDATE";
 /// SQL-backed [`Store`] implementation.
 /// SQL-backed [`Store`] implementation.
 pub struct SqlStore {
 pub struct SqlStore {
     pool: Pool<Any>,
     pool: Pool<Any>,
-    autoid: kuatia_types::autoid::AutoId,
+    autoid: AutoId,
     /// Detected backend kind (lazily probed): one of `BACKEND_*`.
     /// Detected backend kind (lazily probed): one of `BACKEND_*`.
     backend: AtomicU8,
     backend: AtomicU8,
 }
 }
@@ -46,7 +48,7 @@ impl SqlStore {
     pub fn new(pool: Pool<Any>) -> Self {
     pub fn new(pool: Pool<Any>) -> Self {
         Self {
         Self {
             pool,
             pool,
-            autoid: kuatia_types::autoid::AutoId::new(),
+            autoid: AutoId::new(),
             backend: AtomicU8::new(BACKEND_UNKNOWN),
             backend: AtomicU8::new(BACKEND_UNKNOWN),
         }
         }
     }
     }
@@ -223,7 +225,7 @@ fn envelope_id_from_hex(s: &str) -> Result<EnvelopeId, StoreError> {
     Ok(EnvelopeId(arr))
     Ok(EnvelopeId(arr))
 }
 }
 
 
-fn row_to_account(row: &sqlx::any::AnyRow) -> Result<Account, StoreError> {
+fn row_to_account(row: &AnyRow) -> Result<Account, StoreError> {
     let id: i64 = row
     let id: i64 = row
         .try_get("id")
         .try_get("id")
         .map_err(|e| StoreError::Internal(e.to_string()))?;
         .map_err(|e| StoreError::Internal(e.to_string()))?;
@@ -252,7 +254,7 @@ fn row_to_account(row: &sqlx::any::AnyRow) -> Result<Account, StoreError> {
     })
     })
 }
 }
 
 
-fn row_to_posting(row: &sqlx::any::AnyRow) -> Result<Posting, StoreError> {
+fn row_to_posting(row: &AnyRow) -> Result<Posting, StoreError> {
     let transfer_id: String = row
     let transfer_id: String = row
         .try_get("transfer_id")
         .try_get("transfer_id")
         .map_err(|e| StoreError::Internal(e.to_string()))?;
         .map_err(|e| StoreError::Internal(e.to_string()))?;
@@ -637,7 +639,7 @@ impl PostingStore for SqlStore {
 
 
         // Key membership by the same `(hex, idx)` values that were bound, so the
         // Key membership by the same `(hex, idx)` values that were bound, so the
         // per-id lookup below matches without decoding transfer ids back.
         // per-id lookup below matches without decoding transfer ids back.
-        let row_key = |row: &sqlx::any::AnyRow| -> Result<(String, i16), StoreError> {
+        let row_key = |row: &AnyRow| -> Result<(String, i16), StoreError> {
             let transfer_id: String = row
             let transfer_id: String = row
                 .try_get("transfer_id")
                 .try_get("transfer_id")
                 .map_err(|e| StoreError::Internal(e.to_string()))?;
                 .map_err(|e| StoreError::Internal(e.to_string()))?;
@@ -1253,7 +1255,7 @@ impl EventStore for SqlStore {
         // Idempotent on the dedup key: a replayed transfer or lifecycle-transition
         // Idempotent on the dedup key: a replayed transfer or lifecycle-transition
         // event conflicts on `dedup_key` and returns the existing seq instead of a
         // event conflicts on `dedup_key` and returns the existing seq instead of a
         // duplicate row.
         // duplicate row.
-        match kuatia_storage::events::event_dedup_key(&event.kind) {
+        match event_dedup_key(&event.kind) {
             Some(dedup_key) => {
             Some(dedup_key) => {
                 let res = sqlx::query("INSERT INTO events (seq, timestamp, kind, data, dedup_key) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (dedup_key) DO NOTHING")
                 let res = sqlx::query("INSERT INTO events (seq, timestamp, kind, data, dedup_key) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (dedup_key) DO NOTHING")
                     .bind(seq as i64)
                     .bind(seq as i64)

+ 6 - 3
crates/kuatia-storage/src/error.rs

@@ -1,5 +1,8 @@
 //! Error types for storage implementations.
 //! Error types for storage implementations.
 
 
+use std::error::Error;
+use std::fmt;
+
 /// Errors produced by [`Store`](crate::store::Store) implementations.
 /// Errors produced by [`Store`](crate::store::Store) implementations.
 ///
 ///
 /// The store is a dumb instruction follower: writes report affected-row counts,
 /// The store is a dumb instruction follower: writes report affected-row counts,
@@ -15,8 +18,8 @@ pub enum StoreError {
     Internal(String),
     Internal(String),
 }
 }
 
 
-impl std::fmt::Display for StoreError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Display for StoreError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
         match self {
             Self::NotFound(msg) => write!(f, "not found: {msg}"),
             Self::NotFound(msg) => write!(f, "not found: {msg}"),
             Self::Internal(msg) => write!(f, "internal error: {msg}"),
             Self::Internal(msg) => write!(f, "internal error: {msg}"),
@@ -24,4 +27,4 @@ impl std::fmt::Display for StoreError {
     }
     }
 }
 }
 
 
-impl std::error::Error for StoreError {}
+impl Error for StoreError {}

+ 5 - 5
crates/kuatia-storage/src/mem_store.rs

@@ -4,6 +4,7 @@
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
 use std::collections::HashMap;
 use std::collections::HashMap;
+use std::collections::hash_map::Entry;
 use tokio::sync::RwLock;
 use tokio::sync::RwLock;
 
 
 use kuatia_types::autoid::AutoId;
 use kuatia_types::autoid::AutoId;
@@ -13,7 +14,7 @@ use kuatia_types::{
 };
 };
 
 
 use crate::error::StoreError;
 use crate::error::StoreError;
-use crate::events::{EventStore, LedgerEvent};
+use crate::events::{EventStore, LedgerEvent, event_dedup_key};
 use crate::query::{filter_transfers, paginate};
 use crate::query::{filter_transfers, paginate};
 use crate::store::{
 use crate::store::{
     AccountStore, BalanceProjection, BalanceProjectionStore, BookStore, EnvelopeRecord, Page,
     AccountStore, BalanceProjection, BalanceProjectionStore, BookStore, EnvelopeRecord, Page,
@@ -339,8 +340,7 @@ impl PostingStore for InMemoryStore {
         let mut store = self.postings.write().await;
         let mut store = self.postings.write().await;
         let mut inserted: u64 = 0;
         let mut inserted: u64 = 0;
         for posting in postings {
         for posting in postings {
-            if let std::collections::hash_map::Entry::Vacant(e) = store.immutable.entry(posting.id)
-            {
+            if let Entry::Vacant(e) = store.immutable.entry(posting.id) {
                 e.insert(posting.clone());
                 e.insert(posting.clone());
                 // Only newly-inserted postings are activated; a since-spent
                 // Only newly-inserted postings are activated; a since-spent
                 // posting is not re-activated on a replayed insert. The active
                 // posting is not re-activated on a replayed insert. The active
@@ -465,10 +465,10 @@ impl EventStore for InMemoryStore {
         let mut events = self.events.write().await;
         let mut events = self.events.write().await;
         // Idempotent on the dedup key: a replayed transfer or lifecycle-transition
         // Idempotent on the dedup key: a replayed transfer or lifecycle-transition
         // event returns the existing seq instead of inserting a duplicate.
         // event returns the existing seq instead of inserting a duplicate.
-        if let Some(key) = crate::events::event_dedup_key(&event.kind)
+        if let Some(key) = event_dedup_key(&event.kind)
             && let Some(existing) = events
             && let Some(existing) = events
                 .iter()
                 .iter()
-                .find(|e| crate::events::event_dedup_key(&e.kind).as_deref() == Some(key.as_str()))
+                .find(|e| event_dedup_key(&e.kind).as_deref() == Some(key.as_str()))
         {
         {
             return Ok(existing.seq);
             return Ok(existing.seq);
         }
         }

+ 6 - 4
crates/kuatia-types/src/account_code.rs

@@ -4,7 +4,7 @@
 //! 18-character base-36 body followed by two ISO 7064 mod-97 check digits, with
 //! 18-character base-36 body followed by two ISO 7064 mod-97 check digits, with
 //! no country code. It is produced and consumed through a narrow interface of
 //! no country code. It is produced and consumed through a narrow interface of
 //! three methods on [`AccountId`]: [`Display`](fmt::Display) (machine form),
 //! three methods on [`AccountId`]: [`Display`](fmt::Display) (machine form),
-//! [`FromStr`](std::str::FromStr) (validating parse), and
+//! [`FromStr`] (validating parse), and
 //! [`to_grouped`](AccountId::to_grouped) (presentation spacing).
 //! [`to_grouped`](AccountId::to_grouped) (presentation spacing).
 //!
 //!
 //! Everything behind that interface is private: bit-packing the `(id, sub)`
 //! Everything behind that interface is private: bit-packing the `(id, sub)`
@@ -16,7 +16,9 @@
 //! encodable range and to key the deployment's codes.
 //! encodable range and to key the deployment's codes.
 
 
 use crate::AccountId;
 use crate::AccountId;
+use std::error::Error;
 use std::fmt;
 use std::fmt;
+use std::str::FromStr;
 use std::sync::atomic::{AtomicU64, Ordering};
 use std::sync::atomic::{AtomicU64, Ordering};
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -225,7 +227,7 @@ impl fmt::Display for AccountId {
     /// country code. The `(id, sub)` pair is packed into a 93-bit value and run
     /// country code. The `(id, sub)` pair is packed into a 93-bit value and run
     /// through a keyed format-preserving permutation (see [`set_id_seed`]) before
     /// through a keyed format-preserving permutation (see [`set_id_seed`]) before
     /// encoding, so the body does not reveal the raw ids. Round-trips via
     /// encoding, so the body does not reveal the raw ids. Round-trips via
-    /// [`FromStr`](std::str::FromStr); [`to_grouped`](AccountId::to_grouped) adds
+    /// [`FromStr`]; [`to_grouped`](AccountId::to_grouped) adds
     /// the presentation spacing (five groups of four).
     /// the presentation spacing (five groups of four).
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let body = base36_body(obfuscate(pack(self.id, self.sub), id_seed()));
         let body = base36_body(obfuscate(pack(self.id, self.sub), id_seed()));
@@ -262,9 +264,9 @@ impl fmt::Display for ParseAccountIdError {
     }
     }
 }
 }
 
 
-impl std::error::Error for ParseAccountIdError {}
+impl Error for ParseAccountIdError {}
 
 
-impl std::str::FromStr for AccountId {
+impl FromStr for AccountId {
     type Err = ParseAccountIdError;
     type Err = ParseAccountIdError;
 
 
     /// Parse an IBAN-style account code back into the two legs. Any spaces
     /// Parse an IBAN-style account code back into the two legs. Any spaces

+ 4 - 3
crates/kuatia-types/src/lib.rs

@@ -13,6 +13,7 @@ pub use account_code::{
     DEFAULT_ID_SEED, ID_BITS, ParseAccountIdError, SUB_BITS, id_seed, set_id_seed,
     DEFAULT_ID_SEED, ID_BITS, ParseAccountIdError, SUB_BITS, id_seed, set_id_seed,
 };
 };
 
 
+use crate::autoid::AutoId;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 use std::collections::BTreeMap;
 use std::collections::BTreeMap;
 use std::fmt;
 use std::fmt;
@@ -139,7 +140,7 @@ impl Default for AccountId {
     fn default() -> Self {
     fn default() -> Self {
         // Process-global generator: a per-thread one could mint the same id on
         // Process-global generator: a per-thread one could mint the same id on
         // two threads within a millisecond, yielding duplicate account ids.
         // two threads within a millisecond, yielding duplicate account ids.
-        static GEN: crate::autoid::AutoId = crate::autoid::AutoId::new();
+        static GEN: AutoId = AutoId::new();
         Self {
         Self {
             id: GEN.next(),
             id: GEN.next(),
             sub: 0,
             sub: 0,
@@ -220,7 +221,7 @@ impl BookId {
     pub fn generate() -> Self {
     pub fn generate() -> Self {
         // Process-global so the "process-unique" contract holds across threads;
         // Process-global so the "process-unique" contract holds across threads;
         // a per-thread generator can repeat an id on another thread.
         // a per-thread generator can repeat an id on another thread.
-        static GEN: crate::autoid::AutoId = crate::autoid::AutoId::new();
+        static GEN: AutoId = AutoId::new();
         Self(GEN.next())
         Self(GEN.next())
     }
     }
 }
 }
@@ -251,7 +252,7 @@ impl Default for ReservationId {
         // generator lets two sagas on different threads mint the same id within
         // generator lets two sagas on different threads mint the same id within
         // a millisecond, which collapses the reservation-ownership check and
         // a millisecond, which collapses the reservation-ownership check and
         // allows a double-spend under concurrency.
         // allows a double-spend under concurrency.
-        static GEN: crate::autoid::AutoId = crate::autoid::AutoId::new();
+        static GEN: AutoId = AutoId::new();
         Self(GEN.next())
         Self(GEN.next())
     }
     }
 }
 }

+ 7 - 4
crates/kuatia/src/error.rs

@@ -3,6 +3,9 @@
 //! [`LedgerError`] unifies errors from the pure core (validation, selection)
 //! [`LedgerError`] unifies errors from the pure core (validation, selection)
 //! and from storage, so callers get a single error type from every API.
 //! and from storage, so callers get a single error type from every API.
 
 
+use std::error::Error;
+use std::fmt;
+
 use kuatia_core::{
 use kuatia_core::{
     AccountId, AssetId, BookId, EnvelopeId, InsufficientFunds, OverflowError, PostingId,
     AccountId, AssetId, BookId, EnvelopeId, InsufficientFunds, OverflowError, PostingId,
     ResolveError, ValidationError,
     ResolveError, ValidationError,
@@ -78,8 +81,8 @@ pub enum LedgerError {
     },
     },
 }
 }
 
 
-impl std::fmt::Display for LedgerError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Display for LedgerError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
         match self {
             Self::Validation(e) => write!(f, "validation: {e}"),
             Self::Validation(e) => write!(f, "validation: {e}"),
             Self::Store(e) => write!(f, "store: {e}"),
             Self::Store(e) => write!(f, "store: {e}"),
@@ -122,8 +125,8 @@ impl std::fmt::Display for LedgerError {
     }
     }
 }
 }
 
 
-impl std::error::Error for LedgerError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+impl Error for LedgerError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
         match self {
         match self {
             Self::Validation(e) => Some(e),
             Self::Validation(e) => Some(e),
             Self::Store(e) => Some(e),
             Self::Store(e) => Some(e),

+ 2 - 2
crates/kuatia/src/inflight.rs

@@ -17,7 +17,7 @@ use std::sync::Arc;
 
 
 use kuatia_core::{
 use kuatia_core::{
     Account, AccountFlags, AccountId, AssetId, BookId, Cent, EnvelopeId, InsufficientFunds,
     Account, AccountFlags, AccountId, AssetId, BookId, Cent, EnvelopeId, InsufficientFunds,
-    Metadata, Receipt, Transfer, TransferBuilder, hash::double_sha256,
+    Metadata, Receipt, SUB_BITS, Transfer, TransferBuilder, hash::double_sha256,
 };
 };
 use kuatia_storage::error::StoreError;
 use kuatia_storage::error::StoreError;
 use kuatia_storage::store::EnvelopeRecord;
 use kuatia_storage::store::EnvelopeRecord;
@@ -565,7 +565,7 @@ fn inflight_subaccount(transfer: &Transfer) -> i64 {
     first.copy_from_slice(&hash[..8]);
     first.copy_from_slice(&hash[..8]);
     // Keep only the low SUB_BITS so the hold's subaccount id fits the account
     // Keep only the low SUB_BITS so the hold's subaccount id fits the account
     // code's encodable range (ADR-0015). The result is always positive.
     // code's encodable range (ADR-0015). The result is always positive.
-    let mask = (1u64 << kuatia_types::SUB_BITS) - 1;
+    let mask = (1u64 << SUB_BITS) - 1;
     (u64::from_be_bytes(first) & mask) as i64
     (u64::from_be_bytes(first) & mask) as i64
 }
 }
 
 

+ 17 - 20
crates/kuatia/src/ledger/commit.rs

@@ -7,16 +7,17 @@
 //! crash.
 //! crash.
 
 
 use std::collections::HashMap;
 use std::collections::HashMap;
+use std::collections::hash_map::Entry;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
 use legend::ExecutionResult;
 use legend::ExecutionResult;
 use tracing::instrument;
 use tracing::instrument;
 
 
 use kuatia_core::{
 use kuatia_core::{
-    AccountId, AccountSnapshotId, AssetId, Book, Cent, DEFAULT_BOOK, Envelope, EnvelopeBuilder,
-    EnvelopeId, NewPosting, PlanInput, Posting, PostingFilter, PostingId, PostingState, Receipt,
-    ResolveInput, Transfer, account_snapshot_id, draft_movements, envelope_id, resolve_envelope,
-    validate_and_plan,
+    Account, AccountId, AccountSnapshotId, AssetId, Book, Cent, DEFAULT_BOOK, Envelope,
+    EnvelopeBuilder, EnvelopeId, NewPosting, Plan, PlanInput, Posting, PostingFilter, PostingId,
+    PostingState, Receipt, ReservationId, ResolveInput, Transfer, account_snapshot_id,
+    draft_movements, envelope_id, resolve_envelope, validate_and_plan,
 };
 };
 
 
 use kuatia_storage::error::StoreError;
 use kuatia_storage::error::StoreError;
@@ -47,7 +48,7 @@ enum SagaPhase {
 #[derive(serde::Serialize, serde::Deserialize)]
 #[derive(serde::Serialize, serde::Deserialize)]
 struct PendingSaga {
 struct PendingSaga {
     envelope: Envelope,
     envelope: Envelope,
-    reservation: kuatia_core::ReservationId,
+    reservation: ReservationId,
     phase: SagaPhase,
     phase: SagaPhase,
 }
 }
 
 
@@ -59,7 +60,7 @@ struct PendingSaga {
 #[derive(serde::Serialize, serde::Deserialize)]
 #[derive(serde::Serialize, serde::Deserialize)]
 pub(super) struct PendingTransition {
 pub(super) struct PendingTransition {
     /// The next account version to append: version already bumped, flag flipped.
     /// The next account version to append: version already bumped, flag flipped.
-    pub next: kuatia_core::Account,
+    pub next: Account,
     /// The lifecycle event paired with this version bump. It carries the target
     /// The lifecycle event paired with this version bump. It carries the target
     /// version, so re-appending it on recovery dedups to the original.
     /// version, so re-appending it on recovery dedups to the original.
     pub event: LedgerEventKind,
     pub event: LedgerEventKind,
@@ -81,7 +82,7 @@ struct LoadedState {
     /// Postings being consumed by the envelope.
     /// Postings being consumed by the envelope.
     consumed_postings: Vec<Posting>,
     consumed_postings: Vec<Posting>,
     /// Accounts referenced by the envelope.
     /// Accounts referenced by the envelope.
-    accounts: HashMap<AccountId, kuatia_core::Account>,
+    accounts: HashMap<AccountId, Account>,
     /// Current balances for all referenced (account, asset) pairs.
     /// Current balances for all referenced (account, asset) pairs.
     balances: HashMap<(AccountId, AssetId), Cent>,
     balances: HashMap<(AccountId, AssetId), Cent>,
     /// The book gating this transfer, if one is loaded (`None` = unrestricted default).
     /// The book gating this transfer, if one is loaded (`None` = unrestricted default).
@@ -147,11 +148,7 @@ impl Ledger {
     }
     }
 
 
     /// Run pure validation over the loaded state and produce a plan.
     /// Run pure validation over the loaded state and produce a plan.
-    fn plan(
-        &self,
-        envelope: &Envelope,
-        loaded: &LoadedState,
-    ) -> Result<kuatia_core::Plan, LedgerError> {
+    fn plan(&self, envelope: &Envelope, loaded: &LoadedState) -> Result<Plan, LedgerError> {
         let input = PlanInput {
         let input = PlanInput {
             envelope,
             envelope,
             consumed_postings: &loaded.consumed_postings,
             consumed_postings: &loaded.consumed_postings,
@@ -185,7 +182,7 @@ impl Ledger {
         // to zero on the system account, so it produces no debit and loads nothing
         // to zero on the system account, so it produces no debit and loads nothing
         // here.
         // here.
         let mut available: HashMap<(AccountId, AssetId), Vec<Posting>> = HashMap::new();
         let mut available: HashMap<(AccountId, AssetId), Vec<Posting>> = HashMap::new();
-        let mut accounts: HashMap<AccountId, kuatia_core::Account> = HashMap::new();
+        let mut accounts: HashMap<AccountId, Account> = HashMap::new();
         for debit in &draft.debits {
         for debit in &draft.debits {
             let postings = self
             let postings = self
                 .store
                 .store
@@ -197,7 +194,7 @@ impl Ledger {
                 )
                 )
                 .await?;
                 .await?;
             available.insert((debit.account, debit.asset), postings);
             available.insert((debit.account, debit.asset), postings);
-            if let std::collections::hash_map::Entry::Vacant(e) = accounts.entry(debit.account) {
+            if let Entry::Vacant(e) = accounts.entry(debit.account) {
                 e.insert(self.store.get_account(&debit.account).await?);
                 e.insert(self.store.get_account(&debit.account).await?);
             }
             }
         }
         }
@@ -257,7 +254,7 @@ impl Ledger {
 
 
         // Write-ahead: persist {envelope, reservation, phase=Reserving} before any
         // Write-ahead: persist {envelope, reservation, phase=Reserving} before any
         // mutation. The finalize step bumps the phase to Finalizing.
         // mutation. The finalize step bumps the phase to Finalizing.
-        let reservation = kuatia_core::ReservationId::default();
+        let reservation = ReservationId::default();
         let saga_id = reservation.0;
         let saga_id = reservation.0;
         self.save_pending(&envelope, reservation, SagaPhase::Reserving)
         self.save_pending(&envelope, reservation, SagaPhase::Reserving)
             .await?;
             .await?;
@@ -286,7 +283,7 @@ impl Ledger {
     async fn drive_envelope_saga(
     async fn drive_envelope_saga(
         self: &Arc<Self>,
         self: &Arc<Self>,
         envelope: Envelope,
         envelope: Envelope,
-        reservation: kuatia_core::ReservationId,
+        reservation: ReservationId,
     ) -> Result<Receipt, LedgerError> {
     ) -> Result<Receipt, LedgerError> {
         let saga = EnvelopeSaga::new(EnvelopeSagaInputs {
         let saga = EnvelopeSaga::new(EnvelopeSagaInputs {
             reserve: ReserveInput,
             reserve: ReserveInput,
@@ -404,7 +401,7 @@ impl Ledger {
     pub(crate) async fn finalize_envelope(
     pub(crate) async fn finalize_envelope(
         &self,
         &self,
         envelope: &Envelope,
         envelope: &Envelope,
-        reservation: kuatia_core::ReservationId,
+        reservation: ReservationId,
     ) -> Result<Receipt, LedgerError> {
     ) -> Result<Receipt, LedgerError> {
         let tid = envelope_id(envelope);
         let tid = envelope_id(envelope);
         if let Some(record) = self.store.get_transfer(&tid).await? {
         if let Some(record) = self.store.get_transfer(&tid).await? {
@@ -538,7 +535,7 @@ impl Ledger {
     async fn save_pending(
     async fn save_pending(
         &self,
         &self,
         envelope: &Envelope,
         envelope: &Envelope,
-        reservation: kuatia_core::ReservationId,
+        reservation: ReservationId,
         phase: SagaPhase,
         phase: SagaPhase,
     ) -> Result<(), LedgerError> {
     ) -> Result<(), LedgerError> {
         let blob = serde_json::to_vec(&PendingRecord::Envelope(PendingSaga {
         let blob = serde_json::to_vec(&PendingRecord::Envelope(PendingSaga {
@@ -557,10 +554,10 @@ impl Ledger {
     /// key never collides with an in-flight commit saga's key.
     /// key never collides with an in-flight commit saga's key.
     pub(super) async fn save_transition(
     pub(super) async fn save_transition(
         &self,
         &self,
-        next: &kuatia_core::Account,
+        next: &Account,
         event: &LedgerEventKind,
         event: &LedgerEventKind,
     ) -> Result<i64, LedgerError> {
     ) -> Result<i64, LedgerError> {
-        let saga_id = kuatia_core::ReservationId::default().0;
+        let saga_id = ReservationId::default().0;
         let blob = serde_json::to_vec(&PendingRecord::Transition(PendingTransition {
         let blob = serde_json::to_vec(&PendingRecord::Transition(PendingTransition {
             next: next.clone(),
             next: next.clone(),
             event: event.clone(),
             event: event.clone(),

+ 2 - 2
crates/kuatia/src/ledger/lifecycle.rs

@@ -11,7 +11,7 @@
 
 
 use tracing::instrument;
 use tracing::instrument;
 
 
-use kuatia_core::{AccountFlags, AccountId, PostingFilter};
+use kuatia_core::{Account, AccountFlags, AccountId, PostingFilter};
 use kuatia_storage::events::{LedgerEvent, LedgerEventKind};
 use kuatia_storage::events::{LedgerEvent, LedgerEventKind};
 
 
 use super::{Ledger, now_millis};
 use super::{Ledger, now_millis};
@@ -19,7 +19,7 @@ use crate::error::LedgerError;
 
 
 impl Ledger {
 impl Ledger {
     /// Create a new account and emit an AccountCreated event.
     /// Create a new account and emit an AccountCreated event.
-    pub async fn create_account(&self, account: kuatia_core::Account) -> Result<(), LedgerError> {
+    pub async fn create_account(&self, account: Account) -> Result<(), LedgerError> {
         let id = account.id;
         let id = account.id;
         if self.store.create_account(account).await? == 0 {
         if self.store.create_account(account).await? == 0 {
             return Err(LedgerError::AccountAlreadyExists(id));
             return Err(LedgerError::AccountAlreadyExists(id));

+ 2 - 2
crates/kuatia/src/ledger/projection.rs

@@ -15,7 +15,7 @@ use std::sync::Arc;
 use tracing::instrument;
 use tracing::instrument;
 
 
 use kuatia_core::{AccountId, AssetId, Cent, PostingId};
 use kuatia_core::{AccountId, AssetId, Cent, PostingId};
-use kuatia_storage::store::{Store, TransferQuery};
+use kuatia_storage::store::{EnvelopeRecord, Store, TransferQuery};
 
 
 use super::{Ledger, now_millis};
 use super::{Ledger, now_millis};
 use crate::error::LedgerError;
 use crate::error::LedgerError;
@@ -29,7 +29,7 @@ async fn fold_account_delta(
     store: &dyn Store,
     store: &dyn Store,
     account: &AccountId,
     account: &AccountId,
     asset: &AssetId,
     asset: &AssetId,
-    records: &[kuatia_storage::store::EnvelopeRecord],
+    records: &[EnvelopeRecord],
 ) -> Result<(Cent, u64), LedgerError> {
 ) -> Result<(Cent, u64), LedgerError> {
     let mut delta = Cent::ZERO;
     let mut delta = Cent::ZERO;
     let mut count: u64 = 0;
     let mut count: u64 = 0;

+ 16 - 28
crates/kuatia/src/ledger/query.rs

@@ -5,20 +5,23 @@
 //! underlying store is reachable via [`Ledger::store`] for callers that want the
 //! underlying store is reachable via [`Ledger::store`] for callers that want the
 //! raw storage error instead.
 //! raw storage error instead.
 
 
-use kuatia_core::{AccountId, PostingFilter, PostingId, PostingState};
+use kuatia_core::{
+    Account, AccountId, Book, BookId, Posting, PostingFilter, PostingId, PostingState,
+};
 use kuatia_storage::events::LedgerEvent;
 use kuatia_storage::events::LedgerEvent;
 
 
 use super::Ledger;
 use super::Ledger;
 use crate::error::LedgerError;
 use crate::error::LedgerError;
+use crate::store::{EnvelopeRecord, Page, PostingQuery, TransferQuery};
 
 
 impl Ledger {
 impl Ledger {
     /// List all accounts (latest version of each).
     /// List all accounts (latest version of each).
-    pub async fn list_accounts(&self) -> Result<Vec<kuatia_core::Account>, LedgerError> {
+    pub async fn list_accounts(&self) -> Result<Vec<Account>, LedgerError> {
         Ok(self.store.list_accounts().await?)
         Ok(self.store.list_accounts().await?)
     }
     }
 
 
     /// Fetch a single account by id.
     /// Fetch a single account by id.
-    pub async fn get_account(&self, id: &AccountId) -> Result<kuatia_core::Account, LedgerError> {
+    pub async fn get_account(&self, id: &AccountId) -> Result<Account, LedgerError> {
         self.store
         self.store
             .get_account(id)
             .get_account(id)
             .await
             .await
@@ -26,10 +29,7 @@ impl Ledger {
     }
     }
 
 
     /// Return all transfers involving the given account (exact subaccount).
     /// Return all transfers involving the given account (exact subaccount).
-    pub async fn history(
-        &self,
-        account: &AccountId,
-    ) -> Result<Vec<crate::store::EnvelopeRecord>, LedgerError> {
+    pub async fn history(&self, account: &AccountId) -> Result<Vec<EnvelopeRecord>, LedgerError> {
         Ok(self
         Ok(self
             .store
             .store
             .get_transfers_for_account(account.id, Some(account.sub))
             .get_transfers_for_account(account.id, Some(account.sub))
@@ -39,16 +39,13 @@ impl Ledger {
     /// Query transfers with filtering and pagination.
     /// Query transfers with filtering and pagination.
     pub async fn query_transfers(
     pub async fn query_transfers(
         &self,
         &self,
-        query: &crate::store::TransferQuery,
-    ) -> Result<crate::store::Page<crate::store::EnvelopeRecord>, LedgerError> {
+        query: &TransferQuery,
+    ) -> Result<Page<EnvelopeRecord>, LedgerError> {
         Ok(self.store.query_transfers(query).await?)
         Ok(self.store.query_transfers(query).await?)
     }
     }
 
 
     /// Return all postings (any state) for the given account.
     /// Return all postings (any state) for the given account.
-    pub async fn postings(
-        &self,
-        account: &AccountId,
-    ) -> Result<Vec<kuatia_core::Posting>, LedgerError> {
+    pub async fn postings(&self, account: &AccountId) -> Result<Vec<Posting>, LedgerError> {
         Ok(self
         Ok(self
             .store
             .store
             .get_postings_by_account(account.id, Some(account.sub), None, PostingFilter::All)
             .get_postings_by_account(account.id, Some(account.sub), None, PostingFilter::All)
@@ -60,7 +57,7 @@ impl Ledger {
     pub async fn postings_with_state(
     pub async fn postings_with_state(
         &self,
         &self,
         account: &AccountId,
         account: &AccountId,
-    ) -> Result<Vec<(kuatia_core::Posting, PostingState)>, LedgerError> {
+    ) -> Result<Vec<(Posting, PostingState)>, LedgerError> {
         let postings = self.postings(account).await?;
         let postings = self.postings(account).await?;
         let ids: Vec<PostingId> = postings.iter().map(|p| p.id).collect();
         let ids: Vec<PostingId> = postings.iter().map(|p| p.id).collect();
         let states = self.store.get_posting_states(&ids).await?;
         let states = self.store.get_posting_states(&ids).await?;
@@ -68,23 +65,17 @@ impl Ledger {
     }
     }
 
 
     /// Query postings with filtering and pagination.
     /// Query postings with filtering and pagination.
-    pub async fn query_postings(
-        &self,
-        query: &crate::store::PostingQuery,
-    ) -> Result<crate::store::Page<kuatia_core::Posting>, LedgerError> {
+    pub async fn query_postings(&self, query: &PostingQuery) -> Result<Page<Posting>, LedgerError> {
         Ok(self.store.query_postings(query).await?)
         Ok(self.store.query_postings(query).await?)
     }
     }
 
 
     /// Return the full version history for an account.
     /// Return the full version history for an account.
-    pub async fn account_history(
-        &self,
-        id: &AccountId,
-    ) -> Result<Vec<kuatia_core::Account>, LedgerError> {
+    pub async fn account_history(&self, id: &AccountId) -> Result<Vec<Account>, LedgerError> {
         Ok(self.store.get_account_history(id).await?)
         Ok(self.store.get_account_history(id).await?)
     }
     }
 
 
     /// Create a new book.
     /// Create a new book.
-    pub async fn create_book(&self, book: kuatia_core::Book) -> Result<(), LedgerError> {
+    pub async fn create_book(&self, book: Book) -> Result<(), LedgerError> {
         let id = book.id;
         let id = book.id;
         if self.store.create_book(book).await? == 0 {
         if self.store.create_book(book).await? == 0 {
             return Err(LedgerError::BookAlreadyExists(id));
             return Err(LedgerError::BookAlreadyExists(id));
@@ -93,15 +84,12 @@ impl Ledger {
     }
     }
 
 
     /// Fetch a book by id.
     /// Fetch a book by id.
-    pub async fn get_book(
-        &self,
-        id: &kuatia_core::BookId,
-    ) -> Result<kuatia_core::Book, LedgerError> {
+    pub async fn get_book(&self, id: &BookId) -> Result<Book, LedgerError> {
         Ok(self.store.get_book(id).await?)
         Ok(self.store.get_book(id).await?)
     }
     }
 
 
     /// List all books.
     /// List all books.
-    pub async fn list_books(&self) -> Result<Vec<kuatia_core::Book>, LedgerError> {
+    pub async fn list_books(&self) -> Result<Vec<Book>, LedgerError> {
         Ok(self.store.list_books().await?)
         Ok(self.store.list_books().await?)
     }
     }
 
 

+ 5 - 3
crates/kuatia/src/saga.rs

@@ -23,6 +23,8 @@
 //! High-level steps (`PayMovementStep` and `DepositMovementStep`) compose over
 //! High-level steps (`PayMovementStep` and `DepositMovementStep`) compose over
 //! the intent-layer API and can be combined into multi-transfer sagas via `legend!`.
 //! the intent-layer API and can be combined into multi-transfer sagas via `legend!`.
 
 
+use std::fmt;
+use std::future::Future;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
@@ -65,7 +67,7 @@ pub(crate) async fn apply_and_verify<F, Fut>(
 ) -> Result<(), LedgerError>
 ) -> Result<(), LedgerError>
 where
 where
     F: FnOnce() -> Fut,
     F: FnOnce() -> Fut,
-    Fut: std::future::Future<Output = Result<bool, LedgerError>>,
+    Fut: Future<Output = Result<bool, LedgerError>>,
 {
 {
     if count == target as u64 {
     if count == target as u64 {
         return Ok(());
         return Ok(());
@@ -121,8 +123,8 @@ pub struct LedgerCtx {
     ledger: Option<Arc<Ledger>>,
     ledger: Option<Arc<Ledger>>,
 }
 }
 
 
-impl std::fmt::Debug for LedgerCtx {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Debug for LedgerCtx {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("LedgerCtx")
         f.debug_struct("LedgerCtx")
             .field("receipts", &self.receipts)
             .field("receipts", &self.receipts)
             .field("reserved_postings", &self.reserved_postings.len())
             .field("reserved_postings", &self.reserved_postings.len())