#![deny(unsafe_code)]
use std::fmt;
use std::net::SocketAddr;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use std::time::Duration;
use anyhow::{anyhow, Error};
use bytestring::ByteString;
use chrono::LocalResult;
use serde::{
de::{self, Deserializer},
ser::Serializer,
Deserialize, Serialize,
};
mod counter;
mod rate_counter;
pub use counter::{Counter, StatsMergeMode};
pub use rate_counter::RateCounter;
pub type NodeId = u64;
pub type Addr = ByteString;
pub type Timestamp = i64;
pub type TimestampMillis = i64;
const BYTESIZE_K: usize = 1024;
const BYTESIZE_M: usize = 1048576;
const BYTESIZE_G: usize = 1073741824;
#[derive(Clone, Copy, Default)]
pub struct Bytesize(pub usize);
impl Bytesize {
#[inline]
pub fn as_u32(&self) -> u32 {
self.0 as u32
}
#[inline]
pub fn as_u64(&self) -> u64 {
self.0 as u64
}
#[inline]
pub fn as_usize(&self) -> usize {
self.0
}
#[inline]
pub fn string(&self) -> String {
let mut v = self.0;
let mut res = String::new();
let g = v / BYTESIZE_G;
if g > 0 {
res.push_str(&format!("{g}G"));
v %= BYTESIZE_G;
}
let m = v / BYTESIZE_M;
if m > 0 {
res.push_str(&format!("{m}M"));
v %= BYTESIZE_M;
}
let k = v / BYTESIZE_K;
if k > 0 {
res.push_str(&format!("{k}K"));
v %= BYTESIZE_K;
}
if v > 0 {
res.push_str(&format!("{v}B"));
}
res
}
}
impl Deref for Bytesize {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Bytesize {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<usize> for Bytesize {
fn from(v: usize) -> Self {
Bytesize(v)
}
}
impl TryFrom<&str> for Bytesize {
type Error = ParseSizeError;
fn try_from(v: &str) -> Result<Self, Self::Error> {
let value = to_bytesize(v)?;
Ok(Bytesize(value))
}
}
impl FromStr for Bytesize {
type Err = ParseSizeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Bytesize(to_bytesize(s)?))
}
}
impl fmt::Debug for Bytesize {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.string())?;
Ok(())
}
}
impl fmt::Display for Bytesize {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.string())
}
}
impl Serialize for Bytesize {
#[inline]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Bytesize {
#[inline]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let v = to_bytesize(&String::deserialize(deserializer)?).map_err(de::Error::custom)?;
Ok(Bytesize(v))
}
}
#[inline]
pub fn to_bytesize(text: &str) -> Result<usize, ParseSizeError> {
let text = text.to_uppercase().replace("GB", "G").replace("MB", "M").replace("KB", "K");
text.split_inclusive(['G', 'M', 'K', 'B'])
.map(|x| {
let mut chars = x.chars();
let u = chars.nth_back(0).ok_or(ParseSizeError::InvalidFormat)?;
let num_str = chars.as_str();
let v =
num_str.parse::<usize>().map_err(|_| ParseSizeError::InvalidNumber(num_str.to_string()))?;
match u {
'B' => Ok(v),
'K' => Ok(v * BYTESIZE_K),
'M' => Ok(v * BYTESIZE_M),
'G' => Ok(v * BYTESIZE_G),
_ => Err(ParseSizeError::InvalidUnit(u)),
}
})
.sum()
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum ParseSizeError {
InvalidFormat,
InvalidNumber(String),
InvalidUnit(char),
}
impl std::fmt::Display for ParseSizeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::InvalidFormat => write!(f, "invalid size format"),
Self::InvalidNumber(s) => write!(f, "invalid number: '{s}'"),
Self::InvalidUnit(c) => write!(f, "invalid unit: '{c}'"),
}
}
}
impl std::error::Error for ParseSizeError {}
#[inline]
pub fn deserialize_duration<'de, D>(deserializer: D) -> std::result::Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
Ok(to_duration(&v))
}
#[inline]
pub fn deserialize_duration_option<'de, D>(deserializer: D) -> std::result::Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
if v.is_empty() {
Ok(None)
} else {
Ok(Some(to_duration(&v)))
}
}
#[inline]
pub fn to_duration(text: &str) -> Duration {
let text = text.to_lowercase().replace("ms", "Y");
let ms: u64 = text
.split_inclusive(['s', 'm', 'h', 'd', 'w', 'f', 'Y'])
.map(|x| {
let mut chars = x.chars();
let u = match chars.nth_back(0) {
None => return 0,
Some(u) => u,
};
let v = match chars.as_str().parse::<u64>() {
Err(_e) => return 0,
Ok(v) => v,
};
match u {
'Y' => v,
's' => v * 1000,
'm' => v * 60000,
'h' => v * 3600000,
'd' => v * 86400000,
'w' => v * 604800000,
'f' => v * 1209600000,
_ => 0,
}
})
.sum();
Duration::from_millis(ms)
}
#[inline]
pub fn deserialize_addr<'de, D>(deserializer: D) -> std::result::Result<SocketAddr, D::Error>
where
D: Deserializer<'de>,
{
let addr = String::deserialize(deserializer)?
.parse::<std::net::SocketAddr>()
.map_err(serde::de::Error::custom)?;
Ok(addr)
}
#[inline]
pub fn deserialize_addr_option<'de, D>(
deserializer: D,
) -> std::result::Result<Option<std::net::SocketAddr>, D::Error>
where
D: Deserializer<'de>,
{
let addr = String::deserialize(deserializer).map(|mut addr| {
if !addr.contains(':') {
addr += ":0";
}
addr
})?;
let addr = addr.parse::<std::net::SocketAddr>().map_err(serde::de::Error::custom)?;
Ok(Some(addr))
}
#[inline]
pub fn deserialize_datetime_option<'de, D>(deserializer: D) -> std::result::Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
let t_str = String::deserialize(deserializer)?;
if t_str.is_empty() {
Ok(None)
} else {
let t = if let Ok(d) = timestamp_parse_from_str(&t_str, "%Y-%m-%d %H:%M:%S") {
Duration::from_secs(d as u64)
} else {
let d = t_str.parse::<u64>().map_err(serde::de::Error::custom)?;
Duration::from_secs(d)
};
Ok(Some(t))
}
}
#[inline]
pub fn serialize_datetime_option<S>(t: &Option<Duration>, s: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(t) = t {
t.as_secs().to_string().serialize(s)
} else {
"".serialize(s)
}
}
#[inline]
fn timestamp_parse_from_str(ts: &str, fmt: &str) -> anyhow::Result<i64> {
let ndt = chrono::NaiveDateTime::parse_from_str(ts, fmt)?;
let ndt = ndt.and_local_timezone(*chrono::Local::now().offset());
match ndt {
LocalResult::None => Err(anyhow::Error::msg("Impossible")),
LocalResult::Single(d) => Ok(d.timestamp()),
LocalResult::Ambiguous(d, _tz) => Ok(d.timestamp()),
}
}
#[inline]
pub fn timestamp() -> Duration {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_else(|_| {
let now = chrono::Local::now();
Duration::new(now.timestamp() as u64, now.timestamp_subsec_nanos())
})
}
#[inline]
pub fn timestamp_secs() -> Timestamp {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|t| t.as_secs() as i64)
.unwrap_or_else(|_| chrono::Local::now().timestamp())
}
#[inline]
pub fn timestamp_millis() -> TimestampMillis {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|t| t.as_millis() as i64)
.unwrap_or_else(|_| chrono::Local::now().timestamp_millis())
}
#[inline]
pub fn format_timestamp(t: Timestamp) -> String {
if t <= 0 {
"".into()
} else {
use chrono::TimeZone;
if let chrono::LocalResult::Single(t) = chrono::Local.timestamp_opt(t, 0) {
t.format("%Y-%m-%d %H:%M:%S").to_string()
} else {
"".into()
}
}
}
#[inline]
pub fn format_timestamp_now() -> String {
format_timestamp(timestamp_secs())
}
#[inline]
pub fn format_timestamp_millis(t: TimestampMillis) -> String {
if t <= 0 {
"".into()
} else {
use chrono::TimeZone;
if let chrono::LocalResult::Single(t) = chrono::Local.timestamp_millis_opt(t) {
t.format("%Y-%m-%d %H:%M:%S%.3f").to_string()
} else {
"".into()
}
}
}
#[inline]
pub fn format_timestamp_millis_now() -> String {
format_timestamp_millis(timestamp_millis())
}
#[derive(Clone, Serialize)]
pub struct NodeAddr {
pub id: NodeId,
pub addr: Addr,
}
impl std::fmt::Debug for NodeAddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}@{:?}", self.id, self.addr)
}
}
impl FromStr for NodeAddr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('@').collect();
if parts.len() < 2 {
return Err(anyhow!(format!("NodeAddr format error, {}", s)));
}
let id = NodeId::from_str(parts[0])?;
let addr = Addr::from(parts[1]);
Ok(NodeAddr { id, addr })
}
}
impl<'de> de::Deserialize<'de> for NodeAddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
NodeAddr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom)
}
}
#[inline]
pub fn expand_env_vars(value: &str) -> String {
static ENV_PATTERN: once_cell::sync::Lazy<regex::Regex> = once_cell::sync::Lazy::new(|| {
regex::Regex::new(r"(?i)\$\{ENV:([A-Z0-9_]+)\}").expect("Invalid regex pattern")
});
ENV_PATTERN
.replace_all(value, |caps: ®ex::Captures| {
let env_name = &caps[1];
std::env::var(env_name).unwrap_or_else(|_| {
log::warn!("environment variable `{env_name}` not set");
String::new()
})
})
.into_owned()
}
#[inline]
pub fn deserialize_expand_env_vars<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
Ok(expand_env_vars(&v))
}
#[inline]
pub fn deserialize_expand_env_vars_option<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer).map(|s| expand_env_vars(&s)).map(|s| {
if s.is_empty() {
None
} else {
Some(s)
}
})
}