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