1use crate::{
2 borrowed::raw::{
3 Logline as RawLogline, UnvalidatedLogline as UnvalidatedRaw,
4 ValidatedLogline as ValidatedRaw,
5 },
6 shared::*,
7 types::*,
8};
9
10pub type ValidatedLogline<'a> = Logline<'a, Validated>;
43
44pub type UnvalidatedLogline<'a> = Logline<'a, Unvalidated>;
80
81#[must_use]
85#[derive(Debug, Clone, PartialEq)]
86pub struct Logline<'a, V> {
87 pub date: &'a str,
88 pub time: &'a str,
89 pub x_edge_location: &'a str,
90 pub sc_bytes: u64,
91 pub c_ip: IpAddr,
92 pub cs_method: &'a str,
93 pub cs_host: &'a str,
94 pub cs_uri_stem: &'a str,
95 pub sc_status: u16,
96 pub cs_referer: Option<&'a str>,
97 pub cs_user_agent: &'a str,
98 pub cs_uri_query: Option<&'a str>,
99 pub cs_cookie: Option<&'a str>,
100 pub x_edge_result_type: EdgeResultType,
101 pub x_edge_request_id: &'a str,
102 pub x_host_header: &'a str,
103 pub cs_protocol: CsProtocol,
104 pub cs_bytes: u64,
105 pub time_taken: Duration,
106 pub x_forwarded_for: Option<ForwardedForAddrs>,
107 pub ssl_protocol: Option<SslProtocol>,
108 pub ssl_cipher: Option<&'a str>,
109 pub x_edge_response_result_type: EdgeResultType,
110 pub cs_protocol_version: CsProtocolVersion,
111 pub fle_status: Option<&'a str>,
112 pub fle_encrypted_fields: Option<u64>,
113 pub c_port: u16,
114 pub time_to_first_byte: Duration,
115 pub x_edge_detailed_result_type: DetailedEdgeResultType,
116 pub sc_content_type: Option<&'a str>,
117 pub sc_content_len: Option<u64>,
118 pub sc_range_start: Option<i64>, pub sc_range_end: Option<i64>, __marker: PhantomData<V>,
121}
122
123impl<'a> TryFrom<&'a str> for Logline<'a, Validated> {
127 type Error = &'static str;
128
129 fn try_from(line: &'a str) -> Result<Self, Self::Error> {
130 validate_line(line)?;
131 new_log_line(line)
132 }
133}
134
135impl<'a> TryFrom<&'a str> for Logline<'a, Unvalidated> {
136 type Error = &'static str;
137
138 fn try_from(line: &'a str) -> Result<Self, Self::Error> {
139 new_log_line(line)
140 }
141}
142
143fn new_log_line<V>(line: &str) -> Result<Logline<'_, V>, &'static str> {
144 let mut iter = MemchrTabSplitter::new(line);
145
146 let line = Logline {
147 date: iter.next().unwrap(),
148 time: iter.next().unwrap(),
149 x_edge_location: iter.next().unwrap(),
150 sc_bytes: iter
151 .next()
152 .unwrap()
153 .parse()
154 .map_err(|_e| "sc_bytes invalid")?,
155 c_ip: iter.next().unwrap().parse().map_err(|_e| "c_ip invalid")?,
156 cs_method: iter.next().unwrap(),
157 cs_host: iter.next().unwrap(),
158 cs_uri_stem: iter.next().unwrap(),
159 sc_status: iter
160 .next()
161 .unwrap()
162 .parse()
163 .map_err(|_e| "sc_status invalid")?,
164 cs_referer: iter.next().unwrap().as_optional_str(),
165 cs_user_agent: iter.next().unwrap(),
166 cs_uri_query: iter.next().unwrap().as_optional_str(),
167 cs_cookie: iter.next().unwrap().as_optional_str(),
168 x_edge_result_type: iter
169 .next()
170 .unwrap()
171 .parse()
172 .map_err(|_e| "x_edge_result_type invalid")?,
173 x_edge_request_id: iter.next().unwrap(),
174 x_host_header: iter.next().unwrap(),
175 cs_protocol: iter
176 .next()
177 .unwrap()
178 .parse()
179 .map_err(|_e| "cs_protocol invalid")?,
180 cs_bytes: iter
181 .next()
182 .unwrap()
183 .parse()
184 .map_err(|_e| "cs_bytes invalid")?,
185 time_taken: iter
186 .next()
187 .unwrap()
188 .parse::<f64>()
189 .map(Duration::from_secs_f64)
190 .map_err(|_e| "time_taken invalid")?,
191 x_forwarded_for: iter
192 .next()
193 .and_then(as_optional_t)
194 .transpose()
195 .map_err(|_e| "x_forwarded_for invalid")?,
196 ssl_protocol: iter
197 .next()
198 .and_then(as_optional_t)
199 .transpose()
200 .map_err(|_e| "ssl_protocol invalid")?,
201 ssl_cipher: iter.next().unwrap().as_optional_str(),
202 x_edge_response_result_type: iter
203 .next()
204 .unwrap()
205 .parse()
206 .map_err(|_e| "x_edge_response_result_type invalid")?,
207 cs_protocol_version: iter
208 .next()
209 .unwrap()
210 .parse()
211 .map_err(|_e| "cs_protocol_version invalid")?,
212 fle_status: iter.next().unwrap().as_optional_str(),
213 fle_encrypted_fields: iter
214 .next()
215 .and_then(as_optional_t)
216 .transpose()
217 .map_err(|_e| "fle_encrypted_fields invalid")?,
218 c_port: iter
219 .next()
220 .unwrap()
221 .parse()
222 .map_err(|_e| "c_port invalid")?,
223 time_to_first_byte: iter
224 .next()
225 .unwrap()
226 .parse::<f64>()
227 .map(Duration::from_secs_f64)
228 .map_err(|_e| "time_to_first_byte invalid")?,
229 x_edge_detailed_result_type: iter
230 .next()
231 .unwrap()
232 .parse()
233 .map_err(|_e| "x_edge_detailed_result_type invalid")?,
234 sc_content_type: iter.next().unwrap().as_optional_str(),
235 sc_content_len: iter
236 .next()
237 .and_then(as_optional_t)
238 .transpose()
239 .map_err(|_e| "sc_content_len invalid")?,
240 sc_range_start: iter
241 .next()
242 .and_then(as_optional_t)
243 .transpose()
244 .map_err(|_e| "sc_range_start invalid")?,
245 sc_range_end: iter
246 .next()
247 .and_then(as_optional_t)
248 .transpose()
249 .map_err(|_e| "sc_range_end invalid")?,
250 __marker: PhantomData,
251 };
252 Ok(line)
253}
254
255impl<'a> TryFrom<ValidatedRaw<'a>> for Logline<'a, Validated> {
256 type Error = &'static str;
257
258 fn try_from(raw: ValidatedRaw<'a>) -> Result<Self, Self::Error> {
259 try_from_v(raw)
260 }
261}
262
263impl<'a> TryFrom<UnvalidatedRaw<'a>> for Logline<'a, Unvalidated> {
264 type Error = &'static str;
265
266 fn try_from(raw: UnvalidatedRaw<'a>) -> Result<Self, Self::Error> {
267 try_from_v(raw)
268 }
269}
270
271fn try_from_v<V>(raw: RawLogline<'_, V>) -> Result<Logline<'_, V>, &'static str> {
272 let line = Logline {
273 date: raw.date,
274 time: raw.time,
275 x_edge_location: raw.x_edge_location,
276 sc_bytes: raw.sc_bytes.parse().map_err(|_e| "sc_bytes invalid")?,
277 c_ip: raw.c_ip.parse().map_err(|_e| "c_ip invalid")?,
278 cs_method: raw.cs_method,
279 cs_host: raw.cs_host,
280 cs_uri_stem: raw.cs_uri_stem,
281 sc_status: raw.sc_status.parse().map_err(|_e| "sc_status invalid")?,
282 cs_referer: raw.cs_referer.as_optional_str(),
283 cs_user_agent: raw.cs_user_agent,
284 cs_uri_query: raw.cs_uri_query.as_optional_str(),
285 cs_cookie: raw.cs_cookie.as_optional_str(),
286 x_edge_result_type: raw
287 .x_edge_result_type
288 .parse()
289 .map_err(|_e| "x_edge_result_type invalid")?,
290 x_edge_request_id: raw.x_edge_request_id,
291 x_host_header: raw.x_host_header,
292 cs_protocol: raw
293 .cs_protocol
294 .parse()
295 .map_err(|_e| "cs_protocol invalid")?,
296 cs_bytes: raw.cs_bytes.parse().map_err(|_e| "cs_bytes invalid")?,
297 time_taken: raw
298 .time_taken
299 .parse::<f64>()
300 .map(Duration::from_secs_f64)
301 .map_err(|_e| "time_taken invalid")?,
302 x_forwarded_for: parse_as_option(raw.x_forwarded_for)
303 .map_err(|_e| "x_forwarded_for invalid")?,
304 ssl_protocol: parse_as_option(raw.ssl_protocol).map_err(|_e| "ssl_protocol invalid")?,
305 ssl_cipher: raw.ssl_cipher.as_optional_str(),
306 x_edge_response_result_type: raw
307 .x_edge_response_result_type
308 .parse()
309 .map_err(|_e| "x_edge_response_result_type invalid")?,
310 cs_protocol_version: raw
311 .cs_protocol_version
312 .parse()
313 .map_err(|_e| "cs_protocol_version invalid")?,
314 fle_status: raw.fle_status.as_optional_str(),
315 fle_encrypted_fields: parse_as_option(raw.fle_encrypted_fields)
316 .map_err(|_e| "fle_encrypted_fields invalid")?,
317 c_port: raw.c_port.parse().map_err(|_e| "c_port invalid")?,
318 time_to_first_byte: raw
319 .time_to_first_byte
320 .parse::<f64>()
321 .map(Duration::from_secs_f64)
322 .map_err(|_e| "time_to_first_byte invalid")?,
323 x_edge_detailed_result_type: raw
324 .x_edge_detailed_result_type
325 .parse()
326 .map_err(|_e| "x_edge_detailed_result_type invalid")?,
327 sc_content_type: raw.sc_content_type.as_optional_str(),
328 sc_content_len: parse_as_option(raw.sc_content_len)
329 .map_err(|_e| "sc_content_len invalid")?,
330 sc_range_start: parse_as_option(raw.sc_range_start)
331 .map_err(|_e| "sc_range_start invalid")?,
332 sc_range_end: parse_as_option(raw.sc_range_end).map_err(|_e| "sc_range_end invalid")?,
333 __marker: PhantomData,
334 };
335 Ok(line)
336}
337
338impl<'a> From<Logline<'a, Validated>> for Logline<'a, Unvalidated> {
339 fn from(validated: Logline<'a, Validated>) -> Self {
340 Logline {
341 date: validated.date,
342 time: validated.time,
343 x_edge_location: validated.x_edge_location,
344 sc_bytes: validated.sc_bytes,
345 c_ip: validated.c_ip,
346 cs_method: validated.cs_method,
347 cs_host: validated.cs_host,
348 cs_uri_stem: validated.cs_uri_stem,
349 sc_status: validated.sc_status,
350 cs_referer: validated.cs_referer,
351 cs_user_agent: validated.cs_user_agent,
352 cs_uri_query: validated.cs_uri_query,
353 cs_cookie: validated.cs_cookie,
354 x_edge_result_type: validated.x_edge_result_type,
355 x_edge_request_id: validated.x_edge_request_id,
356 x_host_header: validated.x_host_header,
357 cs_protocol: validated.cs_protocol,
358 cs_bytes: validated.cs_bytes,
359 time_taken: validated.time_taken,
360 x_forwarded_for: validated.x_forwarded_for,
361 ssl_protocol: validated.ssl_protocol,
362 ssl_cipher: validated.ssl_cipher,
363 x_edge_response_result_type: validated.x_edge_response_result_type,
364 cs_protocol_version: validated.cs_protocol_version,
365 fle_status: validated.fle_status,
366 fle_encrypted_fields: validated.fle_encrypted_fields,
367 c_port: validated.c_port,
368 time_to_first_byte: validated.time_to_first_byte,
369 x_edge_detailed_result_type: validated.x_edge_detailed_result_type,
370 sc_content_type: validated.sc_content_type,
371 sc_content_len: validated.sc_content_len,
372 sc_range_start: validated.sc_range_start,
373 sc_range_end: validated.sc_range_end,
374 __marker: PhantomData,
375 }
376 }
377}
378
379impl<'a> From<Logline<'a, Unvalidated>> for Logline<'a, Validated> {
380 fn from(unvalidated: Logline<'a, Unvalidated>) -> Self {
381 Logline {
382 date: unvalidated.date,
383 time: unvalidated.time,
384 x_edge_location: unvalidated.x_edge_location,
385 sc_bytes: unvalidated.sc_bytes,
386 c_ip: unvalidated.c_ip,
387 cs_method: unvalidated.cs_method,
388 cs_host: unvalidated.cs_host,
389 cs_uri_stem: unvalidated.cs_uri_stem,
390 sc_status: unvalidated.sc_status,
391 cs_referer: unvalidated.cs_referer,
392 cs_user_agent: unvalidated.cs_user_agent,
393 cs_uri_query: unvalidated.cs_uri_query,
394 cs_cookie: unvalidated.cs_cookie,
395 x_edge_result_type: unvalidated.x_edge_result_type,
396 x_edge_request_id: unvalidated.x_edge_request_id,
397 x_host_header: unvalidated.x_host_header,
398 cs_protocol: unvalidated.cs_protocol,
399 cs_bytes: unvalidated.cs_bytes,
400 time_taken: unvalidated.time_taken,
401 x_forwarded_for: unvalidated.x_forwarded_for,
402 ssl_protocol: unvalidated.ssl_protocol,
403 ssl_cipher: unvalidated.ssl_cipher,
404 x_edge_response_result_type: unvalidated.x_edge_response_result_type,
405 cs_protocol_version: unvalidated.cs_protocol_version,
406 fle_status: unvalidated.fle_status,
407 fle_encrypted_fields: unvalidated.fle_encrypted_fields,
408 c_port: unvalidated.c_port,
409 time_to_first_byte: unvalidated.time_to_first_byte,
410 x_edge_detailed_result_type: unvalidated.x_edge_detailed_result_type,
411 sc_content_type: unvalidated.sc_content_type,
412 sc_content_len: unvalidated.sc_content_len,
413 sc_range_start: unvalidated.sc_range_start,
414 sc_range_end: unvalidated.sc_range_end,
415 __marker: PhantomData,
416 }
417 }
418}