pub mod admin_stats;
pub mod client;
pub mod config;
pub mod hcstr;
pub mod protocol;
pub mod symbology;
pub mod version;
use clap::crate_version;
use const_format::concatcp;
pub const GIT_VERSION: &'static str = version::GIT_VERSION;
pub const GIT_VERSION_SHORT: &'static str = version::GIT_VERSION_SHORT;
pub const CARGO_AND_GIT_VERSION: &'static str =
concatcp!(crate_version!(), "-", GIT_VERSION_SHORT);
#[macro_export]
macro_rules! pool {
($vis:vis, $name:ident, $ty:ty, $max_capacity:expr, $max_elt_size:expr) => {
$vis fn $name() -> &'static netidx::pool::Pool<$ty> {
static POOL: once_cell::race::OnceBox<netidx::pool::Pool<$ty>> = once_cell::race::OnceBox::new();
POOL.get_or_init(|| Box::new(netidx::pool::Pool::new($max_capacity, $max_elt_size)))
}
};
($name:ident, $ty:ty, $max_capacity:expr, $max_elt_size:expr) => {
fn $name() -> &'static netidx::pool::Pool<$ty> {
static POOL: once_cell::race::OnceBox<netidx::pool::Pool<$ty>> = once_cell::race::OnceBox::new();
POOL.get_or_init(|| Box::new(netidx::pool::Pool::new($max_capacity, $max_elt_size)))
}
}
}
#[macro_export]
macro_rules! packed_value {
($name:ident) => {
impl Into<netidx::protocol::value::Value> for $name {
fn into(self) -> netidx::protocol::value::Value {
netidx::protocol::value::Value::Bytes(
netidx::utils::pack(&self).unwrap().freeze(),
)
}
}
impl netidx::protocol::value::FromValue for $name {
fn from_value(v: netidx::protocol::value::Value) -> anyhow::Result<Self> {
match v {
netidx::protocol::value::Value::Bytes(mut b) => {
Ok(netidx::pack::Pack::decode(&mut b)?)
}
_ => anyhow::bail!("invalid value, expected a bytes {:?}", v),
}
}
}
};
}
#[macro_export]
macro_rules! uuid_val {
($name:ident) => {
#[derive(
Debug,
Clone,
Copy,
Hash,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pack,
)]
pub struct $name(pub uuid::Uuid);
packed_value!($name);
impl Deref for $name {
type Target = uuid::Uuid;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<uuid::Uuid> for $name {
fn borrow(&self) -> &uuid::Uuid {
&self.0
}
}
impl JsonSchema for $name {
fn schema_name() -> String {
format!("{}Id", stringify!($name)).to_string()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
uuid::Uuid::json_schema(gen)
}
}
};
}
#[macro_export]
macro_rules! hcstrid {
($name:ident) => {
paste::paste! {
#[derive(Debug)]
pub struct [< $name Inner >] {
pub name: Str,
pub id: uuid::Uuid,
}
#[derive(Debug, Clone, Copy)]
pub struct $name(&'static [< $name Inner >]);
static [<$name:upper S>]: once_cell::sync::Lazy<
arc_swap::ArcSwap<immutable_chunkmap::map::MapM<Str, $name>>
> =
once_cell::sync::Lazy::new(|| {
let map = std::sync::Arc::new(immutable_chunkmap::map::MapM::new());
arc_swap::ArcSwap::new(map)
});
impl $name {
pub fn get(name: &str) -> $name {
loop {
let current = [<$name:upper S>].load();
match current.get(name) {
Some(v) => break *v,
None => {
let name = if name.as_bytes().len() > 255 {
let mut i = 255;
while !name.is_char_boundary(i) {
i -= 1;
}
name.split_at(i).0
} else {
name
};
let name = Str::try_from(name).unwrap();
let ns = uuid::Uuid::from_bytes([
177, 37, 16, 167, 166, 29, 72, 91, 172, 51, 244, 227, 105, 166, 160, 67,
]);
let id = uuid::Uuid::new_v5(&ns, name.as_bytes());
let desk = $name(Box::leak(Box::new([<$name Inner>] { name, id })));
let (new, _) = current.insert(name, desk);
let new = std::sync::Arc::new(new);
let replaced = [<$name:upper S>].compare_and_swap(current, std::sync::Arc::clone(&new));
if std::sync::Arc::ptr_eq(&replaced, &new) {
break desk
}
}
}
}
}
pub fn all() -> std::sync::Arc<immutable_chunkmap::map::MapM<Str, $name>> {
[<$name:upper S>].load_full()
}
}
impl std::ops::Deref for $name {
type Target = [<$name Inner>];
fn deref(&self) -> &'static [<$name Inner>] {
self.0
}
}
impl Symbolic for $name {
type Id = uuid::Uuid;
fn name(&self) -> Str {
self.0.name
}
fn id(&self) -> Self::Id {
self.0.id
}
fn is_valid(&self) -> bool {
true
}
}
impl std::cmp::PartialEq for $name {
fn eq(&self, lhs: &Self) -> bool {
(self.0 as *const [<$name Inner>]) == (lhs.0 as *const [<$name Inner>])
}
}
impl std::cmp::Eq for $name {}
impl std::cmp::PartialOrd for $name {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(self.0 as *const [<$name Inner>]).partial_cmp(&(other.0 as *const [<$name Inner>]))
}
}
impl std::cmp::Ord for $name {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.0 as *const [<$name Inner>]).cmp(&(other.0 as *const [<$name Inner>]))
}
}
impl std::hash::Hash for $name {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self.0 as *const [<$name Inner>]).hash(state)
}
}
impl netidx::pack::Pack for $name {
fn encoded_len(&self) -> usize {
let len = self.name.as_bytes().len();
netidx::pack::varint_len(len as u64) + len
}
fn encode(&self, buf: &mut impl bytes::BufMut) -> Result<(), netidx::pack::PackError> {
netidx::pack::encode_varint(self.name.as_bytes().len() as u64, buf);
Ok(buf.put_slice(self.name.as_bytes()))
}
fn decode(buf: &mut impl bytes::Buf) -> Result<Self, netidx::pack::PackError> {
let len = netidx::pack::decode_varint(buf)? as usize;
if len > buf.remaining() {
return Err(netidx::pack::PackError::TooBig)
}
thread_local! {
static NAME: std::cell::RefCell<bytes::BytesMut> =
std::cell::RefCell::new(bytes::BytesMut::new());
}
NAME.with(|name| {
let mut name = name.borrow_mut();
name.resize(len, 0);
buf.copy_to_slice(&mut name);
let s = match std::str::from_utf8(&*name) {
Ok(s) => s,
Err(_) => return Err(netidx::pack::PackError::InvalidFormat)
};
Ok($name::get(s))
})
}
}
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where S: serde::Serializer,
{
serializer.serialize_str(self.name.as_str())
}
}
impl JsonSchema for $name {
fn schema_name() -> String {
stringify!($name).to_string()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
String::json_schema(gen)
}
}
struct [<$name Visitor>];
impl<'de> serde::de::Visitor<'de> for [<$name Visitor>] {
type Value = $name;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "expecting a string")
}
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
where E: serde::de::Error
{
Ok($name::get(v))
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
where D: serde::Deserializer<'de>,
{
deserializer.deserialize_str([<$name Visitor>])
}
}
}
}
}
pub fn parse_duration(s: &str) -> anyhow::Result<chrono::Duration> {
use anyhow::anyhow;
use chrono::Duration;
if s.ends_with("ns") {
let s = s.strip_suffix("ns").unwrap().trim();
let n = s.parse::<i64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::nanoseconds(n))
} else if s.ends_with("us") {
let s = s.strip_suffix("us").unwrap().trim();
let n = s.parse::<i64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::microseconds(n))
} else if s.ends_with("ms") {
let s = s.strip_suffix("ms").unwrap().trim();
let n = s.parse::<i64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::milliseconds(n))
} else if s.ends_with("s") {
let s = s.strip_suffix("s").unwrap().trim();
let f = s.parse::<f64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::nanoseconds((f * 1e9).trunc() as i64))
} else if s.ends_with("m") {
let s = s.strip_suffix("m").unwrap().trim();
let f = s.parse::<f64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::nanoseconds((f * 60. * 1e9).trunc() as i64))
} else if s.ends_with("h") {
let s = s.strip_suffix("h").unwrap().trim();
let f = s.parse::<f64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::nanoseconds((f * 3600. * 1e9).trunc() as i64))
} else if s.ends_with("d") {
let s = s.strip_suffix("d").unwrap().trim();
let f = s.parse::<f64>().map_err(|e| anyhow!(e.to_string()))?;
Ok(Duration::nanoseconds((f * 86400. * 1e9).trunc() as i64))
} else {
Err(anyhow!("expected a suffix ns, us, ms, s, m, h, d"))
}
}
pub struct DurationVisitor;
impl<'de> serde::de::Visitor<'de> for DurationVisitor {
type Value = chrono::Duration;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "expecting a string")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
parse_duration(s).map_err(|e| E::custom(e.to_string()))
}
}
pub fn deserialize_duration<'de, D>(d: D) -> Result<chrono::Duration, D::Error>
where
D: serde::Deserializer<'de>,
{
d.deserialize_str(DurationVisitor)
}
pub fn deserialize_duration_opt<'de, D>(
d: D,
) -> Result<Option<chrono::Duration>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let s = Option::<String>::deserialize(d)?;
match s {
Some(s) => Ok(Some(parse_duration(&s).map_err(serde::de::Error::custom)?)),
None => Ok(None),
}
}
pub fn serialize_duration<S>(d: &chrono::Duration, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let secs = d.num_milliseconds() as f64 / 1000.;
s.serialize_str(&format!("{}s", secs))
}
pub fn serialize_duration_opt<S>(
d: &Option<chrono::Duration>,
s: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match d {
Some(d) => {
let secs = d.num_milliseconds() as f64 / 1000.;
s.serialize_some(&format!("{}s", secs))
}
None => s.serialize_none(),
}
}