tb_rs/
lib.rs

1//! Native Rust client for TigerBeetle.
2//!
3//! This crate provides a high-performance client for [TigerBeetle](https://tigerbeetle.com),
4//! the financial transactions database.
5//!
6//! # Compatibility
7//!
8//! **This client is compatible with TigerBeetle 0.16.x.**
9//!
10//! TigerBeetle requires exact client-server protocol compatibility. This crate's version
11//! follows the format `TB_VERSION+CRATE_VERSION` (e.g., `0.16.0+0.1.0`), where:
12//! - The main version (`0.16.0`) indicates TigerBeetle server compatibility
13//! - The build metadata (`+0.1.0`) indicates the library version
14//!
15//! # Features
16//!
17//! - **High-performance**: Uses io_uring for efficient async I/O on Linux
18//! - **Type-safe**: Strong typing for accounts, transfers, and results
19//! - **Simple API**: One `Client` type with a clean builder pattern
20//!
21//! # Requirements
22//!
23//! - Linux (kernel 5.6+) with io_uring support
24//!
25//! # Quick Start
26//!
27//! ```ignore
28//! use tb_rs::{Client, Account, AccountFlags};
29//!
30//! // Run inside tokio_uring runtime
31//! tokio_uring::start(async {
32//!     // Connect to cluster
33//!     let mut client = Client::connect(0, "127.0.0.1:3000").await?;
34//!
35//!     // Create an account
36//!     let account = Account {
37//!         id: tb_rs::id(),
38//!         ledger: 1,
39//!         code: 1,
40//!         ..Default::default()
41//!     };
42//!     let errors = client.create_accounts(&[account]).await?;
43//!     assert!(errors.is_empty(), "Account creation failed");
44//!
45//!     // Lookup the account
46//!     let accounts = client.lookup_accounts(&[account.id]).await?;
47//!     println!("Found {} accounts", accounts.len());
48//!
49//!     client.close().await;
50//!     Ok::<_, tb_rs::ClientError>(())
51//! });
52//! ```
53//!
54//! # Configuration
55//!
56//! Use the builder pattern for custom configuration:
57//!
58//! ```ignore
59//! use std::time::Duration;
60//! use tb_rs::Client;
61//!
62//! let client = Client::builder()
63//!     .cluster(0)
64//!     .addresses("127.0.0.1:3000,127.0.0.1:3001")?
65//!     .connect_timeout(Duration::from_secs(10))
66//!     .request_timeout(Duration::from_millis(100))
67//!     .build()
68//!     .await?;
69//! ```
70
71#![deny(unsafe_op_in_unsafe_fn)]
72#![warn(missing_docs)]
73
74// This crate requires Linux with io_uring support
75#[cfg(not(target_os = "linux"))]
76compile_error!("tb-rs requires Linux with io_uring support (kernel 5.6+). This crate does not support other platforms.");
77
78// Public modules
79mod client;
80mod error;
81pub mod protocol;
82
83// Internal implementation (not public)
84mod internal;
85
86// Re-export main types
87pub use client::{Client, ClientBuilder};
88pub use error::{ClientError, ProtocolError, Result};
89
90/// TigerBeetle server version this client is compatible with.
91///
92/// This client will only work correctly with TigerBeetle servers running this version
93/// or compatible versions in the same minor release series.
94pub const TIGERBEETLE_VERSION: &str = "0.16.0";
95
96/// Library version (independent of TigerBeetle version).
97pub const CRATE_VERSION: &str = "0.1.0";
98
99// Re-export protocol types
100pub use protocol::{
101    Account, AccountBalance, AccountFilter, AccountFilterFlags, AccountFlags, CreateAccountResult,
102    CreateAccountsResult, CreateTransferResult, CreateTransfersResult, QueryFilter,
103    QueryFilterFlags, Transfer, TransferFlags,
104};
105
106/// Generate a unique TigerBeetle ID.
107///
108/// Creates a globally unique identifier using timestamp and random data,
109/// suitable for account or transfer IDs.
110///
111/// # Example
112///
113/// ```
114/// let account_id = tb_rs::id();
115/// let transfer_id = tb_rs::id();
116/// assert_ne!(account_id, transfer_id);
117/// ```
118pub fn id() -> u128 {
119    use std::time::{SystemTime, UNIX_EPOCH};
120
121    let timestamp = SystemTime::now()
122        .duration_since(UNIX_EPOCH)
123        .unwrap()
124        .as_nanos() as u64;
125
126    let random: u64 = rand::random();
127
128    ((timestamp as u128) << 64) | (random as u128)
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_id_uniqueness() {
137        let ids: Vec<u128> = (0..1000).map(|_| id()).collect();
138
139        for (i, a) in ids.iter().enumerate() {
140            assert_ne!(*a, 0);
141            for b in &ids[..i] {
142                assert_ne!(a, b);
143            }
144        }
145    }
146
147    #[test]
148    fn test_id_temporal_ordering() {
149        let id1 = id();
150        std::thread::sleep(std::time::Duration::from_millis(1));
151        let id2 = id();
152
153        let ts1 = id1 >> 64;
154        let ts2 = id2 >> 64;
155        assert!(ts2 >= ts1);
156    }
157}