cloudfront_logs/owned/
simple.rs

1use crate::{
2    borrowed::raw::{UnvalidatedLogline as UnvalidatedRaw, ValidatedLogline as ValidatedRaw},
3    shared::*,
4    types::*,
5};
6
7pub type ValidatedLogline = Logline<Validated>;
8pub type UnvalidatedLogline = Logline<Unvalidated>;
9
10/// A simple log line representation owning its field data
11///
12/// Only primitive types from Rust's core/std library and types composable from them are used for the fields.
13/// Therefore types like Date and Time are not present, because they require external dependencies.
14#[derive(Debug, Clone, PartialEq)]
15pub struct Logline<V> {
16    pub date: String,
17    pub time: String,
18    pub x_edge_location: String,
19    pub sc_bytes: u64,
20    pub c_ip: IpAddr,
21    pub cs_method: String,
22    pub cs_host: String,
23    pub cs_uri_stem: String,
24    pub sc_status: u16,
25    pub cs_referer: Option<String>,
26    pub cs_user_agent: String,
27    pub cs_uri_query: Option<String>,
28    pub cs_cookie: Option<String>,
29    pub x_edge_result_type: EdgeResultType,
30    pub x_edge_request_id: String,
31    pub x_host_header: String,
32    pub cs_protocol: CsProtocol,
33    pub cs_bytes: u64,
34    pub time_taken: Duration,
35    pub x_forwarded_for: Option<ForwardedForAddrs>,
36    pub ssl_protocol: Option<SslProtocol>,
37    pub ssl_cipher: Option<String>, // *1
38    pub x_edge_response_result_type: EdgeResultType,
39    pub cs_protocol_version: CsProtocolVersion,
40    pub fle_status: Option<String>, // *1
41    pub fle_encrypted_fields: Option<u64>,
42    pub c_port: u16,
43    pub time_to_first_byte: Duration,
44    pub x_edge_detailed_result_type: DetailedEdgeResultType,
45    pub sc_content_type: String,
46    pub sc_content_len: u64,
47    pub sc_range_start: Option<i64>,
48    pub sc_range_end: Option<i64>,
49    __marker: PhantomData<V>,
50}
51
52// *1: These fields are not typed (enums),
53// currently I have no use case for them and some of those have too many variants.
54
55impl TryFrom<&str> for Logline<Validated> {
56    type Error = &'static str;
57
58    fn try_from(line: &str) -> Result<Self, Self::Error> {
59        validate_line(line)?;
60        new_log_line(line)
61    }
62}
63
64impl TryFrom<&str> for Logline<Unvalidated> {
65    type Error = &'static str;
66
67    fn try_from(line: &str) -> Result<Self, Self::Error> {
68        new_log_line(line)
69    }
70}
71
72fn new_log_line<V>(line: &str) -> Result<Logline<V>, &'static str> {
73    let mut iter = MemchrTabSplitter::new(line);
74
75    let line = Logline {
76        date: iter.next().unwrap().to_string(),
77        time: iter.next().unwrap().to_string(),
78        x_edge_location: iter.next().unwrap().to_string(),
79        sc_bytes: iter
80            .next()
81            .unwrap()
82            .parse::<u64>()
83            .map_err(|_e| "sc_bytes invalid")?,
84        c_ip: iter.next().unwrap().parse().map_err(|_e| "c_ip invalid")?,
85        cs_method: iter.next().unwrap().to_string(),
86        cs_host: iter.next().unwrap().to_string(),
87        cs_uri_stem: iter.next().unwrap().to_string(),
88        sc_status: iter
89            .next()
90            .unwrap()
91            .parse::<u16>()
92            .map_err(|_e| "sc_status invalid")?,
93        cs_referer: iter.next().unwrap().to_optional_string(),
94        cs_user_agent: iter.next().unwrap().to_string(),
95        cs_uri_query: iter.next().unwrap().to_optional_string(),
96        cs_cookie: iter.next().unwrap().to_optional_string(),
97        x_edge_result_type: iter
98            .next()
99            .unwrap()
100            .parse()
101            .map_err(|_e| "x_edge_result_type invalid")?,
102        x_edge_request_id: iter.next().unwrap().to_string(),
103        x_host_header: iter.next().unwrap().to_string(),
104        cs_protocol: iter
105            .next()
106            .unwrap()
107            .parse()
108            .map_err(|_e| "cs_protocol invalid")?,
109        cs_bytes: iter
110            .next()
111            .unwrap()
112            .parse::<u64>()
113            .map_err(|_e| "cs_bytes invalid")?,
114        time_taken: iter
115            .next()
116            .unwrap()
117            .parse::<f64>()
118            .map(Duration::from_secs_f64)
119            .map_err(|_e| "time_taken invalid")?,
120        x_forwarded_for: iter
121            .next()
122            .and_then(as_optional_t)
123            .transpose()
124            .map_err(|_e| "x_forwarded_for invalid")?,
125        ssl_protocol: iter
126            .next()
127            .and_then(as_optional_t)
128            .transpose()
129            .map_err(|_e| "ssl_protocol invalid")?,
130        ssl_cipher: iter.next().unwrap().to_optional_string(),
131        x_edge_response_result_type: iter
132            .next()
133            .unwrap()
134            .parse()
135            .map_err(|_e| "x_edge_response_result_type invalid")?,
136        cs_protocol_version: iter
137            .next()
138            .unwrap()
139            .parse()
140            .map_err(|_e| "cs_protocol_version invalid")?,
141        fle_status: iter.next().unwrap().to_optional_string(),
142        fle_encrypted_fields: iter
143            .next()
144            .and_then(as_optional_t)
145            .transpose()
146            .map_err(|_e| "fle_encrypted_fields invalid")?,
147        c_port: iter
148            .next()
149            .unwrap()
150            .parse::<u16>()
151            .map_err(|_e| "c_port invalid")?,
152        time_to_first_byte: iter
153            .next()
154            .unwrap()
155            .parse::<f64>()
156            .map(Duration::from_secs_f64)
157            .map_err(|_e| "time_to_first_byte invalid")?,
158        x_edge_detailed_result_type: iter
159            .next()
160            .unwrap()
161            .parse()
162            .map_err(|_e| "x_edge_detailed_result_type invalid")?,
163        sc_content_type: iter.next().unwrap().to_string(),
164        sc_content_len: iter
165            .next()
166            .unwrap()
167            .parse::<u64>()
168            .map_err(|_e| "sc_content_len invalid")?,
169        sc_range_start: iter
170            .next()
171            .and_then(as_optional_t)
172            .transpose()
173            .map_err(|_e| "sc_range_start invalid")?,
174        sc_range_end: iter
175            .next()
176            .and_then(as_optional_t)
177            .transpose()
178            .map_err(|_e| "sc_range_end invalid")?,
179        __marker: PhantomData,
180    };
181    Ok(line)
182}
183
184impl Logline<Validated> {
185    pub fn try_from_with_raw(line: &str) -> Result<Self, &'static str> {
186        let raw = ValidatedRaw::try_from(line)?;
187        let line = Self::try_from(raw)?;
188        Ok(line)
189    }
190}
191
192impl Logline<Unvalidated> {
193    pub fn try_from_with_raw(line: &str) -> Result<Self, &'static str> {
194        let raw = UnvalidatedRaw::from(line);
195        let line = Self::try_from(raw)?;
196        Ok(line)
197    }
198}
199
200impl TryFrom<ValidatedRaw<'_>> for Logline<Validated> {
201    type Error = &'static str;
202
203    fn try_from(raw: ValidatedRaw<'_>) -> Result<Self, Self::Error> {
204        let line = Self {
205            date: raw.date.to_string(),
206            time: raw.time.to_string(),
207            x_edge_location: raw.x_edge_location.to_string(),
208            sc_bytes: raw
209                .sc_bytes
210                .parse::<u64>()
211                .map_err(|_e| "sc_bytes invalid")?,
212            c_ip: raw.c_ip.parse().map_err(|_e| "c_ip invalid")?,
213            cs_method: raw.cs_method.to_string(),
214            cs_host: raw.cs_host.to_string(),
215            cs_uri_stem: raw.cs_uri_stem.to_string(),
216            sc_status: raw
217                .sc_status
218                .parse::<u16>()
219                .map_err(|_e| "sc_status invalid")?,
220            cs_referer: raw.cs_referer.to_optional_string(),
221            cs_user_agent: raw.cs_user_agent.to_string(),
222            cs_uri_query: raw.cs_uri_query.to_optional_string(),
223            cs_cookie: raw.cs_cookie.to_optional_string(),
224            x_edge_result_type: raw
225                .x_edge_result_type
226                .parse()
227                .map_err(|_e| "x_edge_result_type invalid")?,
228            x_edge_request_id: raw.x_edge_request_id.to_string(),
229            x_host_header: raw.x_host_header.to_string(),
230            cs_protocol: raw
231                .cs_protocol
232                .parse()
233                .map_err(|_e| "cs_protocol invalid")?,
234            cs_bytes: raw
235                .cs_bytes
236                .parse::<u64>()
237                .map_err(|_e| "cs_bytes invalid")?,
238            time_taken: raw
239                .time_taken
240                .parse::<f64>()
241                .map(Duration::from_secs_f64)
242                .map_err(|_e| "time_taken invalid")?,
243            x_forwarded_for: parse_as_option(raw.x_forwarded_for)
244                .map_err(|_e| "x_forwarded_for invalid")?,
245            ssl_protocol: parse_as_option(raw.ssl_protocol).map_err(|_e| "ssl_protocol invalid")?,
246            ssl_cipher: raw.ssl_cipher.to_optional_string(),
247            x_edge_response_result_type: raw
248                .x_edge_response_result_type
249                .parse()
250                .map_err(|_e| "x_edge_response_result_type invalid")?,
251            cs_protocol_version: raw
252                .cs_protocol_version
253                .parse()
254                .map_err(|_e| "cs_protocol_version invalid")?,
255            fle_status: raw.fle_status.to_optional_string(),
256            fle_encrypted_fields: parse_as_option(raw.fle_encrypted_fields)
257                .map_err(|_e| "fle_encrypted_fields invalid")?,
258            c_port: raw.c_port.parse::<u16>().map_err(|_e| "c_port invalid")?,
259            time_to_first_byte: raw
260                .time_to_first_byte
261                .parse::<f64>()
262                .map(Duration::from_secs_f64)
263                .map_err(|_e| "time_to_first_byte invalid")?,
264            x_edge_detailed_result_type: raw
265                .x_edge_detailed_result_type
266                .parse()
267                .map_err(|_e| "x_edge_detailed_result_type invalid")?,
268            sc_content_type: raw.sc_content_type.to_string(),
269            sc_content_len: raw
270                .sc_content_len
271                .parse::<u64>()
272                .map_err(|_e| "sc_content_len invalid")?,
273            sc_range_start: parse_as_option(raw.sc_range_start)
274                .map_err(|_e| "sc_range_start invalid")?,
275            sc_range_end: parse_as_option(raw.sc_range_end).map_err(|_e| "sc_range_end invalid")?,
276            __marker: PhantomData,
277        };
278        Ok(line)
279    }
280}
281
282impl TryFrom<UnvalidatedRaw<'_>> for Logline<Unvalidated> {
283    type Error = &'static str;
284
285    fn try_from(raw: UnvalidatedRaw<'_>) -> Result<Self, Self::Error> {
286        let line = Self {
287            date: raw.date.to_string(),
288            time: raw.time.to_string(),
289            x_edge_location: raw.x_edge_location.to_string(),
290            sc_bytes: raw
291                .sc_bytes
292                .parse::<u64>()
293                .map_err(|_e| "sc_bytes invalid")?,
294            c_ip: raw.c_ip.parse().map_err(|_e| "c_ip invalid")?,
295            cs_method: raw.cs_method.to_string(),
296            cs_host: raw.cs_host.to_string(),
297            cs_uri_stem: raw.cs_uri_stem.to_string(),
298            sc_status: raw
299                .sc_status
300                .parse::<u16>()
301                .map_err(|_e| "sc_status invalid")?,
302            cs_referer: raw.cs_referer.to_optional_string(),
303            cs_user_agent: raw.cs_user_agent.to_string(),
304            cs_uri_query: raw.cs_uri_query.to_optional_string(),
305            cs_cookie: raw.cs_cookie.to_optional_string(),
306            x_edge_result_type: raw
307                .x_edge_result_type
308                .parse()
309                .map_err(|_e| "x_edge_result_type invalid")?,
310            x_edge_request_id: raw.x_edge_request_id.to_string(),
311            x_host_header: raw.x_host_header.to_string(),
312            cs_protocol: raw
313                .cs_protocol
314                .parse()
315                .map_err(|_e| "cs_protocol invalid")?,
316            cs_bytes: raw
317                .cs_bytes
318                .parse::<u64>()
319                .map_err(|_e| "cs_bytes invalid")?,
320            time_taken: raw
321                .time_taken
322                .parse::<f64>()
323                .map(Duration::from_secs_f64)
324                .map_err(|_e| "time_taken invalid")?,
325            x_forwarded_for: parse_as_option(raw.x_forwarded_for)
326                .map_err(|_e| "x_forwarded_for invalid")?,
327            ssl_protocol: parse_as_option(raw.ssl_protocol).map_err(|_e| "ssl_protocol invalid")?,
328            ssl_cipher: raw.ssl_cipher.to_optional_string(),
329            x_edge_response_result_type: raw
330                .x_edge_response_result_type
331                .parse()
332                .map_err(|_e| "x_edge_response_result_type invalid")?,
333            cs_protocol_version: raw
334                .cs_protocol_version
335                .parse()
336                .map_err(|_e| "cs_protocol_version invalid")?,
337            fle_status: raw.fle_status.to_optional_string(),
338            fle_encrypted_fields: parse_as_option(raw.fle_encrypted_fields)
339                .map_err(|_e| "fle_encrypted_fields invalid")?,
340            c_port: raw.c_port.parse::<u16>().map_err(|_e| "c_port invalid")?,
341            time_to_first_byte: raw
342                .time_to_first_byte
343                .parse::<f64>()
344                .map(Duration::from_secs_f64)
345                .map_err(|_e| "time_to_first_byte invalid")?,
346            x_edge_detailed_result_type: raw
347                .x_edge_detailed_result_type
348                .parse()
349                .map_err(|_e| "x_edge_detailed_result_type invalid")?,
350            sc_content_type: raw.sc_content_type.to_string(),
351            sc_content_len: raw
352                .sc_content_len
353                .parse::<u64>()
354                .map_err(|_e| "sc_content_len invalid")?,
355            sc_range_start: parse_as_option(raw.sc_range_start)
356                .map_err(|_e| "sc_range_start invalid")?,
357            sc_range_end: parse_as_option(raw.sc_range_end).map_err(|_e| "sc_range_end invalid")?,
358            __marker: PhantomData,
359        };
360        Ok(line)
361    }
362}