1use serde::Deserialize;
4use serde_json::Value;
5
6#[derive(Debug, Clone)]
10pub struct NetworkEntry {
11 pub frame_ref: Option<String>,
14 pub page_ref: Option<String>,
16 pub monotonic_time: Option<f64>,
19 pub started_date_time: String,
22 pub time: Option<f64>,
25 pub request: RequestSnapshot,
26 pub response: ResponseSnapshot,
27 pub raw_snapshot: Value,
31}
32
33#[derive(Debug, Clone)]
34pub struct RequestSnapshot {
35 pub method: String,
36 pub url: String,
37 pub http_version: String,
38 pub headers: Vec<HeaderEntry>,
39 pub headers_size: Option<u64>,
40 pub body_size: Option<u64>,
41 pub post_data: Option<RequestPostData>,
42}
43
44#[derive(Debug, Clone)]
45pub struct ResponseSnapshot {
46 pub status: Option<u16>,
47 pub status_text: String,
48 pub http_version: String,
49 pub headers: Vec<HeaderEntry>,
50 pub headers_size: Option<u64>,
51 pub body_size: Option<u64>,
52 pub redirect_url: Option<String>,
54 pub content: ResponseContent,
55}
56
57#[derive(Debug, Clone)]
58pub struct HeaderEntry {
59 pub name: String,
60 pub value: String,
61}
62
63#[derive(Debug, Clone)]
64pub struct RequestPostData {
65 pub sha1: String,
67}
68
69#[derive(Debug, Clone)]
70pub struct ResponseContent {
71 pub size: Option<u64>,
72 pub mime_type: String,
73 pub sha1: Option<String>,
76}
77
78#[derive(Deserialize)]
83#[serde(rename_all = "camelCase")]
84struct SnapshotWire {
85 #[serde(default, rename = "_frameref")]
86 frame_ref: Option<String>,
87 #[serde(default)]
88 pageref: Option<String>,
89 #[serde(default, rename = "_monotonicTime")]
90 monotonic_time: Option<f64>,
91 #[serde(default)]
92 started_date_time: String,
93 #[serde(default = "default_time")]
94 time: f64,
95 request: RequestWire,
96 response: ResponseWire,
97}
98
99fn default_time() -> f64 {
100 -1.0
101}
102
103#[derive(Deserialize)]
104#[serde(rename_all = "camelCase")]
105struct RequestWire {
106 method: String,
107 url: String,
108 #[serde(default)]
109 http_version: String,
110 #[serde(default)]
111 headers: Vec<HeaderEntryWire>,
112 #[serde(default = "default_neg_one")]
113 headers_size: i64,
114 #[serde(default = "default_neg_one")]
115 body_size: i64,
116 #[serde(default)]
117 post_data: Option<PostDataWire>,
118}
119
120#[derive(Deserialize)]
121#[serde(rename_all = "camelCase")]
122struct ResponseWire {
123 #[serde(default = "default_neg_one_i32")]
124 status: i32,
125 #[serde(default)]
126 status_text: String,
127 #[serde(default)]
128 http_version: String,
129 #[serde(default)]
130 headers: Vec<HeaderEntryWire>,
131 #[serde(default = "default_neg_one")]
132 headers_size: i64,
133 #[serde(default = "default_neg_one")]
134 body_size: i64,
135 #[serde(default)]
136 redirect_url: String,
137 content: ContentWire,
138}
139
140#[derive(Deserialize)]
141struct HeaderEntryWire {
142 name: String,
143 value: String,
144}
145
146#[derive(Deserialize)]
147struct PostDataWire {
148 #[serde(rename = "_sha1")]
149 sha1: String,
150}
151
152#[derive(Deserialize)]
153#[serde(rename_all = "camelCase")]
154struct ContentWire {
155 #[serde(default = "default_neg_one")]
156 size: i64,
157 #[serde(default)]
158 mime_type: String,
159 #[serde(default, rename = "_sha1")]
160 sha1: Option<String>,
161}
162
163fn default_neg_one() -> i64 {
164 -1
165}
166fn default_neg_one_i32() -> i32 {
167 -1
168}
169
170fn unknown_neg_one_u64(n: i64) -> Option<u64> {
173 if n == -1 { None } else { Some(n as u64) }
174}
175
176fn unknown_neg_one_f64(n: f64) -> Option<f64> {
177 if n == -1.0 { None } else { Some(n) }
178}
179
180fn empty_string_to_none(s: String) -> Option<String> {
181 if s.is_empty() { None } else { Some(s) }
182}
183
184impl NetworkEntry {
185 pub(crate) fn from_snapshot(snapshot: Value) -> Result<Self, serde_json::Error> {
186 let wire: SnapshotWire = serde_json::from_value(snapshot.clone())?;
187 Ok(NetworkEntry {
188 frame_ref: wire.frame_ref,
189 page_ref: wire.pageref,
190 monotonic_time: wire.monotonic_time,
191 started_date_time: wire.started_date_time,
192 time: unknown_neg_one_f64(wire.time),
193 request: RequestSnapshot {
194 method: wire.request.method,
195 url: wire.request.url,
196 http_version: wire.request.http_version,
197 headers: wire
198 .request
199 .headers
200 .into_iter()
201 .map(|h| HeaderEntry {
202 name: h.name,
203 value: h.value,
204 })
205 .collect(),
206 headers_size: unknown_neg_one_u64(wire.request.headers_size),
207 body_size: unknown_neg_one_u64(wire.request.body_size),
208 post_data: wire
209 .request
210 .post_data
211 .map(|p| RequestPostData { sha1: p.sha1 }),
212 },
213 response: ResponseSnapshot {
214 status: if wire.response.status == -1 {
215 None
216 } else {
217 Some(wire.response.status as u16)
218 },
219 status_text: wire.response.status_text,
220 http_version: wire.response.http_version,
221 headers: wire
222 .response
223 .headers
224 .into_iter()
225 .map(|h| HeaderEntry {
226 name: h.name,
227 value: h.value,
228 })
229 .collect(),
230 headers_size: unknown_neg_one_u64(wire.response.headers_size),
231 body_size: unknown_neg_one_u64(wire.response.body_size),
232 redirect_url: empty_string_to_none(wire.response.redirect_url),
233 content: ResponseContent {
234 size: unknown_neg_one_u64(wire.response.content.size),
235 mime_type: wire.response.content.mime_type,
236 sha1: wire.response.content.sha1,
237 },
238 },
239 raw_snapshot: snapshot,
240 })
241 }
242}