use super::Lock;
pub struct Reservation {
inner: Option<Box<dyn ReservationHandle>>,
lock: Lock,
}
pub(crate) trait ReservationHandle {
fn commit(self: Box<Self>);
fn rollback(self: Box<Self>);
fn lock(&self) -> Lock;
}
impl Reservation {
pub fn commit(mut self) {
if let Some(inner) = self.inner.take() {
inner.commit();
}
}
pub fn rollback(mut self) {
if let Some(inner) = self.inner.take() {
inner.rollback();
}
}
pub fn lock(&self) -> &Lock {
&self.lock
}
pub(crate) fn from_handle(inner: Box<dyn ReservationHandle>) -> Self {
let lock = inner.lock();
Self {
inner: Some(inner),
lock,
}
}
}
impl Drop for Reservation {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
inner.rollback();
}
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::rc::Rc;
use super::{Lock, Reservation, ReservationHandle};
use crate::param::{Asset, Price, Volume};
use crate::pretrade::handles::ReservationHandleImpl;
use crate::pretrade::{Mutation, RiskMutation};
#[test]
fn drop_without_explicit_finalize_rolls_back() {
let calls = Rc::new(RefCell::new(Vec::new()));
let calls_clone = Rc::clone(&calls);
let apply = Box::new(move |mutation: &RiskMutation| {
if let RiskMutation::SetKillSwitch { id, enabled } = mutation {
calls_clone.borrow_mut().push((*id, *enabled));
}
});
let reservation = Reservation::from_handle(Box::new(ReservationHandleImpl::new(
vec![
Mutation {
commit: RiskMutation::SetKillSwitch {
id: "m1",
enabled: true,
},
rollback: RiskMutation::SetKillSwitch {
id: "m1",
enabled: false,
},
},
Mutation {
commit: RiskMutation::ReserveNotional {
asset: Asset::new("USD").expect("asset code must be valid"),
amount: Volume::from_str("10").expect("volume must be valid"),
},
rollback: RiskMutation::ReserveNotional {
asset: Asset::new("USD").expect("asset code must be valid"),
amount: Volume::from_str("10").expect("volume must be valid"),
},
},
],
apply,
)));
drop(reservation);
assert_eq!(&*calls.borrow(), &[("m1", false)]);
}
#[test]
fn drop_without_explicit_finalize_can_ignore_non_kill_switch_mutations() {
let calls = Rc::new(RefCell::new(Vec::new()));
let calls_clone = Rc::clone(&calls);
let apply = Box::new(move |mutation: &RiskMutation| {
if let RiskMutation::SetKillSwitch { id, enabled } = mutation {
calls_clone.borrow_mut().push((*id, *enabled));
}
});
let reservation = Reservation::from_handle(Box::new(ReservationHandleImpl::new(
vec![
Mutation {
commit: RiskMutation::SetKillSwitch {
id: "m1",
enabled: true,
},
rollback: RiskMutation::SetKillSwitch {
id: "m1",
enabled: false,
},
},
Mutation {
commit: RiskMutation::ReserveNotional {
asset: Asset::new("USD").expect("asset code must be valid"),
amount: Volume::from_str("10").expect("volume must be valid"),
},
rollback: RiskMutation::ReserveNotional {
asset: Asset::new("USD").expect("asset code must be valid"),
amount: Volume::from_str("10").expect("volume must be valid"),
},
},
],
apply,
)));
drop(reservation);
assert_eq!(&*calls.borrow(), &[("m1", false)]);
}
#[test]
fn commit_is_noop_for_finalized_reservation() {
let reservation = Reservation {
inner: None,
lock: Lock::default(),
};
reservation.commit();
}
#[test]
fn rollback_is_noop_for_finalized_reservation() {
let reservation = Reservation {
inner: None,
lock: Lock::default(),
};
reservation.rollback();
}
#[test]
fn commit_with_locked_reservation_handle() {
let reservation = Reservation::from_handle(Box::new(LockedReservationHandle {
lock: Lock::new(None),
}));
reservation.commit();
}
#[test]
fn lock_returns_handle_lock_with_some_price() {
let price = Price::from_str("185").expect("price must be valid");
let reservation = Reservation::from_handle(Box::new(LockedReservationHandle {
lock: Lock::new(Some(price)),
}));
assert_eq!(reservation.lock().price(), Some(price));
}
#[test]
fn lock_returns_handle_lock_with_none_price() {
let reservation = Reservation::from_handle(Box::new(LockedReservationHandle {
lock: Lock::new(None),
}));
assert_eq!(reservation.lock().price(), None);
}
struct LockedReservationHandle {
lock: Lock,
}
impl ReservationHandle for LockedReservationHandle {
fn commit(self: Box<Self>) {}
fn rollback(self: Box<Self>) {}
fn lock(&self) -> Lock {
self.lock
}
}
}