Skip to main content

nautilus_macros/
lib.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//! Procedural macros for Nautilus model and serialization types.
17
18#![warn(clippy::pedantic)]
19#![allow(
20    clippy::manual_let_else,
21    clippy::option_if_let_else,
22    clippy::or_fun_call,
23    clippy::redundant_else,
24    clippy::similar_names,
25    clippy::single_match_else,
26    reason = "workspace policy; generated by scripts/update-clippy-allowances.py"
27)]
28#![allow(
29    clippy::assert_is_empty,
30    reason = "`assert!(x.is_empty())` is clearer than comparing against an empty value"
31)]
32
33mod model;
34mod serialization;
35mod support;
36
37use proc_macro::TokenStream;
38
39/// Expands a named-field struct into a Serde-backed custom data type.
40///
41/// Generates the required Serde derives, `HasTsInit`, `CustomDataTrait`, `Data` conversions,
42/// constructor, display implementation, and optional PyO3 bindings. The struct must contain
43/// `ts_event` and `ts_init` fields.
44///
45/// Use `#[custom_data(pyo3)]` to generate Python bindings.
46/// Use `#[custom_data(pyo3, no_display)]` to provide a custom display implementation.
47/// Use `stub_module = "nautilus_trader.<module>"` with `pyo3` to emit stub metadata.
48/// Use `#[custom_data_field(serde)]` for fields requiring the generic Serde Python bridge.
49#[proc_macro_attribute]
50pub fn custom_data(attr: TokenStream, item: TokenStream) -> TokenStream {
51    model::expand_custom_data(attr.into(), item.into()).into()
52}
53
54/// Adds Arrow schema, encoding, and decoding implementations to a custom data struct.
55///
56/// Apply this above `#[nautilus_model::custom_data]` for macro-generated model behavior, or use it
57/// alone when the model traits are implemented manually. Call
58/// `nautilus_serialization::ensure_custom_data_registered::<T>()` before catalog or streaming
59/// serialization.
60///
61/// Supported field types include `InstrumentId`, `AccountId`, `Currency`, `BarType`, `Params`,
62/// `Price`, `Quantity`, `Decimal`, optional prices and quantities, `UnixNanos`, `f64`, `f32`, `bool`,
63/// `String`, `u64`, `i64`, `u32`, `i32`, `Vec<f64>`, and `Vec<u8>`.
64/// Prices and quantities use nullable `Decimal128(38, 16)` columns. `Vec<u8>` is the deliberate
65/// opaque-byte escape hatch for custom schemas.
66/// `Decimal` values preserve their numeric value at scale 16, but not the source value's
67/// trailing-zero scale.
68/// Use `#[custom_data_field(native_enum)]` on a native enum that implements `Display` and
69/// `FromStr` to encode it as a compact dictionary of display names.
70/// Use `#[custom_data_field(serde)]` on a field to store any Serde serializable field as a
71/// Serde JSON-backed Arrow `Utf8` column. Python field access for such fields comes from
72/// `nautilus_model`'s `custom_data` macro, not this macro.
73///
74/// Use `#[arrow_custom_data(pyo3)]` to generate `PyArrow` `encode_record_batch_py` and
75/// `decode_record_batch_py` methods; this is independent from the model macro's `pyo3` option.
76/// Use `stub_module = "nautilus_trader.<module>"` with `pyo3` to emit pyo3-stub-gen metadata.
77#[proc_macro_attribute]
78pub fn arrow_custom_data(attr: TokenStream, item: TokenStream) -> TokenStream {
79    serialization::expand_arrow_custom_data(attr.into(), item.into()).into()
80}