nautilus_model/ffi/identifiers/
instrument_id.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    collections::hash_map::DefaultHasher,
18    ffi::c_char,
19    hash::{Hash, Hasher},
20    str::FromStr,
21};
22
23use nautilus_core::ffi::string::{cstr_as_str, str_to_cstr};
24
25use crate::identifiers::{InstrumentId, Symbol, Venue};
26
27#[unsafe(no_mangle)]
28pub extern "C" fn instrument_id_new(symbol: Symbol, venue: Venue) -> InstrumentId {
29    InstrumentId::new(symbol, venue)
30}
31
32/// Returns any [`InstrumentId`] parsing error from the provided C string pointer.
33///
34/// # Safety
35///
36/// - Assumes `ptr` is a valid C string pointer.
37#[unsafe(no_mangle)]
38pub unsafe extern "C" fn instrument_id_check_parsing(ptr: *const c_char) -> *const c_char {
39    let value = unsafe { cstr_as_str(ptr) };
40    match InstrumentId::from_str(value) {
41        Ok(_) => str_to_cstr(""),
42        Err(e) => str_to_cstr(&e.to_string()),
43    }
44}
45
46/// Returns a Nautilus identifier from a C string pointer.
47///
48/// # Safety
49///
50/// - Assumes `ptr` is a valid C string pointer.
51#[unsafe(no_mangle)]
52pub unsafe extern "C" fn instrument_id_from_cstr(ptr: *const c_char) -> InstrumentId {
53    let value = unsafe { cstr_as_str(ptr) };
54    InstrumentId::from(value)
55}
56
57/// Returns an [`InstrumentId`] as a C string pointer.
58#[unsafe(no_mangle)]
59pub extern "C" fn instrument_id_to_cstr(instrument_id: &InstrumentId) -> *const c_char {
60    str_to_cstr(&instrument_id.to_string())
61}
62
63#[unsafe(no_mangle)]
64pub extern "C" fn instrument_id_hash(instrument_id: &InstrumentId) -> u64 {
65    let mut h = DefaultHasher::new();
66    instrument_id.hash(&mut h);
67    h.finish()
68}
69
70#[unsafe(no_mangle)]
71pub extern "C" fn instrument_id_is_synthetic(instrument_id: &InstrumentId) -> u8 {
72    u8::from(instrument_id.is_synthetic())
73}
74
75#[cfg(test)]
76pub mod stubs {
77    use std::str::FromStr;
78
79    use rstest::fixture;
80
81    use crate::identifiers::{InstrumentId, Symbol, Venue, stubs::*};
82
83    #[fixture]
84    pub fn btc_usdt_perp_binance() -> InstrumentId {
85        InstrumentId::from_str("BTCUSDT-PERP.BINANCE").unwrap()
86    }
87
88    #[fixture]
89    pub fn audusd_sim(symbol_aud_usd: Symbol, venue_sim: Venue) -> InstrumentId {
90        InstrumentId {
91            symbol: symbol_aud_usd,
92            venue: venue_sim,
93        }
94    }
95}
96
97////////////////////////////////////////////////////////////////////////////////
98// Tests
99////////////////////////////////////////////////////////////////////////////////
100#[cfg(test)]
101mod tests {
102    use std::ffi::CStr;
103
104    use rstest::rstest;
105
106    use super::{InstrumentId, *};
107    use crate::identifiers::{Symbol, Venue};
108
109    #[rstest]
110    fn test_to_cstr() {
111        unsafe {
112            let id = InstrumentId::from("ETH/USDT.BINANCE");
113            let result = instrument_id_to_cstr(&id);
114            assert_eq!(CStr::from_ptr(result).to_str().unwrap(), "ETH/USDT.BINANCE");
115        }
116    }
117
118    #[rstest]
119    fn test_to_cstr_and_back() {
120        unsafe {
121            let id = InstrumentId::from("ETH/USDT.BINANCE");
122            let result = instrument_id_to_cstr(&id);
123            let id2 = instrument_id_from_cstr(result);
124            assert_eq!(id, id2);
125        }
126    }
127
128    #[rstest]
129    fn test_from_symbol_and_back() {
130        unsafe {
131            let id = InstrumentId::new(Symbol::from("ETH/USDT"), Venue::from("BINANCE"));
132            let result = instrument_id_to_cstr(&id);
133            let id2 = instrument_id_from_cstr(result);
134            assert_eq!(id, id2);
135        }
136    }
137}