Skip to main content

brokapi_bcs/
cid.rs

1//! The client order id codec: BCS accepts a full UUID as `clientOrderId`.
2
3use brokapi_core::{Charset, CidCaps, CidCodec, CidError, ClientOrderId, NativeCid};
4use uuid::Uuid;
5
6/// BCS accepts full UUID strings as `clientOrderId` (lossless, 128-bit).
7#[derive(Debug, Clone, Copy, Default)]
8pub struct BcsCidCodec;
9
10impl CidCodec for BcsCidCodec {
11    fn caps(&self) -> CidCaps {
12        CidCaps {
13            max_len: 36,
14            charset: Charset::Ascii,
15            native_bits: 128,
16            lossless: true,
17        }
18    }
19
20    fn encode(&self, cid: ClientOrderId) -> Result<NativeCid, CidError> {
21        let s = cid.hyphenated().to_string();
22        if s.len() > self.caps().max_len {
23            return Err(CidError::TooLong {
24                len: s.len(),
25                max: self.caps().max_len,
26            });
27        }
28        Ok(NativeCid::new(s))
29    }
30
31    fn decode(&self, native: &NativeCid) -> Option<ClientOrderId> {
32        Uuid::parse_str(native.as_str()).ok()
33    }
34}
35
36/// Namespace of the stable cids given to orders that are not ours.
37const FOREIGN_NS: Uuid = Uuid::from_u128(0x7bc5_2f01_53a4_4d0e_9b6a_0c8e_bc50_0001);
38
39/// Stable cid for an order of this account that this connector did not place (the broker's order
40/// list carries only the broker's id, not our `clientOrderId`): the same order id always gives the
41/// same cid, so it can be told apart from ours and found again.
42pub fn foreign_cid(order_id: &str) -> ClientOrderId {
43    Uuid::new_v5(&FOREIGN_NS, order_id.as_bytes())
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use proptest::prelude::*;
50
51    prop_compose! {
52        fn arb_cid()(bytes in any::<[u8; 16]>()) -> ClientOrderId {
53            ClientOrderId::from_bytes(bytes)
54        }
55    }
56
57    #[test]
58    fn foreign_cids_are_stable_and_distinct() {
59        assert_eq!(
60            foreign_cid("20261019-TQBR-1"),
61            foreign_cid("20261019-TQBR-1")
62        );
63        assert_ne!(
64            foreign_cid("20261019-TQBR-1"),
65            foreign_cid("20261019-TQBR-2")
66        );
67        assert_eq!(foreign_cid("x").get_version_num(), 5);
68    }
69
70    proptest! {
71        #[test]
72        fn bcs_cid_roundtrip(cid in arb_cid()) {
73            let codec = BcsCidCodec;
74            let native = codec.encode(cid).expect("encode");
75            let decoded = codec.decode(&native).expect("decode");
76            prop_assert_eq!(decoded, cid);
77        }
78    }
79}