Monorepo consolidation: workspace, shared types, transport plans, docker/swam assets
This commit is contained in:
196
shared/src/lib.rs
Normal file
196
shared/src/lib.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const HEADER_X_CORRELATION_ID: &str = "x-correlation-id";
|
||||
pub const HEADER_TRACEPARENT: &str = "traceparent";
|
||||
pub const HEADER_TRACE_ID: &str = "trace-id";
|
||||
pub const NATS_HEADER_CORRELATION_ID: &str = "correlation-id";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
|
||||
pub struct TenantId(String);
|
||||
|
||||
impl TenantId {
|
||||
pub fn new(id: impl Into<String>) -> Self {
|
||||
Self(id.into())
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TenantId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TenantId {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for TenantId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct CorrelationId(String);
|
||||
|
||||
impl CorrelationId {
|
||||
pub fn new(id: impl Into<String>) -> Self {
|
||||
Self(id.into())
|
||||
}
|
||||
|
||||
pub fn generate() -> Self {
|
||||
Self(Uuid::new_v4().to_string())
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CorrelationId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CorrelationId {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for CorrelationId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct TraceId(String);
|
||||
|
||||
impl TraceId {
|
||||
pub fn new(id: impl Into<String>) -> Self {
|
||||
Self(id.into())
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn is_valid_hex_32(&self) -> bool {
|
||||
is_valid_hex_32(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TraceId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TraceId {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for TraceId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trace_id_from_traceparent(traceparent: &str) -> Option<&str> {
|
||||
let mut parts = traceparent.split('-');
|
||||
let version = parts.next()?;
|
||||
let trace_id = parts.next()?;
|
||||
let span_id = parts.next()?;
|
||||
let flags = parts.next()?;
|
||||
if version.len() != 2 || trace_id.len() != 32 || span_id.len() != 16 || flags.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
if !trace_id.chars().all(|c| c.is_ascii_hexdigit())
|
||||
|| !span_id.chars().all(|c| c.is_ascii_hexdigit())
|
||||
|| !flags.chars().all(|c| c.is_ascii_hexdigit())
|
||||
|| !version.chars().all(|c| c.is_ascii_hexdigit())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(trace_id)
|
||||
}
|
||||
|
||||
pub fn traceparent_from_trace_id(trace_id: &TraceId) -> Option<String> {
|
||||
if !trace_id.is_valid_hex_32() {
|
||||
return None;
|
||||
}
|
||||
let span_id = Uuid::new_v4().simple().to_string()[..16].to_string();
|
||||
Some(format!("00-{}-{span_id}-01", trace_id.as_str()))
|
||||
}
|
||||
|
||||
fn is_valid_hex_32(s: &str) -> bool {
|
||||
s.len() == 32 && s.chars().all(|c| c.is_ascii_hexdigit())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tenant_id_serialization_roundtrip() {
|
||||
let id = TenantId::new("acme-corp");
|
||||
let json = serde_json::to_string(&id).unwrap();
|
||||
let decoded: TenantId = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(id, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tenant_id_default_is_empty() {
|
||||
let id = TenantId::default();
|
||||
assert!(id.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tenant_id_is_send_sync() {
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
assert_send_sync::<TenantId>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correlation_id_roundtrip_is_string() {
|
||||
let id = CorrelationId::new("corr-1");
|
||||
let json = serde_json::to_string(&id).unwrap();
|
||||
assert_eq!(json, "\"corr-1\"");
|
||||
let decoded: CorrelationId = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(decoded.as_str(), "corr-1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trace_id_from_traceparent_parses() {
|
||||
let tp = "00-0123456789abcdef0123456789abcdef-1111111111111111-01";
|
||||
assert_eq!(
|
||||
trace_id_from_traceparent(tp),
|
||||
Some("0123456789abcdef0123456789abcdef")
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user