nautilus_databento/
common.rs1use std::fmt::Debug;
19
20use databento::historical::DateTimeRange;
21use nautilus_core::{UnixNanos, string::secret::REDACTED};
22use time::OffsetDateTime;
23use zeroize::ZeroizeOnDrop;
24
25pub const DATABENTO: &str = "DATABENTO";
26pub const ALL_SYMBOLS: &str = "ALL_SYMBOLS";
27
28#[derive(Clone, ZeroizeOnDrop)]
30pub struct Credential {
31 api_key: Box<[u8]>,
32}
33
34impl Debug for Credential {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.debug_struct(stringify!(Credential))
37 .field("api_key", &REDACTED)
38 .finish()
39 }
40}
41
42impl Credential {
43 #[must_use]
45 pub fn new(api_key: impl Into<String>) -> Self {
46 let api_key_bytes = api_key.into().into_bytes();
47
48 Self {
49 api_key: api_key_bytes.into_boxed_slice(),
50 }
51 }
52
53 #[must_use]
60 pub fn api_key(&self) -> &str {
61 std::str::from_utf8(&self.api_key).expect("API key is valid UTF-8")
62 }
63
64 #[must_use]
69 pub fn api_key_masked(&self) -> String {
70 nautilus_core::string::secret::mask_api_key(self.api_key())
71 }
72}
73
74pub fn get_date_time_range(start: UnixNanos, end: UnixNanos) -> anyhow::Result<DateTimeRange> {
78 Ok(DateTimeRange::from((
79 OffsetDateTime::from_unix_timestamp_nanos(i128::from(start.as_u64()))?,
80 OffsetDateTime::from_unix_timestamp_nanos(i128::from(end.as_u64()))?,
81 )))
82}
83
84#[cfg(test)]
85mod tests {
86 use rstest::*;
87
88 use super::*;
89
90 #[rstest]
91 #[case(
92 UnixNanos::default(),
93 UnixNanos::default(),
94 "DateTimeRange { start: 1970-01-01 0:00:00.0 +00:00:00, end: 1970-01-01 0:00:00.0 +00:00:00 }"
95 )]
96 #[case(UnixNanos::default(), 1_000_000_000.into(), "DateTimeRange { start: 1970-01-01 0:00:00.0 +00:00:00, end: 1970-01-01 0:00:01.0 +00:00:00 }")]
97 fn test_get_date_time_range(
98 #[case] start: UnixNanos,
99 #[case] end: UnixNanos,
100 #[case] range_str: &str,
101 ) {
102 let range = get_date_time_range(start, end).unwrap();
103 assert_eq!(format!("{range:?}"), range_str);
104 }
105
106 #[rstest]
107 fn test_credential_api_key_masked_short() {
108 let credential = Credential::new("short");
109 assert_eq!(credential.api_key_masked(), "*****");
110 }
111
112 #[rstest]
113 fn test_credential_api_key_masked_long() {
114 let credential = Credential::new("abcdefghijklmnop");
115 assert_eq!(credential.api_key_masked(), "abcd...mnop");
116 }
117
118 #[rstest]
119 fn test_credential_debug_redaction() {
120 let credential = Credential::new("test_api_key");
121 let debug_str = format!("{credential:?}");
122 assert!(debug_str.contains(REDACTED));
123 assert!(!debug_str.contains("test_api_key"));
124 }
125}