1use http::{Extensions, HeaderMap};
4
5#[derive(Debug, Clone)]
7pub struct TenantId(pub String);
8
9#[derive(Debug, Clone)]
11pub struct TenantConfig {
12 pub header_name: String,
14 pub required: bool,
16 pub fallback: Option<String>,
18}
19
20impl Default for TenantConfig {
21 fn default() -> Self {
22 Self {
23 header_name: "x-tenant-id".to_string(),
24 required: false,
25 fallback: None,
26 }
27 }
28}
29
30impl TenantConfig {
31 #[must_use]
33 pub fn tenant_from_headers(&self, headers: &HeaderMap) -> Option<TenantId> {
34 headers
35 .get(&self.header_name)
36 .and_then(|value| value.to_str().ok())
37 .map(|value| TenantId(value.to_owned()))
38 .or_else(|| self.fallback.clone().map(TenantId))
39 }
40
41 #[must_use]
43 pub const fn is_required(&self) -> bool {
44 self.required
45 }
46}
47
48pub fn set_tenant_in_extensions(extensions: &mut Extensions, tenant_id: impl Into<String>) {
52 extensions.insert(TenantId(tenant_id.into()));
53}
54
55pub fn tenant_from_extensions(extensions: &Extensions) -> Option<&TenantId> {
59 extensions.get::<TenantId>()
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_tenant_config_default() {
68 let cfg = TenantConfig::default();
69 assert_eq!(cfg.header_name, "x-tenant-id");
70 assert!(!cfg.required);
71 assert!(cfg.fallback.is_none());
72 }
73
74 #[test]
75 fn test_tenant_config_with_fallback() {
76 let cfg = TenantConfig {
77 header_name: "x-tenant-id".to_string(),
78 required: false,
79 fallback: Some("default".to_string()),
80 };
81 assert_eq!(cfg.fallback, Some("default".to_string()));
82 }
83
84 #[test]
85 fn test_tenant_from_headers() {
86 let mut headers = HeaderMap::new();
87 headers.insert("x-tenant-id", "tenant-123".parse().unwrap());
88 let tenant = TenantConfig::default().tenant_from_headers(&headers);
89 assert_eq!(tenant.unwrap().0, "tenant-123");
90 }
91
92 #[test]
93 fn test_tenant_from_headers_uses_fallback() {
94 let cfg = TenantConfig {
95 fallback: Some("default".to_string()),
96 ..Default::default()
97 };
98 assert_eq!(
99 cfg.tenant_from_headers(&HeaderMap::new()).unwrap().0,
100 "default"
101 );
102 }
103
104 #[test]
107 fn test_set_and_get_tenant_in_extensions() {
108 let mut ext = Extensions::new();
109 set_tenant_in_extensions(&mut ext, "org-42");
110
111 let tenant = tenant_from_extensions(&ext);
112 assert!(tenant.is_some());
113 assert_eq!(tenant.unwrap().0, "org-42");
114 }
115
116 #[test]
117 fn test_tenant_from_extensions_empty() {
118 let ext = Extensions::new();
119 assert!(tenant_from_extensions(&ext).is_none());
120 }
121
122 #[test]
123 fn test_set_tenant_overwrites_previous() {
124 let mut ext = Extensions::new();
125 set_tenant_in_extensions(&mut ext, "first");
126 set_tenant_in_extensions(&mut ext, "second");
127 assert_eq!(tenant_from_extensions(&ext).unwrap().0, "second");
128 }
129}