sentinel_common/
ids.rs

1//! Type-safe identifier newtypes for Sentinel proxy.
2//!
3//! These types provide compile-time safety for identifiers, preventing
4//! accidental mixing of different ID types (e.g., passing a RouteId
5//! where an UpstreamId is expected).
6
7use serde::{Deserialize, Serialize};
8use std::fmt;
9use uuid::Uuid;
10
11/// Unique correlation ID for request tracing across components.
12///
13/// Correlation IDs follow requests through the entire proxy pipeline,
14/// enabling end-to-end tracing and log correlation.
15#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub struct CorrelationId(String);
17
18impl CorrelationId {
19    /// Create a new random correlation ID
20    pub fn new() -> Self {
21        Self(Uuid::new_v4().to_string())
22    }
23
24    /// Create from an existing string
25    pub fn from_string(s: impl Into<String>) -> Self {
26        Self(s.into())
27    }
28
29    /// Get the inner string value
30    pub fn as_str(&self) -> &str {
31        &self.0
32    }
33
34    /// Convert to owned String
35    pub fn into_string(self) -> String {
36        self.0
37    }
38}
39
40impl Default for CorrelationId {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl fmt::Display for CorrelationId {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "{}", self.0)
49    }
50}
51
52impl From<String> for CorrelationId {
53    fn from(s: String) -> Self {
54        Self(s)
55    }
56}
57
58impl From<&str> for CorrelationId {
59    fn from(s: &str) -> Self {
60        Self(s.to_string())
61    }
62}
63
64/// Unique request ID for internal tracking.
65///
66/// Request IDs are generated per-request and used for internal
67/// metrics, logging, and debugging.
68#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
69pub struct RequestId(String);
70
71impl RequestId {
72    /// Create a new random request ID
73    pub fn new() -> Self {
74        Self(Uuid::new_v4().to_string())
75    }
76
77    /// Get the inner string value
78    pub fn as_str(&self) -> &str {
79        &self.0
80    }
81}
82
83impl Default for RequestId {
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89impl fmt::Display for RequestId {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "{}", self.0)
92    }
93}
94
95/// Route identifier.
96///
97/// Identifies a configured route in the proxy. Routes define
98/// how requests are matched and forwarded to upstreams.
99#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
100pub struct RouteId(String);
101
102impl RouteId {
103    pub fn new(id: impl Into<String>) -> Self {
104        Self(id.into())
105    }
106
107    pub fn as_str(&self) -> &str {
108        &self.0
109    }
110}
111
112impl fmt::Display for RouteId {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(f, "{}", self.0)
115    }
116}
117
118/// Upstream identifier.
119///
120/// Identifies a configured upstream pool. Upstreams are groups
121/// of backend servers that handle requests.
122#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
123pub struct UpstreamId(String);
124
125impl UpstreamId {
126    pub fn new(id: impl Into<String>) -> Self {
127        Self(id.into())
128    }
129
130    pub fn as_str(&self) -> &str {
131        &self.0
132    }
133}
134
135impl fmt::Display for UpstreamId {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        write!(f, "{}", self.0)
138    }
139}
140
141/// Agent identifier.
142///
143/// Identifies a configured external processing agent (WAF, auth, etc.).
144#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
145pub struct AgentId(String);
146
147impl AgentId {
148    pub fn new(id: impl Into<String>) -> Self {
149        Self(id.into())
150    }
151
152    pub fn as_str(&self) -> &str {
153        &self.0
154    }
155}
156
157impl fmt::Display for AgentId {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        write!(f, "{}", self.0)
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn test_correlation_id() {
169        let id1 = CorrelationId::new();
170        let id2 = CorrelationId::from_string("test-id");
171
172        assert_ne!(id1, id2);
173        assert_eq!(id2.as_str(), "test-id");
174    }
175
176    #[test]
177    fn test_route_id() {
178        let id = RouteId::new("my-route");
179        assert_eq!(id.as_str(), "my-route");
180        assert_eq!(id.to_string(), "my-route");
181    }
182
183    #[test]
184    fn test_upstream_id() {
185        let id = UpstreamId::new("backend-pool");
186        assert_eq!(id.as_str(), "backend-pool");
187    }
188
189    #[test]
190    fn test_agent_id() {
191        let id = AgentId::new("waf-agent");
192        assert_eq!(id.as_str(), "waf-agent");
193    }
194}