use std::{fmt::Debug, sync::LazyLock};
use databento::historical::DateTimeRange;
use nautilus_core::{UnixNanos, string::secret::REDACTED};
use nautilus_model::identifiers::{ClientId, Venue};
use time::OffsetDateTime;
use ustr::Ustr;
use zeroize::ZeroizeOnDrop;
pub const DATABENTO: &str = "DATABENTO";
pub static DATABENTO_VENUE: LazyLock<Venue> = LazyLock::new(|| Venue::new(Ustr::from(DATABENTO)));
pub static DATABENTO_CLIENT_ID: LazyLock<ClientId> =
LazyLock::new(|| ClientId::new(Ustr::from(DATABENTO)));
pub const ALL_SYMBOLS: &str = "ALL_SYMBOLS";
#[derive(Clone, ZeroizeOnDrop)]
pub struct Credential {
api_key: Box<[u8]>,
}
impl Debug for Credential {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(Credential))
.field("api_key", &REDACTED)
.finish()
}
}
impl Credential {
#[must_use]
pub fn new(api_key: impl Into<String>) -> Self {
let api_key_bytes = api_key.into().into_bytes();
Self {
api_key: api_key_bytes.into_boxed_slice(),
}
}
#[must_use]
pub fn api_key(&self) -> &str {
std::str::from_utf8(&self.api_key).expect("API key is valid UTF-8")
}
#[must_use]
pub fn api_key_masked(&self) -> String {
nautilus_core::string::secret::mask_api_key(self.api_key())
}
}
pub fn get_date_time_range(start: UnixNanos, end: UnixNanos) -> anyhow::Result<DateTimeRange> {
Ok(DateTimeRange::from((
OffsetDateTime::from_unix_timestamp_nanos(i128::from(start.as_u64()))?,
OffsetDateTime::from_unix_timestamp_nanos(i128::from(end.as_u64()))?,
)))
}
#[cfg(test)]
mod tests {
use rstest::*;
use super::*;
#[rstest]
#[case(
UnixNanos::default(),
UnixNanos::default(),
"DateTimeRange { start: 1970-01-01 0:00:00.0 +00:00:00, end: 1970-01-01 0:00:00.0 +00:00:00 }"
)]
#[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 }")]
fn test_get_date_time_range(
#[case] start: UnixNanos,
#[case] end: UnixNanos,
#[case] range_str: &str,
) {
let range = get_date_time_range(start, end).unwrap();
assert_eq!(format!("{range:?}"), range_str);
}
#[rstest]
fn test_credential_api_key_masked_short() {
let credential = Credential::new("short");
assert_eq!(credential.api_key_masked(), "*****");
}
#[rstest]
fn test_credential_api_key_masked_long() {
let credential = Credential::new("abcdefghijklmnop");
assert_eq!(credential.api_key_masked(), "abcd...mnop");
}
#[rstest]
fn test_credential_debug_redaction() {
let credential = Credential::new("test_api_key");
let debug_str = format!("{credential:?}");
assert!(debug_str.contains(REDACTED));
assert!(!debug_str.contains("test_api_key"));
}
}