From b6a2c85a35095a73c6c541af4676d10b6d9c038b Mon Sep 17 00:00:00 2001 From: Joaquin Bejar Date: Wed, 25 Feb 2026 12:01:00 +0100 Subject: [PATCH] #22: enforce must_use and repr(u8) policy on core APIs --- src/execution/list.rs | 4 ++++ src/execution/match_result.rs | 1 + src/execution/trade.rs | 3 +++ src/orders/base.rs | 3 +++ src/orders/order_type.rs | 16 ++++++++++++++++ src/orders/pegged.rs | 1 + src/orders/status.rs | 3 +++ src/orders/time_in_force.rs | 4 ++++ src/price_level/entry.rs | 4 ++++ src/price_level/level.rs | 8 ++++++++ src/price_level/order_queue.rs | 8 ++++++++ src/price_level/snapshot.rs | 1 + src/price_level/statistics.rs | 9 +++++++++ 13 files changed, 65 insertions(+) diff --git a/src/execution/list.rs b/src/execution/list.rs index de360a4..53aefd8 100644 --- a/src/execution/list.rs +++ b/src/execution/list.rs @@ -13,11 +13,13 @@ pub struct TradeList { impl TradeList { /// Create a new empty trade list + #[must_use] pub fn new() -> Self { Self { trades: Vec::new() } } /// Create a trade list from an existing vector + #[must_use] pub fn from_vec(trades: Vec) -> Self { Self { trades } } @@ -28,11 +30,13 @@ impl TradeList { } /// Get a reference to the underlying vector + #[must_use] pub fn as_vec(&self) -> &Vec { &self.trades } /// Convert into a vector of trades + #[must_use] pub fn into_vec(self) -> Vec { self.trades } diff --git a/src/execution/match_result.rs b/src/execution/match_result.rs index 9b5246d..885dd30 100644 --- a/src/execution/match_result.rs +++ b/src/execution/match_result.rs @@ -27,6 +27,7 @@ pub struct MatchResult { impl MatchResult { /// Create a new empty match result + #[must_use] pub fn new(order_id: Id, initial_quantity: u64) -> Self { Self { order_id, diff --git a/src/execution/trade.rs b/src/execution/trade.rs index db0fb8e..bd7124b 100644 --- a/src/execution/trade.rs +++ b/src/execution/trade.rs @@ -33,6 +33,7 @@ pub struct Trade { impl Trade { /// Create a new trade + #[must_use] pub fn new( trade_id: Id, taker_order_id: Id, @@ -58,6 +59,7 @@ impl Trade { } /// Returns the side of the maker order + #[must_use] pub fn maker_side(&self) -> Side { match self.taker_side { Side::Buy => Side::Sell, @@ -66,6 +68,7 @@ impl Trade { } /// Returns the total value of this trade + #[must_use] pub fn total_value(&self) -> u128 { self.price.as_u128() * (self.quantity.as_u64() as u128) } diff --git a/src/orders/base.rs b/src/orders/base.rs index f187c2a..d68c496 100644 --- a/src/orders/base.rs +++ b/src/orders/base.rs @@ -6,6 +6,7 @@ use std::fmt; use std::str::FromStr; /// Represents the side of an order +#[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Side { /// Buy side (bids) @@ -33,6 +34,7 @@ impl Side { /// let buy_side = sell_side.opposite(); /// assert_eq!(buy_side, Side::Buy); /// ``` + #[must_use] pub fn opposite(&self) -> Self { match self { Side::Buy => Side::Sell, @@ -91,6 +93,7 @@ impl Hash32 { } /// Returns the inner byte array as a mutable reference. + #[must_use] pub fn as_bytes_mut(&mut self) -> &mut [u8; 32] { &mut self.0 } diff --git a/src/orders/order_type.rs b/src/orders/order_type.rs index 8acebf6..59d8b75 100644 --- a/src/orders/order_type.rs +++ b/src/orders/order_type.rs @@ -188,6 +188,7 @@ pub enum OrderType { impl OrderType { /// Get the order ID + #[must_use] pub fn id(&self) -> Id { match self { Self::Standard { id, .. } => *id, @@ -201,6 +202,7 @@ impl OrderType { } /// Get the user ID associated with this order + #[must_use] pub fn user_id(&self) -> Hash32 { match self { Self::Standard { user_id, .. } @@ -214,6 +216,7 @@ impl OrderType { } /// Get the price + #[must_use] pub fn price(&self) -> Price { match self { Self::Standard { price, .. } => *price, @@ -227,6 +230,7 @@ impl OrderType { } /// Get the visible quantity + #[must_use] pub fn visible_quantity(&self) -> u64 { match self { Self::Standard { quantity, .. } => quantity.as_u64(), @@ -244,6 +248,7 @@ impl OrderType { } /// Get the hidden quantity + #[must_use] pub fn hidden_quantity(&self) -> u64 { match self { Self::IcebergOrder { @@ -257,6 +262,7 @@ impl OrderType { } /// Get the order side + #[must_use] pub fn side(&self) -> Side { match self { Self::Standard { side, .. } => *side, @@ -270,6 +276,7 @@ impl OrderType { } /// Get the time in force + #[must_use] pub fn time_in_force(&self) -> TimeInForce { match self { Self::Standard { time_in_force, .. } => *time_in_force, @@ -283,6 +290,7 @@ impl OrderType { } /// Get the timestamp + #[must_use] pub fn timestamp(&self) -> u64 { match self { Self::Standard { timestamp, .. } => timestamp.as_u64(), @@ -296,21 +304,25 @@ impl OrderType { } /// Check if the order is immediate-or-cancel + #[must_use] pub fn is_immediate(&self) -> bool { self.time_in_force().is_immediate() } /// Check if the order is fill-or-kill + #[must_use] pub fn is_fill_or_kill(&self) -> bool { matches!(self.time_in_force(), TimeInForce::Fok) } /// Check if this is a post-only order + #[must_use] pub fn is_post_only(&self) -> bool { matches!(self, Self::PostOnly { .. }) } /// Create a new standard order with reduced quantity + #[must_use] pub fn with_reduced_quantity(&self, new_quantity: u64) -> Self { let new_quantity = Quantity::new(new_quantity); match self { @@ -382,6 +394,7 @@ impl OrderType { } /// Update an iceberg order, refreshing visible part from hidden + #[must_use] pub fn refresh_iceberg(&self, refresh_amount: u64) -> (Self, u64) { match self { Self::IcebergOrder { @@ -461,6 +474,7 @@ impl OrderType { /// - Optionally, an updated version of this order (if partially filled) /// - The quantity that was reduced from hidden portion (for iceberg/reserve orders) /// - The remaining quantity of the incoming order + #[must_use] pub fn match_against(&self, incoming_quantity: u64) -> (u64, Option, u64, u64) { match self { Self::Standard { @@ -712,6 +726,7 @@ impl OrderType { impl OrderType { /// Get the extra fields + #[must_use] pub fn extra_fields(&self) -> &T { match self { Self::Standard { extra_fields, .. } => extra_fields, @@ -738,6 +753,7 @@ impl OrderType { } /// Transform the extra fields type using a function + #[must_use] pub fn map_extra_fields(self, f: F) -> OrderType where F: FnOnce(T) -> U, diff --git a/src/orders/pegged.rs b/src/orders/pegged.rs index 73cba02..dcccb25 100644 --- a/src/orders/pegged.rs +++ b/src/orders/pegged.rs @@ -4,6 +4,7 @@ use std::fmt; use std::str::FromStr; /// Reference price type for pegged orders +#[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum PegReferenceType { /// Pegged to best bid price diff --git a/src/orders/status.rs b/src/orders/status.rs index 2883e1e..f1e7296 100644 --- a/src/orders/status.rs +++ b/src/orders/status.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use std::str::FromStr; /// Represents the current status of an order in the system +#[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum OrderStatus { /// Order has been created but not yet processed @@ -30,6 +31,7 @@ pub enum OrderStatus { impl OrderStatus { /// Returns true if the order is still active in the book #[allow(dead_code)] + #[must_use] pub fn is_active(&self) -> bool { matches!(self, Self::Active | Self::PartiallyFilled) } @@ -37,6 +39,7 @@ impl OrderStatus { /// Returns true if the order has been terminated /// (filled, canceled, rejected, or expired) #[allow(dead_code)] + #[must_use] pub fn is_terminated(&self) -> bool { matches!( self, diff --git a/src/orders/time_in_force.rs b/src/orders/time_in_force.rs index c3b9292..9db6a25 100644 --- a/src/orders/time_in_force.rs +++ b/src/orders/time_in_force.rs @@ -4,6 +4,7 @@ use std::fmt; use std::str::FromStr; /// Specifies how long an order remains active before it is executed or expires. +#[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum TimeInForce { /// Good 'Til Canceled - The order remains active until it is filled or canceled. @@ -36,16 +37,19 @@ pub enum TimeInForce { impl TimeInForce { /// Returns true if the order should be canceled after attempting to match + #[must_use] pub fn is_immediate(&self) -> bool { matches!(self, Self::Ioc | Self::Fok) } /// Returns true if the order has a specific expiration time + #[must_use] pub fn has_expiry(&self) -> bool { matches!(self, Self::Gtd(_) | Self::Day) } /// Checks if an order with this time in force has expired + #[must_use] pub fn is_expired(&self, current_timestamp: u64, market_close_timestamp: Option) -> bool { match self { Self::Gtd(expiry) => current_timestamp >= *expiry, diff --git a/src/price_level/entry.rs b/src/price_level/entry.rs index 8157384..eea3016 100644 --- a/src/price_level/entry.rs +++ b/src/price_level/entry.rs @@ -18,16 +18,19 @@ pub struct OrderBookEntry { impl OrderBookEntry { /// Create a new order book entry #[allow(dead_code)] + #[must_use] pub fn new(level: Arc, index: usize) -> Self { Self { level, index } } /// Get the price of this entry + #[must_use] pub fn price(&self) -> u128 { self.level.price() } /// Get the visible quantity at this entry + #[must_use] pub fn visible_quantity(&self) -> u64 { self.level.visible_quantity() } @@ -39,6 +42,7 @@ impl OrderBookEntry { /// Get the order count at this entry #[allow(dead_code)] + #[must_use] pub fn order_count(&self) -> usize { self.level.order_count() } diff --git a/src/price_level/level.rs b/src/price_level/level.rs index 8ad7857..10ed5a8 100644 --- a/src/price_level/level.rs +++ b/src/price_level/level.rs @@ -71,6 +71,7 @@ impl PriceLevel { impl PriceLevel { /// Create a new price level + #[must_use] pub fn new(price: u128) -> Self { Self { price, @@ -83,16 +84,19 @@ impl PriceLevel { } /// Get the price of this level + #[must_use] pub fn price(&self) -> u128 { self.price } /// Get the visible quantity + #[must_use] pub fn visible_quantity(&self) -> u64 { self.visible_quantity.load(Ordering::Acquire) } /// Get the hidden quantity + #[must_use] pub fn hidden_quantity(&self) -> u64 { self.hidden_quantity.load(Ordering::Acquire) } @@ -107,11 +111,13 @@ impl PriceLevel { } /// Get the number of orders + #[must_use] pub fn order_count(&self) -> usize { self.order_count.load(Ordering::Acquire) } /// Get the statistics for this price level + #[must_use] pub fn stats(&self) -> Arc { self.stats.clone() } @@ -139,6 +145,7 @@ impl PriceLevel { } /// Creates an iterator over the orders in the price level. + #[must_use] pub fn iter_orders(&self) -> Vec>> { self.orders.to_vec() } @@ -260,6 +267,7 @@ impl PriceLevel { } /// Create a snapshot of the current price level state + #[must_use] pub fn snapshot(&self) -> PriceLevelSnapshot { PriceLevelSnapshot { price: self.price, diff --git a/src/price_level/order_queue.rs b/src/price_level/order_queue.rs index 97bc1a7..9dbdcb4 100644 --- a/src/price_level/order_queue.rs +++ b/src/price_level/order_queue.rs @@ -22,6 +22,7 @@ pub struct OrderQueue { impl OrderQueue { /// Create a new empty order queue + #[must_use] pub fn new() -> Self { Self { orders: DashMap::new(), @@ -37,6 +38,7 @@ impl OrderQueue { } /// Attempt to pop an order from the queue + #[must_use] pub fn pop(&self) -> Option>> { loop { if let Some(order_id) = self.order_ids.pop() { @@ -52,17 +54,20 @@ impl OrderQueue { } /// Search for an order with the given ID. O(1) operation. + #[must_use] pub fn find(&self, order_id: Id) -> Option>> { self.orders.get(&order_id).map(|o| o.value().clone()) } /// Remove an order with the given ID /// Returns the removed order if found. O(1) for the map, but the ID remains in the queue. + #[must_use] pub fn remove(&self, order_id: Id) -> Option>> { self.orders.remove(&order_id).map(|(_, order)| order) } /// Convert the queue to a vector (for snapshots) + #[must_use] pub fn to_vec(&self) -> Vec>> { let mut orders: Vec>> = self.orders.iter().map(|o| o.value().clone()).collect(); @@ -86,6 +91,7 @@ impl OrderQueue { /// A new `OrderQueue` instance containing all the orders from the input vector. /// #[allow(dead_code)] + #[must_use] pub fn from_vec(orders: Vec>>) -> Self { let queue = OrderQueue::new(); for order in orders { @@ -96,6 +102,7 @@ impl OrderQueue { /// Check if the queue is empty #[allow(dead_code)] + #[must_use] pub fn is_empty(&self) -> bool { self.orders.is_empty() } @@ -106,6 +113,7 @@ impl OrderQueue { /// /// * `usize` - The total count of orders in the queue. /// + #[must_use] pub fn len(&self) -> usize { self.orders.len() } diff --git a/src/price_level/snapshot.rs b/src/price_level/snapshot.rs index fcdb6d8..abc4cf7 100644 --- a/src/price_level/snapshot.rs +++ b/src/price_level/snapshot.rs @@ -27,6 +27,7 @@ pub struct PriceLevelSnapshot { impl PriceLevelSnapshot { /// Create a new empty snapshot + #[must_use] pub fn new(price: u128) -> Self { Self { price, diff --git a/src/price_level/statistics.rs b/src/price_level/statistics.rs index d4c9873..c5ed73a 100644 --- a/src/price_level/statistics.rs +++ b/src/price_level/statistics.rs @@ -75,6 +75,7 @@ impl PriceLevelStatistics { } /// Create new empty statistics + #[must_use] pub fn new() -> Self { let current_time = Self::current_timestamp_milliseconds_or_zero(); @@ -145,31 +146,37 @@ impl PriceLevelStatistics { } /// Get total number of orders added + #[must_use] pub fn orders_added(&self) -> usize { self.orders_added.load(Ordering::Relaxed) } /// Get total number of orders removed + #[must_use] pub fn orders_removed(&self) -> usize { self.orders_removed.load(Ordering::Relaxed) } /// Get total number of orders executed + #[must_use] pub fn orders_executed(&self) -> usize { self.orders_executed.load(Ordering::Relaxed) } /// Get total quantity executed + #[must_use] pub fn quantity_executed(&self) -> u64 { self.quantity_executed.load(Ordering::Relaxed) } /// Get total value executed + #[must_use] pub fn value_executed(&self) -> u64 { self.value_executed.load(Ordering::Relaxed) } /// Get average execution price + #[must_use] pub fn average_execution_price(&self) -> Option { let qty = self.quantity_executed.load(Ordering::Relaxed); let value = self.value_executed.load(Ordering::Relaxed); @@ -182,6 +189,7 @@ impl PriceLevelStatistics { } /// Get average waiting time for executed orders (in milliseconds) + #[must_use] pub fn average_waiting_time(&self) -> Option { let count = self.orders_executed.load(Ordering::Relaxed); let sum = self.sum_waiting_time.load(Ordering::Relaxed); @@ -194,6 +202,7 @@ impl PriceLevelStatistics { } /// Get time since last execution (in milliseconds) + #[must_use] pub fn time_since_last_execution(&self) -> Option { let last = self.last_execution_time.load(Ordering::Relaxed); if last == 0 {