1use std::io::{Read, Write};
11
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 context::{BuildEventContext, read_build_event_context, write_build_event_context},
16 error::MuninError,
17 field_flags::BuildEventArgsFieldFlags,
18 primitives::{BinlogDateTime, read_7bit_count, read_7bit_int, read_datetime},
19 string_table::StringTable,
20 writers::{WriteContext, write_7bit_int, write_datetime, write_dedup_string},
21};
22
23#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
25pub struct ExtendedDataFields {
26 pub extended_type: Option<String>,
27 pub extended_metadata_index: i32,
28 pub extended_data: Option<String>,
29}
30
31#[derive(Debug, Clone, Default, Serialize, Deserialize)]
33pub struct BuildEventArgsFields {
34 pub flags: BuildEventArgsFieldFlags,
35 pub message: Option<String>,
36 pub build_event_context: Option<BuildEventContext>,
37 pub thread_id: i32,
38 pub help_keyword: Option<String>,
39 pub sender_name: Option<String>,
40 pub timestamp: Option<BinlogDateTime>,
41 pub extended: Option<ExtendedDataFields>,
42
43 pub subcategory: Option<String>,
45 pub code: Option<String>,
46 pub file: Option<String>,
47 pub project_file: Option<String>,
48 pub line_number: i32,
49 pub column_number: i32,
50 pub end_line_number: i32,
51 pub end_column_number: i32,
52
53 pub arguments: Option<Vec<Option<String>>>,
55
56 pub importance: i32,
58}
59
60fn read_dedup_string(
62 reader: &mut impl Read,
63 strings: &StringTable,
64) -> Result<Option<String>, MuninError> {
65 let index = read_7bit_int(reader)?;
66 Ok(strings.get(index)?.map(|s| s.to_string()))
67}
68
69pub fn read_build_event_args_fields(
75 reader: &mut impl Read,
76 strings: &StringTable,
77 file_format_version: i32,
78 read_importance: bool,
79) -> Result<BuildEventArgsFields, MuninError> {
80 let flags_raw = read_7bit_int(reader)? as u32;
81 let flags = BuildEventArgsFieldFlags::from_raw(flags_raw);
82 let mut fields = BuildEventArgsFields {
83 flags,
84 ..Default::default()
85 };
86
87 if flags.contains(BuildEventArgsFieldFlags::MESSAGE) {
90 fields.message = read_dedup_string(reader, strings)?;
91 }
92
93 if flags.contains(BuildEventArgsFieldFlags::BUILD_EVENT_CONTEXT) {
94 fields.build_event_context = Some(read_build_event_context(reader, file_format_version)?);
95 }
96
97 if flags.contains(BuildEventArgsFieldFlags::THREAD_ID) {
98 fields.thread_id = read_7bit_int(reader)?;
99 }
100
101 if flags.contains(BuildEventArgsFieldFlags::HELP_KEYWORD) {
102 fields.help_keyword = read_dedup_string(reader, strings)?;
103 }
104
105 if flags.contains(BuildEventArgsFieldFlags::SENDER_NAME) {
106 fields.sender_name = read_dedup_string(reader, strings)?;
107 }
108
109 if flags.contains(BuildEventArgsFieldFlags::TIMESTAMP) {
110 fields.timestamp = Some(read_datetime(reader)?);
111 }
112
113 if flags.contains(BuildEventArgsFieldFlags::EXTENDED) {
114 let extended_type = read_dedup_string(reader, strings)?;
115 let extended_metadata_index = read_7bit_int(reader)?;
118 let extended_data = read_dedup_string(reader, strings)?;
119 fields.extended = Some(ExtendedDataFields {
120 extended_type,
121 extended_metadata_index,
122 extended_data,
123 });
124 }
125
126 if flags.contains(BuildEventArgsFieldFlags::SUBCATEGORY) {
129 fields.subcategory = read_dedup_string(reader, strings)?;
130 }
131
132 if flags.contains(BuildEventArgsFieldFlags::CODE) {
133 fields.code = read_dedup_string(reader, strings)?;
134 }
135
136 if flags.contains(BuildEventArgsFieldFlags::FILE) {
137 fields.file = read_dedup_string(reader, strings)?;
138 }
139
140 if flags.contains(BuildEventArgsFieldFlags::PROJECT_FILE) {
141 fields.project_file = read_dedup_string(reader, strings)?;
142 }
143
144 if flags.contains(BuildEventArgsFieldFlags::LINE_NUMBER) {
145 fields.line_number = read_7bit_int(reader)?;
146 }
147
148 if flags.contains(BuildEventArgsFieldFlags::COLUMN_NUMBER) {
149 fields.column_number = read_7bit_int(reader)?;
150 }
151
152 if flags.contains(BuildEventArgsFieldFlags::END_LINE_NUMBER) {
153 fields.end_line_number = read_7bit_int(reader)?;
154 }
155
156 if flags.contains(BuildEventArgsFieldFlags::END_COLUMN_NUMBER) {
157 fields.end_column_number = read_7bit_int(reader)?;
158 }
159
160 if flags.contains(BuildEventArgsFieldFlags::ARGUMENTS) {
161 let count = read_7bit_count(reader, "event arguments count")?;
162 let mut args = Vec::with_capacity(count);
163 for _ in 0..count {
164 args.push(read_dedup_string(reader, strings)?);
165 }
166 fields.arguments = Some(args);
167 }
168
169 if (file_format_version < 13 && read_importance)
173 || (file_format_version >= 13 && flags.contains(BuildEventArgsFieldFlags::IMPORTANCE))
174 {
175 fields.importance = read_7bit_int(reader)?;
176 }
177
178 Ok(fields)
179}
180
181pub fn write_build_event_args_fields<W: Write>(
189 w: &mut W,
190 ctx: &mut WriteContext,
191 fields: &BuildEventArgsFields,
192 write_importance: bool,
193) -> std::io::Result<()> {
194 let flags = fields.flags;
195 write_7bit_int(w, flags.bits() as i32)?;
196
197 if flags.contains(BuildEventArgsFieldFlags::MESSAGE) {
198 write_dedup_string(w, ctx, fields.message.as_deref())?;
199 }
200
201 if flags.contains(BuildEventArgsFieldFlags::BUILD_EVENT_CONTEXT) {
202 let bec = fields.build_event_context.unwrap_or_default();
203 write_build_event_context(w, &bec, ctx.file_format_version)?;
204 }
205
206 if flags.contains(BuildEventArgsFieldFlags::THREAD_ID) {
207 write_7bit_int(w, fields.thread_id)?;
208 }
209
210 if flags.contains(BuildEventArgsFieldFlags::HELP_KEYWORD) {
211 write_dedup_string(w, ctx, fields.help_keyword.as_deref())?;
212 }
213
214 if flags.contains(BuildEventArgsFieldFlags::SENDER_NAME) {
215 write_dedup_string(w, ctx, fields.sender_name.as_deref())?;
216 }
217
218 if flags.contains(BuildEventArgsFieldFlags::TIMESTAMP) {
219 let ts = fields.timestamp.unwrap_or_default();
220 write_datetime(w, ts)?;
221 }
222
223 if flags.contains(BuildEventArgsFieldFlags::EXTENDED) {
224 let ext = fields.extended.clone().unwrap_or_default();
225 write_dedup_string(w, ctx, ext.extended_type.as_deref())?;
226 write_7bit_int(w, ext.extended_metadata_index)?;
227 write_dedup_string(w, ctx, ext.extended_data.as_deref())?;
228 }
229
230 if flags.contains(BuildEventArgsFieldFlags::SUBCATEGORY) {
231 write_dedup_string(w, ctx, fields.subcategory.as_deref())?;
232 }
233
234 if flags.contains(BuildEventArgsFieldFlags::CODE) {
235 write_dedup_string(w, ctx, fields.code.as_deref())?;
236 }
237
238 if flags.contains(BuildEventArgsFieldFlags::FILE) {
239 write_dedup_string(w, ctx, fields.file.as_deref())?;
240 }
241
242 if flags.contains(BuildEventArgsFieldFlags::PROJECT_FILE) {
243 write_dedup_string(w, ctx, fields.project_file.as_deref())?;
244 }
245
246 if flags.contains(BuildEventArgsFieldFlags::LINE_NUMBER) {
247 write_7bit_int(w, fields.line_number)?;
248 }
249
250 if flags.contains(BuildEventArgsFieldFlags::COLUMN_NUMBER) {
251 write_7bit_int(w, fields.column_number)?;
252 }
253
254 if flags.contains(BuildEventArgsFieldFlags::END_LINE_NUMBER) {
255 write_7bit_int(w, fields.end_line_number)?;
256 }
257
258 if flags.contains(BuildEventArgsFieldFlags::END_COLUMN_NUMBER) {
259 write_7bit_int(w, fields.end_column_number)?;
260 }
261
262 if flags.contains(BuildEventArgsFieldFlags::ARGUMENTS) {
263 let args = fields.arguments.as_deref().unwrap_or(&[]);
264 write_7bit_int(w, args.len() as i32)?;
265 for a in args {
266 write_dedup_string(w, ctx, a.as_deref())?;
267 }
268 }
269
270 if (ctx.file_format_version < 13 && write_importance)
271 || (ctx.file_format_version >= 13 && flags.contains(BuildEventArgsFieldFlags::IMPORTANCE))
272 {
273 write_7bit_int(w, fields.importance)?;
274 }
275
276 Ok(())
277}
278
279#[cfg(test)]
280mod tests;