nautilus_system/python/
config.rs1use nautilus_core::{UnixNanos, python::to_pyvalue_err};
19use pyo3::prelude::*;
20
21use crate::config::{RotationConfig, StreamingConfig};
22
23const NANOSECONDS_PER_DAY: u64 = 86_400_000_000_000;
24
25#[pyo3_stub_gen::derive::gen_stub_pymethods]
26#[pymethods]
27impl StreamingConfig {
28 #[new]
30 #[pyo3(signature = (
31 catalog_path,
32 fs_protocol=None,
33 flush_interval_ms=None,
34 replace_existing=false,
35 rotation_mode="NO_ROTATION",
36 max_file_size=1_073_741_824,
37 rotation_interval_ns=None,
38 schedule_ns=None,
39 ))]
40 #[expect(clippy::too_many_arguments)]
41 fn py_new(
42 catalog_path: String,
43 fs_protocol: Option<String>,
44 flush_interval_ms: Option<u64>,
45 replace_existing: bool,
46 rotation_mode: &str,
47 max_file_size: u64,
48 rotation_interval_ns: Option<u64>,
49 schedule_ns: Option<u64>,
50 ) -> PyResult<Self> {
51 let rotation_config = match rotation_mode.to_ascii_uppercase().as_str() {
52 "SIZE" => {
53 if max_file_size == 0 {
54 return Err(to_pyvalue_err("max_file_size must be positive"));
55 }
56 RotationConfig::Size {
57 max_size: max_file_size,
58 }
59 }
60 "INTERVAL" => RotationConfig::Interval {
61 interval_ns: positive_interval(rotation_interval_ns)?,
62 },
63 "SCHEDULED_DATES" => RotationConfig::ScheduledDates {
64 interval_ns: positive_interval(rotation_interval_ns)?,
65 schedule_ns: UnixNanos::from(schedule_ns.unwrap_or(0)),
66 },
67 "NO_ROTATION" => RotationConfig::NoRotation,
68 value => {
69 return Err(to_pyvalue_err(format!("Invalid rotation_mode: '{value}'")));
70 }
71 };
72
73 Self::builder()
74 .catalog_path(catalog_path)
75 .fs_protocol(fs_protocol.unwrap_or_else(|| "file".to_string()))
76 .flush_interval_ms(flush_interval_ms.unwrap_or(1_000))
77 .replace_existing(replace_existing)
78 .rotation_config(rotation_config)
79 .build()
80 .map_err(nautilus_common::python::config_error_to_pyvalue_err)
81 }
82
83 #[getter]
84 fn catalog_path(&self) -> &str {
85 &self.catalog_path
86 }
87
88 #[getter]
89 fn fs_protocol(&self) -> &str {
90 &self.fs_protocol
91 }
92
93 #[getter]
94 const fn flush_interval_ms(&self) -> u64 {
95 self.flush_interval_ms
96 }
97
98 #[getter]
99 const fn replace_existing(&self) -> bool {
100 self.replace_existing
101 }
102
103 #[getter]
104 fn rotation_mode(&self) -> &'static str {
105 match self.rotation_config {
106 RotationConfig::Size { .. } => "SIZE",
107 RotationConfig::Interval { .. } => "INTERVAL",
108 RotationConfig::ScheduledDates { .. } => "SCHEDULED_DATES",
109 RotationConfig::NoRotation => "NO_ROTATION",
110 }
111 }
112
113 #[getter]
114 fn max_file_size(&self) -> Option<u64> {
115 match self.rotation_config {
116 RotationConfig::Size { max_size } => Some(max_size),
117 _ => None,
118 }
119 }
120
121 #[getter]
122 fn rotation_interval_ns(&self) -> Option<u64> {
123 match self.rotation_config {
124 RotationConfig::Interval { interval_ns }
125 | RotationConfig::ScheduledDates { interval_ns, .. } => Some(interval_ns),
126 _ => None,
127 }
128 }
129
130 #[getter]
131 fn schedule_ns(&self) -> Option<u64> {
132 match self.rotation_config {
133 RotationConfig::ScheduledDates { schedule_ns, .. } => Some(schedule_ns.as_u64()),
134 _ => None,
135 }
136 }
137
138 fn __repr__(&self) -> String {
139 format!("{self:?}")
140 }
141}
142
143fn positive_interval(interval_ns: Option<u64>) -> PyResult<u64> {
144 let interval_ns = interval_ns.unwrap_or(NANOSECONDS_PER_DAY);
145 if interval_ns == 0 {
146 return Err(to_pyvalue_err("rotation_interval_ns must be positive"));
147 }
148 Ok(interval_ns)
149}