use chrono::{DateTime, Datelike, Duration, NaiveDateTime, Utc, Weekday};
use serde::{Deserialize, Deserializer};
pub async fn sleep(millis: u64) {
tokio::time::sleep(std::time::Duration::from_millis(millis)).await;
}
pub fn multiply_by_1000_f64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = f64::deserialize(deserializer)?;
Ok((value * 1000.0) as i64)
}
pub fn multiply_by_10_string<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let value: f64 = s.parse().map_err(serde::de::Error::custom)?;
Ok((value * 10.0) as i64)
}
pub fn multiply_by_1000_string<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let value: f64 = s.parse().map_err(serde::de::Error::custom)?;
Ok((value * 1000.0) as i64)
}
pub fn multiply_by_10_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = i64::deserialize(deserializer)?;
Ok(value * 10)
}
pub fn format_datetime<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: serde::Deserializer<'de>,
{
let timestamp_str = String::deserialize(deserializer)?;
let timestamp_ms = timestamp_str
.parse::<i64>()
.map_err(|e| serde::de::Error::custom(format!("Failed to parse timestamp: {}", e)))?;
let secs = timestamp_ms / 1000;
let nsecs = ((timestamp_ms % 1000) * 1_000_000) as u32;
let datetime = DateTime::from_timestamp(secs, nsecs)
.ok_or_else(|| serde::de::Error::custom("Invalid timestamp"))?;
Ok(datetime)
}
pub fn parse_string_to_float<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let value: f64 = s.parse().map_err(serde::de::Error::custom)?;
Ok(value)
}
pub fn format_iso8601_datetime<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where
D: serde::Deserializer<'de>,
{
let iso8601_str = String::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&iso8601_str, "%Y-%m-%dT%H:%M:%S%.f")
.map_err(serde::de::Error::custom)
}
pub fn round_and_to_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let opt_value: Option<f64> = Option::deserialize(deserializer)?;
match opt_value {
Some(value) => {
let rounded_value = value.round() as i64;
Ok(rounded_value)
}
None => Ok(0), }
}
pub fn to_i64_and_multiply_by_1000<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let opt_value: Option<f64> = Option::deserialize(deserializer)?;
match opt_value {
Some(value) => {
let rounded_value = (value * 1000.0).round() as i64;
Ok(rounded_value)
}
None => Ok(0), }
}
pub fn next_working_day(mut date: DateTime<Utc>) -> DateTime<Utc> {
date = date + Duration::days(1);
while matches!(date.weekday(), Weekday::Sat | Weekday::Sun) {
date = date + Duration::days(1);
}
date
}
pub fn previous_working_day(mut date: DateTime<Utc>) -> DateTime<Utc> {
while matches!(date.weekday(), Weekday::Sat | Weekday::Sun) {
date = date - Duration::days(1);
}
date
}
pub fn remove_special_character_from_string<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let text = String::deserialize(deserializer)?;
Ok(text.trim().replace('\n', ""))
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::{TimeZone, Utc, Weekday};
#[test]
fn next_working_day_monday() {
let date = Utc.with_ymd_and_hms(2025, 7, 28, 12, 0, 0).unwrap(); let tomorrow = date + Duration::days(1);
let result = next_working_day(date);
assert_eq!(result.date_naive(), tomorrow.date_naive());
assert_eq!(result.weekday(), Weekday::Tue);
}
#[test]
fn test_previous_working_day_monday() {
let date = Utc.with_ymd_and_hms(2024, 6, 10, 12, 0, 0).unwrap(); let result = previous_working_day(date);
assert_eq!(result.date_naive(), date.date_naive());
assert_eq!(result.weekday(), Weekday::Mon);
}
#[test]
fn test_previous_working_day_sunday() {
let date = Utc.with_ymd_and_hms(2025, 7, 27, 12, 0, 0).unwrap(); let result = previous_working_day(date);
assert_eq!(
result.date_naive(),
Utc.with_ymd_and_hms(2025, 7, 25, 12, 0, 0)
.unwrap()
.date_naive()
); assert_eq!(result.weekday(), Weekday::Fri);
}
#[test]
fn test_previous_working_day_saturday() {
let date = Utc.with_ymd_and_hms(2024, 6, 8, 12, 0, 0).unwrap(); let result = previous_working_day(date);
assert_eq!(
result.date_naive(),
Utc.with_ymd_and_hms(2024, 6, 7, 12, 0, 0)
.unwrap()
.date_naive()
); assert_eq!(result.weekday(), Weekday::Fri);
}
#[test]
fn test_previous_working_day_wednesday() {
let date = Utc.with_ymd_and_hms(2024, 6, 12, 12, 0, 0).unwrap(); let result = previous_working_day(date);
assert_eq!(result.date_naive(), date.date_naive());
assert_eq!(result.weekday(), Weekday::Wed);
}
}
pub fn format_number_with_commas(n: i64) -> String {
let mut s = n.abs().to_string();
let mut result = String::new();
let mut count = 0;
while let Some(c) = s.pop() {
if count != 0 && count % 3 == 0 {
result.insert(0, ',');
}
result.insert(0, c);
count += 1;
}
if n < 0 {
result.insert(0, '-');
}
result
}
#[test]
fn test_format_number_with_commas_positive() {
assert_eq!(format_number_with_commas(1398300), "1,398,300");
assert_eq!(format_number_with_commas(1000), "1,000");
assert_eq!(format_number_with_commas(12), "12");
assert_eq!(format_number_with_commas(0), "0");
}
#[test]
fn test_format_number_with_commas_negative() {
assert_eq!(format_number_with_commas(-1398300), "-1,398,300");
assert_eq!(format_number_with_commas(-1000), "-1,000");
assert_eq!(format_number_with_commas(-12), "-12");
assert_eq!(format_number_with_commas(-1), "-1");
}
pub fn get_property_from_event<T>(
payload: Option<serde_json::Value>,
property_name: &str,
) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
payload.and_then(|value| {
value
.get(property_name)
.and_then(|prop| serde_json::from_value(prop.clone()).ok())
})
}
#[test]
fn test_get_property_from_event() {
let json_string = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
let payload: Option<serde_json::Value> = serde_json::from_str(json_string).unwrap();
assert_eq!(
get_property_from_event::<String>(payload, "name"),
Some("John Doe".to_string())
);
}