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}