Skip to main content

nautilus_plugin/surfaces/commands/
query.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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
16//! Query commands and their boundary-owned handles.
17//!
18//! The plug-in constructs a [`QueryAccountCommand`] or [`QueryOrderCommand`],
19//! wraps it in the matching `*Handle`, and hands the host a pointer via
20//! [`HostVTable::query_account`](crate::host::HostVTable::query_account) or
21//! [`HostVTable::query_order`](crate::host::HostVTable::query_order). The
22//! host derefs the handle once and routes the borrowed command into the
23//! calling strategy's query path. The plug-in owns the box and frees it
24//! when the call returns.
25
26#![allow(unsafe_code)]
27
28use std::ops::Deref;
29
30use nautilus_core::Params;
31use nautilus_model::identifiers::{AccountId, ClientId, ClientOrderId};
32
33/// Query-account command. Mirrors the arguments to `Strategy::query_account`.
34#[repr(C)]
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct QueryAccountCommand {
37    /// The account identifier to query.
38    pub account_id: AccountId,
39
40    /// Optional client routing identifier.
41    pub client_id: Option<ClientId>,
42
43    /// Optional venue-specific parameters.
44    pub params: Option<Params>,
45}
46
47impl QueryAccountCommand {
48    /// Creates a new [`QueryAccountCommand`] instance.
49    #[must_use]
50    pub const fn new(
51        account_id: AccountId,
52        client_id: Option<ClientId>,
53        params: Option<Params>,
54    ) -> Self {
55        Self {
56            account_id,
57            client_id,
58            params,
59        }
60    }
61}
62
63/// Boundary-owned wrapper that lets [`QueryAccountCommand`] cross the cdylib
64/// FFI boundary by reference.
65#[repr(C)]
66#[derive(Debug, Clone)]
67pub struct QueryAccountHandle(Box<QueryAccountCommand>);
68
69impl QueryAccountHandle {
70    /// Wraps `command` in a boundary-owned handle.
71    #[must_use]
72    pub fn new(command: QueryAccountCommand) -> Self {
73        Self(Box::new(command))
74    }
75
76    /// Returns a reference to the wrapped command.
77    #[must_use]
78    pub fn command(&self) -> &QueryAccountCommand {
79        &self.0
80    }
81
82    /// Consumes the wrapper and returns the inner command.
83    #[must_use]
84    pub fn into_inner(self) -> QueryAccountCommand {
85        *self.0
86    }
87}
88
89impl Deref for QueryAccountHandle {
90    type Target = QueryAccountCommand;
91
92    fn deref(&self) -> &Self::Target {
93        &self.0
94    }
95}
96
97/// Query-order command. Mirrors the arguments to `Strategy::query_order`.
98///
99/// The host resolves `client_order_id` against the live cache to materialise
100/// the `&OrderAny` reference the trait method requires.
101#[repr(C)]
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct QueryOrderCommand {
104    /// The client order identifier of the order to query.
105    pub client_order_id: ClientOrderId,
106
107    /// Optional client routing identifier.
108    pub client_id: Option<ClientId>,
109
110    /// Optional venue-specific parameters.
111    pub params: Option<Params>,
112}
113
114impl QueryOrderCommand {
115    /// Creates a new [`QueryOrderCommand`] instance.
116    #[must_use]
117    pub const fn new(
118        client_order_id: ClientOrderId,
119        client_id: Option<ClientId>,
120        params: Option<Params>,
121    ) -> Self {
122        Self {
123            client_order_id,
124            client_id,
125            params,
126        }
127    }
128}
129
130/// Boundary-owned wrapper that lets [`QueryOrderCommand`] cross the cdylib
131/// FFI boundary by reference.
132#[repr(C)]
133#[derive(Debug, Clone)]
134pub struct QueryOrderHandle(Box<QueryOrderCommand>);
135
136impl QueryOrderHandle {
137    /// Wraps `command` in a boundary-owned handle.
138    #[must_use]
139    pub fn new(command: QueryOrderCommand) -> Self {
140        Self(Box::new(command))
141    }
142
143    /// Returns a reference to the wrapped command.
144    #[must_use]
145    pub fn command(&self) -> &QueryOrderCommand {
146        &self.0
147    }
148
149    /// Consumes the wrapper and returns the inner command.
150    #[must_use]
151    pub fn into_inner(self) -> QueryOrderCommand {
152        *self.0
153    }
154}
155
156impl Deref for QueryOrderHandle {
157    type Target = QueryOrderCommand;
158
159    fn deref(&self) -> &Self::Target {
160        &self.0
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use rstest::rstest;
167
168    use super::*;
169
170    #[rstest]
171    fn query_account_handle_round_trips_command() {
172        let cmd = QueryAccountCommand::new(AccountId::from("BINANCE-001"), None, None);
173        let handle = QueryAccountHandle::new(cmd.clone());
174        assert_eq!(handle.command(), &cmd);
175        assert_eq!(&*handle, &cmd);
176        assert_eq!(handle.into_inner(), cmd);
177    }
178
179    #[rstest]
180    fn query_order_handle_round_trips_command() {
181        let cmd = QueryOrderCommand::new(ClientOrderId::from("O-1"), None, None);
182        let handle = QueryOrderHandle::new(cmd.clone());
183        assert_eq!(handle.command(), &cmd);
184        assert_eq!(&*handle, &cmd);
185        assert_eq!(handle.into_inner(), cmd);
186    }
187}