cloudfront_logs/typed/
mod.rs

1use crate::{shared::*, types::*, CheckedRawLogLine, TIME_DATE_FMT, TIME_TIME_FMT};
2
3pub use LogLine as TypedLogLine;
4
5/// A simple log line representation owning its field data
6///
7/// Only primitive types from Rust's core/std library are used for the fields.
8/// Therefore types like Date and Time are not present.
9#[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, // *2; URI path
20    pub sc_status: u16,      // *2
21    pub cs_referer: Option<String>,
22    pub cs_user_agent: String,
23    pub cs_uri_query: Option<String>, // *2
24    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>, // *1
34    pub x_edge_response_result_type: EdgeResultType,
35    pub cs_protocol_version: CsProtocolVersion,
36    pub fle_status: Option<String>, // *1
37    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, // *2
42    pub sc_content_len: u64,
43    pub sc_range_start: Option<i64>,
44    pub sc_range_end: Option<i64>,
45}
46
47// *1: These fields are not typed yet (as enums or structs),
48// currently I have no use case for them and some of those have too many variants.
49
50// *2: Candidate for future typing
51
52impl<'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}