1use crate::{shared::*, types::*, CheckedRawLogLine, TIME_DATE_FMT, TIME_TIME_FMT};
2
3pub use LogLine as TypedLogLine;
4
5#[derive(Debug, PartialEq)]
10pub struct LogLine {
11 pub date: Date,
12 pub time: Time,
13 pub datetime: OffsetDateTime,
14 pub x_edge_location: String,
15 pub sc_bytes: u64,
16 pub c_ip: IpAddr,
17 pub cs_method: String,
18 pub cs_host: String,
19 pub cs_uri_stem: String, pub sc_status: u16, pub cs_referer: Option<String>,
22 pub cs_user_agent: String,
23 pub cs_uri_query: Option<String>, pub cs_cookie: Option<String>,
25 pub x_edge_result_type: EdgeResultType,
26 pub x_edge_request_id: String,
27 pub x_host_header: String,
28 pub cs_protocol: CsProtocol,
29 pub cs_bytes: u64,
30 pub time_taken: Duration,
31 pub x_forwarded_for: Option<ForwardedForAddrs>,
32 pub ssl_protocol: Option<SslProtocol>,
33 pub ssl_cipher: Option<String>, pub x_edge_response_result_type: EdgeResultType,
35 pub cs_protocol_version: CsProtocolVersion,
36 pub fle_status: Option<String>, pub fle_encrypted_fields: Option<u64>,
38 pub c_port: u16,
39 pub time_to_first_byte: Duration,
40 pub x_edge_detailed_result_type: DetailedEdgeResultType,
41 pub sc_content_type: String, pub sc_content_len: u64,
43 pub sc_range_start: Option<i64>,
44 pub sc_range_end: Option<i64>,
45}
46
47impl<'a> TryFrom<&'a str> for LogLine {
53 type Error = &'static str;
54
55 fn try_from(line: &'a str) -> Result<Self, Self::Error> {
56 validate_line(line)?;
57
58 let mut iter = MemchrTabSplitter::new(line);
59
60 let date = Date::parse(iter.next().unwrap(), TIME_DATE_FMT).map_err(|_e| "date invalid")?;
61 let time = Time::parse(iter.next().unwrap(), TIME_TIME_FMT).map_err(|_e| "time invalid")?;
62 let datetime = OffsetDateTime::new_utc(date, time);
63
64 let line = Self {
65 date,
66 time,
67 datetime,
68 x_edge_location: iter.next().unwrap().to_string(),
69 sc_bytes: iter
70 .next()
71 .unwrap()
72 .parse::<u64>()
73 .map_err(|_e| "sc_bytes invalid")?,
74 c_ip: iter.next().unwrap().parse().map_err(|_e| "c_ip invalid")?,
75 cs_method: iter.next().unwrap().to_string(),
76 cs_host: iter.next().unwrap().to_string(),
77 cs_uri_stem: iter.next().unwrap().to_string(),
78 sc_status: iter
79 .next()
80 .unwrap()
81 .parse::<u16>()
82 .map_err(|_e| "sc_status invalid")?,
83 cs_referer: iter.next().unwrap().to_optional_string(),
84 cs_user_agent: iter.next().unwrap().to_string(),
85 cs_uri_query: iter.next().unwrap().to_optional_string(),
86 cs_cookie: iter.next().unwrap().to_optional_string(),
87 x_edge_result_type: iter
88 .next()
89 .unwrap()
90 .parse()
91 .map_err(|_e| "x_edge_result_type invalid")?,
92 x_edge_request_id: iter.next().unwrap().to_string(),
93 x_host_header: iter.next().unwrap().to_string(),
94 cs_protocol: iter
95 .next()
96 .unwrap()
97 .parse()
98 .map_err(|_e| "cs_protocol invalid")?,
99 cs_bytes: iter
100 .next()
101 .unwrap()
102 .parse::<u64>()
103 .map_err(|_e| "cs_bytes invalid")?,
104 time_taken: iter
105 .next()
106 .unwrap()
107 .parse::<f64>()
108 .map(Duration::from_secs_f64)
109 .map_err(|_e| "time_taken invalid")?,
110 x_forwarded_for: iter
111 .next()
112 .and_then(as_optional_t)
113 .transpose()
114 .map_err(|_e| "x_forwarded_for invalid")?,
115 ssl_protocol: iter
116 .next()
117 .and_then(as_optional_t)
118 .transpose()
119 .map_err(|_e| "ssl_protocol invalid")?,
120 ssl_cipher: iter.next().unwrap().to_optional_string(),
121 x_edge_response_result_type: iter
122 .next()
123 .unwrap()
124 .parse()
125 .map_err(|_e| "x_edge_response_result_type invalid")?,
126 cs_protocol_version: iter
127 .next()
128 .unwrap()
129 .parse()
130 .map_err(|_e| "cs_protocol_version invalid")?,
131 fle_status: iter.next().unwrap().to_optional_string(),
132 fle_encrypted_fields: iter
133 .next()
134 .and_then(as_optional_t)
135 .transpose()
136 .map_err(|_e| "fle_encrypted_fields invalid")?,
137 c_port: iter
138 .next()
139 .unwrap()
140 .parse::<u16>()
141 .map_err(|_e| "c_port invalid")?,
142 time_to_first_byte: iter
143 .next()
144 .unwrap()
145 .parse::<f64>()
146 .map(Duration::from_secs_f64)
147 .map_err(|_e| "time_to_first_byte invalid")?,
148 x_edge_detailed_result_type: iter
149 .next()
150 .unwrap()
151 .parse()
152 .map_err(|_e| "x_edge_detailed_result_type invalid")?,
153 sc_content_type: iter.next().unwrap().to_string(),
154 sc_content_len: iter
155 .next()
156 .unwrap()
157 .parse::<u64>()
158 .map_err(|_e| "sc_content_len invalid")?,
159 sc_range_start: iter
160 .next()
161 .and_then(as_optional_t)
162 .transpose()
163 .map_err(|_e| "sc_range_start invalid")?,
164 sc_range_end: iter
165 .next()
166 .and_then(as_optional_t)
167 .transpose()
168 .map_err(|_e| "sc_range_end invalid")?,
169 };
170 Ok(line)
171 }
172}
173
174impl TryFrom<CheckedRawLogLine<'_>> for LogLine {
175 type Error = &'static str;
176
177 fn try_from(raw: CheckedRawLogLine<'_>) -> Result<Self, Self::Error> {
178 let date = Date::parse(raw.date, TIME_DATE_FMT).map_err(|_e| "date invalid")?;
179 let time = Time::parse(raw.time, TIME_TIME_FMT).map_err(|_e| "time invalid")?;
180 let datetime = OffsetDateTime::new_utc(date, time);
181
182 let sll = LogLine {
183 date,
184 time,
185 datetime,
186 x_edge_location: raw.x_edge_location.to_string(),
187 sc_bytes: raw
188 .sc_bytes
189 .parse::<u64>()
190 .map_err(|_e| "sc_bytes invalid")?,
191 c_ip: raw.c_ip.parse().map_err(|_e| "c_ip invalid")?,
192 cs_method: raw.cs_method.to_string(),
193 cs_host: raw.cs_host.to_string(),
194 cs_uri_stem: raw.cs_uri_stem.to_string(),
195 sc_status: raw
196 .sc_status
197 .parse::<u16>()
198 .map_err(|_e| "sc_status invalid")?,
199 cs_referer: raw.cs_referer.to_optional_string(),
200 cs_user_agent: raw.cs_user_agent.to_string(),
201 cs_uri_query: raw.cs_uri_query.to_optional_string(),
202 cs_cookie: raw.cs_cookie.to_optional_string(),
203 x_edge_result_type: raw
204 .x_edge_result_type
205 .parse()
206 .map_err(|_e| "x_edge_result_type invalid")?,
207 x_edge_request_id: raw.x_edge_request_id.to_string(),
208 x_host_header: raw.x_host_header.to_string(),
209 cs_protocol: raw
210 .cs_protocol
211 .parse()
212 .map_err(|_e| "cs_protocol invalid")?,
213 cs_bytes: raw
214 .cs_bytes
215 .parse::<u64>()
216 .map_err(|_e| "cs_bytes invalid")?,
217 time_taken: raw
218 .time_taken
219 .parse::<f64>()
220 .map(Duration::from_secs_f64)
221 .map_err(|_e| "time_taken invalid")?,
222 x_forwarded_for: parse_as_option(raw.x_forwarded_for)
223 .map_err(|_e| "x_forwarded_for invalid")?,
224 ssl_protocol: parse_as_option(raw.ssl_protocol).map_err(|_e| "ssl_protocol invalid")?,
225 ssl_cipher: raw.ssl_cipher.to_optional_string(),
226 x_edge_response_result_type: raw
227 .x_edge_response_result_type
228 .parse()
229 .map_err(|_e| "x_edge_response_result_type invalid")?,
230 cs_protocol_version: raw
231 .cs_protocol_version
232 .parse()
233 .map_err(|_e| "cs_protocol_version invalid")?,
234 fle_status: raw.fle_status.to_optional_string(),
235 fle_encrypted_fields: parse_as_option(raw.fle_encrypted_fields)
236 .map_err(|_e| "fle_encrypted_fields invalid")?,
237 c_port: raw.c_port.parse::<u16>().map_err(|_e| "c_port invalid")?,
238 time_to_first_byte: raw
239 .time_to_first_byte
240 .parse::<f64>()
241 .map(Duration::from_secs_f64)
242 .map_err(|_e| "time_to_first_byte invalid")?,
243 x_edge_detailed_result_type: raw
244 .x_edge_detailed_result_type
245 .parse()
246 .map_err(|_e| "x_edge_detailed_result_type invalid")?,
247 sc_content_type: raw.sc_content_type.to_string(),
248 sc_content_len: raw
249 .sc_content_len
250 .parse::<u64>()
251 .map_err(|_e| "sc_content_len invalid")?,
252 sc_range_start: parse_as_option(raw.sc_range_start)
253 .map_err(|_e| "sc_range_start invalid")?,
254 sc_range_end: parse_as_option(raw.sc_range_end).map_err(|_e| "sc_range_end invalid")?,
255 };
256 Ok(sll)
257 }
258}