@@ -37,6 +37,15 @@ impl LogLine<'_> {
3737 LogLine :: V1 ( line) => line. status ,
3838 }
3939 }
40+
41+ pub fn user_agent ( & self ) -> Option < & str > {
42+ match self {
43+ LogLine :: V1 ( line) => line
44+ . http
45+ . as_ref ( )
46+ . and_then ( |http| http. useragent . as_deref ( ) ) ,
47+ }
48+ }
4049}
4150
4251/// This struct corresponds to the `"version": "1"` variant of the [LogLine] enum.
@@ -60,6 +69,14 @@ pub struct LogLineV1<'a> {
6069 #[ serde( borrow) ]
6170 pub url : Cow < ' a , str > ,
6271 pub status : u16 ,
72+ #[ serde( borrow) ]
73+ pub http : Option < Http < ' a > > ,
74+ }
75+
76+ #[ derive( Debug , Deserialize ) ]
77+ pub struct Http < ' a > {
78+ #[ serde( borrow) ]
79+ pub useragent : Option < Cow < ' a , str > > ,
6380}
6481
6582#[ cfg( test) ]
@@ -79,6 +96,7 @@ mod tests {
7996 method: "GET",
8097 url: "https://static.staging.crates.io/?1705420437",
8198 status: 403,
99+ http: None,
82100 },
83101 )
84102 "# ) ;
@@ -90,6 +108,7 @@ mod tests {
90108 assert_eq ! ( output. method( ) , "GET" ) ;
91109 assert_eq ! ( output. url( ) , "https://static.staging.crates.io/?1705420437" ) ;
92110 assert_eq ! ( output. status( ) , 403 ) ;
111+ assert_eq ! ( output. user_agent( ) , None ) ;
93112
94113 match output {
95114 LogLine :: V1 ( l) => {
@@ -99,6 +118,44 @@ mod tests {
99118 }
100119 }
101120
121+ #[ test]
122+ fn test_parse_with_user_agent ( ) {
123+ let input = r#"{"bytes":36308,"content_type":"application/gzip","date_time":"2025-10-26T23:57:34.867635728Z","http":{"protocol":"HTTP/2","referer":null,"useragent":"cargo/1.92.0-nightly (344c4567c 2025-10-21)"},"ip":"192.0.2.1","method":"GET","status":200,"url":"https://static.crates.io/crates/scale-info/2.11.3/download","version":"1"}"# ;
124+ let output = assert_ok ! ( serde_json:: from_str:: <LogLine <' _>>( input) ) ;
125+ assert_debug_snapshot ! ( output, @r#"
126+ V1(
127+ LogLineV1 {
128+ date_time: 2025-10-26T23:57:34.867635728Z,
129+ method: "GET",
130+ url: "https://static.crates.io/crates/scale-info/2.11.3/download",
131+ status: 200,
132+ http: Some(
133+ Http {
134+ useragent: Some(
135+ "cargo/1.92.0-nightly (344c4567c 2025-10-21)",
136+ ),
137+ },
138+ ),
139+ },
140+ )
141+ "# ) ;
142+
143+ assert_eq ! (
144+ output. date_time( ) . to_string( ) ,
145+ "2025-10-26 23:57:34.867635728 UTC"
146+ ) ;
147+ assert_eq ! ( output. method( ) , "GET" ) ;
148+ assert_eq ! (
149+ output. url( ) ,
150+ "https://static.crates.io/crates/scale-info/2.11.3/download"
151+ ) ;
152+ assert_eq ! ( output. status( ) , 200 ) ;
153+ assert_eq ! (
154+ output. user_agent( ) ,
155+ Some ( "cargo/1.92.0-nightly (344c4567c 2025-10-21)" )
156+ ) ;
157+ }
158+
102159 #[ allow( clippy:: ptr_arg) ]
103160 fn is_borrowed ( s : & Cow < ' _ , str > ) -> bool {
104161 match s {
0 commit comments