cloudfront_logs/simple/
mod.rs

1use crate::{shared::*, types::*, CheckedRawLogLine};
2
3pub use LogLine as SimpleLogLine;
4
5/// A simple log line representation owning its field data
6///
7/// Only primitive types from Rust's core/std library and types composable from them are used for the fields.
8/// Therefore types like Date and Time are not present, because they require external dependencies.
9#[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>, // *1
33    pub x_edge_response_result_type: EdgeResultType,
34    pub cs_protocol_version: CsProtocolVersion,
35    pub fle_status: Option<String>, // *1
36    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
46// *1: These fields are not typed (enums),
47// currently I have no use case for them and some of those have too many variants.
48
49impl 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}