1use crate::segment::{AwsOperation, Http, Request, Response, Subsegment};
4
5pub trait Namespace {
7 fn name(&self, prefix: &str) -> String;
11
12 fn update_subsegment(&self, subsegment: &mut Subsegment);
14}
15
16#[derive(Debug)]
18pub struct AwsNamespace {
19 service: String,
20 operation: String,
21 request_id: Option<String>,
22 response_status: Option<u16>,
23}
24
25impl AwsNamespace {
26 pub fn new(service: impl Into<String>, operation: impl Into<String>) -> Self {
28 Self {
29 service: service.into(),
30 operation: operation.into(),
31 request_id: None,
32 response_status: None,
33 }
34 }
35
36 pub fn request_id(&mut self, request_id: impl Into<String>) -> &mut Self {
38 self.request_id = Some(request_id.into());
39 self
40 }
41
42 pub fn response_status(&mut self, status: u16) -> &mut Self {
44 self.response_status = Some(status);
45 self
46 }
47}
48
49impl Namespace for AwsNamespace {
50 fn name(&self, _prefix: &str) -> String {
51 self.service.clone()
52 }
53
54 fn update_subsegment(&self, subsegment: &mut Subsegment) {
55 if subsegment.namespace.is_none() {
56 subsegment.namespace = Some("aws".to_string());
57 }
58 if let Some(aws) = subsegment.aws.as_mut() {
59 if aws.operation.is_none() {
60 aws.operation = Some(self.operation.clone());
61 }
62 if aws.request_id.is_none() {
63 aws.request_id = self.request_id.clone();
64 }
65 } else {
66 subsegment.aws = Some(AwsOperation {
67 operation: Some(self.operation.clone()),
68 request_id: self.request_id.clone(),
69 ..AwsOperation::default()
70 });
71 }
72 if let Some(response_status) = self.response_status {
73 if let Some(http) = subsegment.http.as_mut() {
74 if let Some(response) = http.response.as_mut() {
75 if response.status.is_none() {
76 response.status = Some(response_status);
77 }
78 } else {
79 http.response = Some(Response {
80 status: Some(response_status),
81 ..Response::default()
82 });
83 }
84 } else {
85 subsegment.http = Some(Http {
86 response: Some(Response {
87 status: Some(response_status),
88 ..Response::default()
89 }),
90 ..Http::default()
91 });
92 }
93 }
94 }
95}
96
97#[derive(Debug)]
99pub struct RemoteNamespace {
100 name: String,
101 method: String,
102 url: String,
103 response_status: Option<u16>,
104}
105
106impl RemoteNamespace {
107 pub fn new(name: impl Into<String>, method: impl Into<String>, url: impl Into<String>) -> Self {
109 Self {
110 name: name.into(),
111 method: method.into(),
112 url: url.into(),
113 response_status: None,
114 }
115 }
116
117 pub fn response_status(&mut self, status: u16) -> &mut Self {
119 self.response_status = Some(status);
120 self
121 }
122}
123
124impl Namespace for RemoteNamespace {
125 fn name(&self, _prefix: &str) -> String {
126 self.name.clone()
127 }
128
129 fn update_subsegment(&self, subsegment: &mut Subsegment) {
130 if subsegment.namespace.is_none() {
131 subsegment.namespace = Some("remote".to_string());
132 }
133 if let Some(http) = subsegment.http.as_mut() {
134 if let Some(request) = http.request.as_mut() {
135 if request.method.is_none() {
136 request.method = Some(self.method.clone());
137 }
138 if request.url.is_none() {
139 request.url = Some(self.url.clone());
140 }
141 } else {
142 http.request = Some(Request {
143 url: Some(self.url.clone()),
144 method: Some(self.method.clone()),
145 ..Request::default()
146 });
147 }
148 } else {
149 subsegment.http = Some(Http {
150 request: Some(Request {
151 url: Some(self.url.clone()),
152 method: Some(self.method.clone()),
153 ..Request::default()
154 }),
155 ..Http::default()
156 });
157 }
158 if let Some(response_status) = self.response_status {
159 let http = subsegment.http.as_mut().expect("http must have been set");
160 if let Some(response) = http.response.as_mut() {
161 if response.status.is_none() {
162 response.status = Some(response_status);
163 }
164 } else {
165 http.response = Some(Response {
166 status: Some(response_status),
167 ..Response::default()
168 });
169 }
170 }
171 }
172}
173
174#[derive(Debug)]
176pub struct CustomNamespace {
177 name: String,
178}
179
180impl CustomNamespace {
181 pub fn new(name: impl Into<String>) -> Self {
183 Self { name: name.into() }
184 }
185}
186
187impl Namespace for CustomNamespace {
188 fn name(&self, prefix: &str) -> String {
189 format!("{}{}", prefix, self.name)
190 }
191
192 fn update_subsegment(&self, _subsegment: &mut Subsegment) {}
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn aws_namespace_should_have_service_name_as_name() {
202 let namespace = AwsNamespace::new("S3", "GetObject");
203 assert_eq!(namespace.name(""), "S3");
204 assert_eq!(namespace.name("prefix"), "S3");
205 }
206
207 #[test]
208 fn aws_namespace_should_update_subsegment_with_aws_operation() {
209 let namespace = AwsNamespace::new("S3", "GetObject");
210 let mut subsegment = Subsegment::default();
211 namespace.update_subsegment(&mut subsegment);
212 assert_eq!(subsegment.namespace.unwrap(), "aws");
213 assert_eq!(subsegment.aws.unwrap().operation.unwrap(), "GetObject");
214 }
215
216 #[test]
217 fn aws_namespace_should_update_subsegment_with_request_id() {
218 let mut namespace = AwsNamespace::new("S3", "GetObject");
219 namespace.request_id("12345");
220 let mut subsegment = Subsegment::default();
221 namespace.update_subsegment(&mut subsegment);
222 assert_eq!(subsegment.aws.unwrap().request_id.unwrap(), "12345");
223 }
224
225 #[test]
226 fn aws_namespace_should_update_subsegment_with_response_status() {
227 let mut namespace = AwsNamespace::new("S3", "GetObject");
228 namespace.response_status(200);
229 let mut subsegment = Subsegment::default();
230 namespace.update_subsegment(&mut subsegment);
231 assert_eq!(
232 subsegment
233 .http
234 .expect("http")
235 .response
236 .expect("response")
237 .status
238 .expect("status"),
239 200,
240 );
241 }
242
243 #[test]
244 fn remote_namespace_should_have_name_as_name() {
245 let namespace = RemoteNamespace::new("codemonger.io", "GET", "https://codemonger.io/");
246 assert_eq!(namespace.name(""), "codemonger.io");
247 assert_eq!(namespace.name("prefix"), "codemonger.io");
248 }
249
250 #[test]
251 fn remote_namespace_should_update_subsegment_with_remote_service() {
252 let namespace = RemoteNamespace::new("codemonger.io", "GET", "https://codemonger.io/");
253 let mut subsegment = Subsegment::default();
254 namespace.update_subsegment(&mut subsegment);
255 assert_eq!(subsegment.namespace.unwrap(), "remote");
256 let request = subsegment.http.expect("http").request.expect("request");
257 assert_eq!(request.method.unwrap(), "GET");
258 assert_eq!(request.url.unwrap(), "https://codemonger.io/");
259 }
260
261 #[test]
262 fn remote_namespace_should_update_subsegment_with_response_status() {
263 let mut namespace = RemoteNamespace::new("codemonger.io", "GET", "https://codemonger.io/");
264 namespace.response_status(200);
265 let mut subsegment = Subsegment::default();
266 namespace.update_subsegment(&mut subsegment);
267 assert_eq!(
268 subsegment
269 .http
270 .expect("http")
271 .response
272 .expect("response")
273 .status
274 .expect("status"),
275 200,
276 );
277 }
278
279 #[test]
280 fn custom_namespace_should_have_prefixed_name() {
281 let namespace = CustomNamespace::new("TestSubsegment");
282 assert_eq!(namespace.name(""), "TestSubsegment");
283 assert_eq!(namespace.name("prefix"), "prefixTestSubsegment");
284 }
285}