#![forbid(unsafe_code)]
#![warn(missing_docs)]
mod cursor;
mod event;
mod kind;
mod sync;
mod traits;
mod value;
pub use cursor::{Cursor, EmittedEvent};
pub use event::WalletEvent;
pub use kind::EventKind;
pub use sync::{SyncLifecycle, SyncStatus};
pub use traits::{filter_events, CatchUp, EventEmitter};
pub use value::{Amount, AssetId, WalletId};
pub use enumset::{enum_set, EnumSet};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cursor_advances_monotonically() {
assert_eq!(Cursor(0).next(), Cursor(1));
assert!(Cursor(1) > Cursor(0));
}
#[test]
fn event_is_tagged_snake_case() {
let e = WalletEvent::Confirmation {
tx_id: "ab".into(),
height: 100,
};
let json = serde_json::to_string(&e).unwrap();
assert!(json.contains("\"type\":\"confirmation\""), "{json}");
let back: WalletEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn kind_maps_each_variant() {
let e = WalletEvent::FundsReceived {
wallet_id: WalletId(1),
asset: None,
amount: Amount(5),
coin_id: "c".into(),
confirmed_height: 10,
};
assert_eq!(e.kind(), EventKind::FundsReceived);
}
#[test]
fn filter_admits_only_matching_kinds() {
let received = WalletEvent::FundsReceived {
wallet_id: WalletId(1),
asset: None,
amount: Amount(5),
coin_id: "c".into(),
confirmed_height: 10,
};
let tip = WalletEvent::NewTip {
height: 9,
header_hash: "hh".into(),
};
let funds_only = EventKind::FundsReceived | EventKind::FundsSent;
assert!(received.matches(funds_only));
assert!(!tip.matches(funds_only));
}
#[test]
fn filter_events_retains_only_matching_in_order() {
let events = vec![
EmittedEvent {
cursor: Cursor(1),
event: WalletEvent::NewTip {
height: 1,
header_hash: "a".into(),
},
},
EmittedEvent {
cursor: Cursor(2),
event: WalletEvent::FundsReceived {
wallet_id: WalletId(1),
asset: None,
amount: Amount(5),
coin_id: "c".into(),
confirmed_height: 10,
},
},
];
let kept = filter_events(events, EventKind::FundsReceived.into());
assert_eq!(kept.len(), 1);
assert_eq!(kept[0].cursor, Cursor(2));
}
#[test]
fn amount_exposes_mojos() {
assert_eq!(Amount(42).mojos(), 42);
}
#[test]
fn newtypes_display() {
assert_eq!(WalletId(3).to_string(), "3");
assert_eq!(Amount(9).to_string(), "9");
assert_eq!(AssetId("tail".into()).to_string(), "tail");
}
#[test]
fn sync_status_round_trips() {
let s = SyncStatus {
state: SyncLifecycle::Synced,
peak_height: 100,
target_height: 100,
};
let json = serde_json::to_string(&s).unwrap();
assert!(json.contains("\"state\":\"synced\""), "{json}");
let back: SyncStatus = serde_json::from_str(&json).unwrap();
assert_eq!(back, s);
}
}