cloudfront_logs/borrowed/
raw.rs

1use crate::{shared::*, types::*};
2
3/// The validated raw log line
4///
5/// All fields are [`&str`] slices into the original log line.
6///
7/// On construction it checks if the line can be parsed.
8/// This is useful if you cannot skip the comment lines or have reason to not trust the input for format correctness.
9/// The latter should be only an issue if you do not use this crate on CloudFront logs directly.
10///
11/// # Panics
12///
13/// Technically the construction should never panic, because the input is validated before the line is sliced into fields.
14/// The fields are only view into the original full log line, no parsing errors can occur.
15///
16/// # Examples
17///
18/// Use `.try_from()` or `.try_into()` to construct an instance, since action can fail.
19///
20/// ```rust
21/// use cloudfront_logs::{borrowed::raw::ValidatedLogline, types::*};
22///
23/// let line = "2019-12-04	21:02:31	LAX1	392	192.0.2.100	GET	d111111abcdef8.cloudfront.net	/index.html	200	-	Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/78.0.3904.108%20Safari/537.36	-	-	Hit	SOX4xwn4XV6Q4rgb7XiVGOHms_BGlTAC4KyHmureZmBNrjGdRLiNIQ==	d111111abcdef8.cloudfront.net	https	23	0.001	-	TLSv1.2	ECDHE-RSA-AES128-GCM-SHA256	Hit	HTTP/2.0	-	-	11040	0.001	Hit	text/html	78	-	-";
24///
25/// let item = ValidatedLogline::try_from(line).unwrap();
26/// // alternative:
27/// let item: ValidatedLogline<'_> = line.try_into().unwrap();
28///
29/// assert_eq!(item.date, "2019-12-04");
30/// assert_eq!(item.sc_bytes, "392");
31/// assert_eq!(item.c_ip, "192.0.2.100");
32/// ```
33pub type ValidatedLogline<'a> = Logline<'a, Validated>;
34
35/// The unvalidated raw log line
36///
37/// All fields are [`&str`] slices into the original log line.
38///
39/// Unlike [`ValidatedLogline`], this variant does not check if the line can be parsed.
40/// Use this if you already did a check before creating this struct.
41/// A common scenario is that you 1) trust the input data and 2) skipped the comment lines.
42///
43/// Note: This is the only variant which can use the `From` trait instead of `TryFrom`,
44/// because validation is skipped and the input data does not need to be parsed into other types.
45///
46/// # Panics
47///
48/// Construction can panic if the input is not a valid log line!
49///
50/// # Examples
51///
52/// Use `.from()` or `.into()` to construct an instance, since no validation is done.
53///
54/// ```rust
55/// use cloudfront_logs::{borrowed::raw::UnvalidatedLogline, types::*};
56///
57/// let line = "2019-12-04	21:02:31	LAX1	392	192.0.2.100	GET	d111111abcdef8.cloudfront.net	/index.html	200	-	Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/78.0.3904.108%20Safari/537.36	-	-	Hit	SOX4xwn4XV6Q4rgb7XiVGOHms_BGlTAC4KyHmureZmBNrjGdRLiNIQ==	d111111abcdef8.cloudfront.net	https	23	0.001	-	TLSv1.2	ECDHE-RSA-AES128-GCM-SHA256	Hit	HTTP/2.0	-	-	11040	0.001	Hit	text/html	78	-	-";
58///
59/// let item = UnvalidatedLogline::from(line);
60/// // alternative:
61/// let item: UnvalidatedLogline<'_> = line.into();
62///
63/// assert_eq!(item.date, "2019-12-04");
64/// assert_eq!(item.sc_bytes, "392");
65/// assert_eq!(item.c_ip, "192.0.2.100");
66/// ```
67pub type UnvalidatedLogline<'a> = Logline<'a, Unvalidated>;
68
69/// The generic, raw log line type
70///
71/// Do not use it directly, prefer [`ValidatedLogline`] or [`UnvalidatedLogline`] instead.
72#[must_use]
73#[derive(Debug, Clone, Copy, PartialEq)]
74pub struct Logline<'a, V> {
75    pub date: &'a str,
76    pub time: &'a str,
77    pub x_edge_location: &'a str,
78    pub sc_bytes: &'a str,
79    pub c_ip: &'a str,
80    pub cs_method: &'a str,
81    pub cs_host: &'a str,
82    pub cs_uri_stem: &'a str,
83    pub sc_status: &'a str,
84    pub cs_referer: &'a str,
85    pub cs_user_agent: &'a str,
86    pub cs_uri_query: &'a str,
87    pub cs_cookie: &'a str,
88    pub x_edge_result_type: &'a str,
89    pub x_edge_request_id: &'a str,
90    pub x_host_header: &'a str,
91    pub cs_protocol: &'a str,
92    pub cs_bytes: &'a str,
93    pub time_taken: &'a str,
94    pub x_forwarded_for: &'a str,
95    pub ssl_protocol: &'a str,
96    pub ssl_cipher: &'a str,
97    pub x_edge_response_result_type: &'a str,
98    pub cs_protocol_version: &'a str,
99    pub fle_status: &'a str,
100    pub fle_encrypted_fields: &'a str,
101    pub c_port: &'a str,
102    pub time_to_first_byte: &'a str,
103    pub x_edge_detailed_result_type: &'a str,
104    pub sc_content_type: &'a str,
105    pub sc_content_len: &'a str,
106    pub sc_range_start: &'a str,
107    pub sc_range_end: &'a str,
108    __marker: PhantomData<V>,
109}
110
111impl<'a> TryFrom<&'a str> for Logline<'a, Validated> {
112    type Error = &'static str;
113
114    fn try_from(line: &'a str) -> Result<Self, Self::Error> {
115        validate_line(line)?;
116        let result = new_log_line(line);
117        Ok(result)
118    }
119}
120
121impl<'a> From<&'a str> for Logline<'a, Unvalidated> {
122    fn from(line: &'a str) -> Self {
123        new_log_line(line)
124    }
125}
126
127fn new_log_line<V>(line: &str) -> Logline<'_, V> {
128    let mut iter = MemchrTabSplitter::new(line);
129
130    Logline {
131        date: iter.next().unwrap(),
132        time: iter.next().unwrap(),
133        x_edge_location: iter.next().unwrap(),
134        sc_bytes: iter.next().unwrap(),
135        c_ip: iter.next().unwrap(),
136        cs_method: iter.next().unwrap(),
137        cs_host: iter.next().unwrap(),
138        cs_uri_stem: iter.next().unwrap(),
139        sc_status: iter.next().unwrap(),
140        cs_referer: iter.next().unwrap(),
141        cs_user_agent: iter.next().unwrap(),
142        cs_uri_query: iter.next().unwrap(),
143        cs_cookie: iter.next().unwrap(),
144        x_edge_result_type: iter.next().unwrap(),
145        x_edge_request_id: iter.next().unwrap(),
146        x_host_header: iter.next().unwrap(),
147        cs_protocol: iter.next().unwrap(),
148        cs_bytes: iter.next().unwrap(),
149        time_taken: iter.next().unwrap(),
150        x_forwarded_for: iter.next().unwrap(),
151        ssl_protocol: iter.next().unwrap(),
152        ssl_cipher: iter.next().unwrap(),
153        x_edge_response_result_type: iter.next().unwrap(),
154        cs_protocol_version: iter.next().unwrap(),
155        fle_status: iter.next().unwrap(),
156        fle_encrypted_fields: iter.next().unwrap(),
157        c_port: iter.next().unwrap(),
158        time_to_first_byte: iter.next().unwrap(),
159        x_edge_detailed_result_type: iter.next().unwrap(),
160        sc_content_type: iter.next().unwrap(),
161        sc_content_len: iter.next().unwrap(),
162        sc_range_start: iter.next().unwrap(),
163        sc_range_end: iter.next().unwrap(),
164        __marker: PhantomData,
165    }
166}
167
168impl<'a> From<Logline<'a, Validated>> for Logline<'a, Unvalidated> {
169    fn from(validated: Logline<'a, Validated>) -> Self {
170        Logline {
171            date: validated.date,
172            time: validated.time,
173            x_edge_location: validated.x_edge_location,
174            sc_bytes: validated.sc_bytes,
175            c_ip: validated.c_ip,
176            cs_method: validated.cs_method,
177            cs_host: validated.cs_host,
178            cs_uri_stem: validated.cs_uri_stem,
179            sc_status: validated.sc_status,
180            cs_referer: validated.cs_referer,
181            cs_user_agent: validated.cs_user_agent,
182            cs_uri_query: validated.cs_uri_query,
183            cs_cookie: validated.cs_cookie,
184            x_edge_result_type: validated.x_edge_result_type,
185            x_edge_request_id: validated.x_edge_request_id,
186            x_host_header: validated.x_host_header,
187            cs_protocol: validated.cs_protocol,
188            cs_bytes: validated.cs_bytes,
189            time_taken: validated.time_taken,
190            x_forwarded_for: validated.x_forwarded_for,
191            ssl_protocol: validated.ssl_protocol,
192            ssl_cipher: validated.ssl_cipher,
193            x_edge_response_result_type: validated.x_edge_response_result_type,
194            cs_protocol_version: validated.cs_protocol_version,
195            fle_status: validated.fle_status,
196            fle_encrypted_fields: validated.fle_encrypted_fields,
197            c_port: validated.c_port,
198            time_to_first_byte: validated.time_to_first_byte,
199            x_edge_detailed_result_type: validated.x_edge_detailed_result_type,
200            sc_content_type: validated.sc_content_type,
201            sc_content_len: validated.sc_content_len,
202            sc_range_start: validated.sc_range_start,
203            sc_range_end: validated.sc_range_end,
204            __marker: PhantomData,
205        }
206    }
207}
208
209impl<'a> From<Logline<'a, Unvalidated>> for Logline<'a, Validated> {
210    fn from(unvalidated: Logline<'a, Unvalidated>) -> Self {
211        Logline {
212            date: unvalidated.date,
213            time: unvalidated.time,
214            x_edge_location: unvalidated.x_edge_location,
215            sc_bytes: unvalidated.sc_bytes,
216            c_ip: unvalidated.c_ip,
217            cs_method: unvalidated.cs_method,
218            cs_host: unvalidated.cs_host,
219            cs_uri_stem: unvalidated.cs_uri_stem,
220            sc_status: unvalidated.sc_status,
221            cs_referer: unvalidated.cs_referer,
222            cs_user_agent: unvalidated.cs_user_agent,
223            cs_uri_query: unvalidated.cs_uri_query,
224            cs_cookie: unvalidated.cs_cookie,
225            x_edge_result_type: unvalidated.x_edge_result_type,
226            x_edge_request_id: unvalidated.x_edge_request_id,
227            x_host_header: unvalidated.x_host_header,
228            cs_protocol: unvalidated.cs_protocol,
229            cs_bytes: unvalidated.cs_bytes,
230            time_taken: unvalidated.time_taken,
231            x_forwarded_for: unvalidated.x_forwarded_for,
232            ssl_protocol: unvalidated.ssl_protocol,
233            ssl_cipher: unvalidated.ssl_cipher,
234            x_edge_response_result_type: unvalidated.x_edge_response_result_type,
235            cs_protocol_version: unvalidated.cs_protocol_version,
236            fle_status: unvalidated.fle_status,
237            fle_encrypted_fields: unvalidated.fle_encrypted_fields,
238            c_port: unvalidated.c_port,
239            time_to_first_byte: unvalidated.time_to_first_byte,
240            x_edge_detailed_result_type: unvalidated.x_edge_detailed_result_type,
241            sc_content_type: unvalidated.sc_content_type,
242            sc_content_len: unvalidated.sc_content_len,
243            sc_range_start: unvalidated.sc_range_start,
244            sc_range_end: unvalidated.sc_range_end,
245            __marker: PhantomData,
246        }
247    }
248}