1use std::fmt::{Debug, Display};
2
3use crate::{
4 parsed_log::{
5 ParsedCuLog, ParsedDataLog, ParsedFailedLog, ParsedInvokeLog, ParsedLog, ParsedOtherLog,
6 ParsedProgramLog, ParsedReturnLog, ParsedSuccessLog,
7 },
8 raw_log::{
9 RawCuLog, RawDataLog, RawFailedLog, RawInvokeLog, RawLog, RawOtherLog, RawProgramLog,
10 RawReturnLog, RawSuccessLog,
11 },
12 Result,
13};
14
15pub mod parsed;
16pub mod raw;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct ComputeUnits {
20 pub consumed: u64,
21 pub budget: u64,
22}
23
24impl From<RawCuLog<'_>> for ComputeUnits {
25 fn from(value: RawCuLog) -> Self {
26 ComputeUnits {
27 consumed: value.consumed,
28 budget: value.budget,
29 }
30 }
31}
32
33impl From<ParsedCuLog> for ComputeUnits {
34 fn from(value: ParsedCuLog) -> Self {
35 ComputeUnits {
36 consumed: value.consumed,
37 budget: value.budget,
38 }
39 }
40}
41
42struct StructuredLog<Id, ProgramResult, ProgramLog, DataLog, ReturnData, RawLog> {
63 pub program_id: Id,
64 pub depth: u8,
65 pub result: ProgramResult,
66 pub program_logs: Vec<ProgramLog>,
67 pub data_logs: Vec<DataLog>,
68 pub return_data: Option<ReturnData>,
69 pub compute_log: Option<ComputeUnits>,
70 pub cpi_logs: Vec<Self>,
71 pub raw_logs: Vec<RawLog>,
72}
73
74impl<Id, Program, Data, ReturnData, Err, RawLog>
75 StructuredLog<Id, ProgramResult<Err>, Program, Data, ReturnData, RawLog>
76where
77 Id: Eq + Debug + Display,
78 Program: Log<RawLog = RawLog>,
79 Data: Log<RawLog = RawLog>,
80{
81 #[allow(clippy::type_complexity)]
82 pub fn from_logs<Invoke, Success, Failed, Return, Compute, Other>(
83 logs: Vec<Log2<Invoke, Success, Failed, Program, Data, Return, Compute, Other>>,
84 ) -> Result<Vec<Self>>
85 where
86 Invoke: Log<RawLog = RawLog> + InvokeLog<ProgramId = Id>,
87 Success: Log<RawLog = RawLog> + SuccessLog<ProgramId = Id>,
88 Failed: Log<RawLog = RawLog> + FailedLog<ProgramId = Id, Err = Err>,
89 Return: Log<RawLog = RawLog> + ReturnLog<ProgramId = Id, Data = ReturnData>,
90 Compute: Log<RawLog = RawLog> + ComputeUnitsLog<ProgramId = Id> + Into<ComputeUnits>,
91 Other: Log<RawLog = RawLog>,
92 {
93 let mut stack = Vec::new();
94 let mut completed = Vec::new();
95
96 for log in logs {
97 match log {
98 Log2::Invoke(log) => {
99 stack.push(FrameBuilder::new(
100 log.program_id(),
101 log.depth(),
102 log.raw_log(),
103 ));
104 }
105 Log2::Success(log) => {
106 let builder = stack
107 .pop()
108 .expect("Unmatched success log without a prior invoke");
109 assert_eq!(
110 builder.program_id,
111 log.program_id(),
112 "Mismatched success: expected {}, got {}",
113 builder.program_id,
114 log.program_id()
115 );
116 let structured = builder.finalize(ProgramResult::Success, log.raw_log());
117
118 if let Some(parent) = stack.last_mut() {
119 parent.cpi_logs.push(structured);
120 } else {
121 completed.push(structured);
122 }
123 }
124 Log2::Failed(log) => {
125 let builder = stack
126 .pop()
127 .expect("Unmatched failed log without a prior invoke");
128 assert_eq!(
129 builder.program_id,
130 log.program_id(),
131 "Mismatched failed: expected {}, got {}",
132 builder.program_id,
133 log.program_id()
134 );
135 let structured = builder.finalize(ProgramResult::Err(log.err()), log.raw_log());
136
137 if let Some(parent) = stack.last_mut() {
138 parent.cpi_logs.push(structured);
139 } else {
140 completed.push(structured);
141 }
142 }
143 Log2::Log(log) => {
144 if let Some(top) = stack.last_mut() {
145 top.push_program_log(log);
146 }
147 }
148 Log2::Data(log) => {
149 if let Some(top) = stack.last_mut() {
150 top.push_data_log(log);
151 }
152 }
153 Log2::Return(log) => {
154 if let Some(top) = stack.last_mut() {
155 if top.program_id == log.program_id() {
156 top.set_return_data(log.data(), log.raw_log());
157 } else {
158 top.push_raw(log.raw_log());
159 }
160 }
161 }
162 Log2::Cu(log) => {
163 if let Some(top) = stack.last_mut() {
164 if top.program_id == log.program_id() {
165 top.set_compute_log(log);
166 } else {
167 top.push_raw(log.raw_log());
168 }
169 }
170 }
171 Log2::Other(log) => {
172 if let Some(top) = stack.last_mut() {
173 top.push_raw(log.raw_log());
174 }
175 }
176 }
177 }
178
179 assert!(
180 stack.is_empty(),
181 "Unbalanced log stack: {} frames left",
182 stack.len()
183 );
184 Ok(completed)
185 }
186}
187
188enum ProgramResult<Err> {
189 Success,
190 Err(Err),
191}
192
193pub(crate) enum Log2<Invoke, Success, Failed, Program, Data, Return, Cu, Other> {
194 Invoke(Invoke),
195 Success(Success),
196 Failed(Failed),
197 Log(Program),
198 Data(Data),
199 Return(Return),
200 Cu(Cu),
201 Other(Other),
202}
203
204impl<'a> From<RawLog<'a>>
205 for Log2<
206 RawInvokeLog<'a>,
207 RawSuccessLog<'a>,
208 RawFailedLog<'a>,
209 RawProgramLog<'a>,
210 RawDataLog<'a>,
211 RawReturnLog<'a>,
212 RawCuLog<'a>,
213 RawOtherLog<'a>,
214 >
215{
216 fn from(value: RawLog<'a>) -> Self {
217 match value {
218 RawLog::Invoke(raw_invoke_log) => Log2::Invoke(raw_invoke_log),
219 RawLog::Success(raw_success_log) => Log2::Success(raw_success_log),
220 RawLog::Failed(raw_failed_log) => Log2::Failed(raw_failed_log),
221 RawLog::Log(raw_program_log) => Log2::Log(raw_program_log),
222 RawLog::Data(raw_data_log) => Log2::Data(raw_data_log),
223 RawLog::Return(raw_return_log) => Log2::Return(raw_return_log),
224 RawLog::Cu(raw_cu_log) => Log2::Cu(raw_cu_log),
225 RawLog::Other(raw_other) => Log2::Other(raw_other),
226 }
227 }
228}
229
230impl From<ParsedLog>
231 for Log2<
232 ParsedInvokeLog,
233 ParsedSuccessLog,
234 ParsedFailedLog,
235 ParsedProgramLog,
236 ParsedDataLog,
237 ParsedReturnLog,
238 ParsedCuLog,
239 ParsedOtherLog,
240 >
241{
242 fn from(value: ParsedLog) -> Self {
243 match value {
244 ParsedLog::Invoke(invoke_log) => Log2::Invoke(invoke_log),
245 ParsedLog::Success(success_log) => Log2::Success(success_log),
246 ParsedLog::Failed(failed_log) => Log2::Failed(failed_log),
247 ParsedLog::Log(program_log) => Log2::Log(program_log),
248 ParsedLog::Data(data_log) => Log2::Data(data_log),
249 ParsedLog::Return(return_log) => Log2::Return(return_log),
250 ParsedLog::Cu(cu_log) => Log2::Cu(cu_log),
251 ParsedLog::Other(other) => Log2::Other(other),
252 }
253 }
254}
255
256pub(crate) trait Log {
257 type RawLog;
258
259 fn raw_log(&self) -> Self::RawLog;
260}
261
262pub(crate) trait InvokeLog {
263 type ProgramId;
264
265 fn program_id(&self) -> Self::ProgramId;
266 fn depth(&self) -> u8;
267}
268
269pub(crate) trait SuccessLog {
270 type ProgramId;
271
272 fn program_id(&self) -> Self::ProgramId;
273}
274
275pub(crate) trait FailedLog {
276 type ProgramId;
277 type Err;
278
279 fn program_id(&self) -> Self::ProgramId;
280 fn err(&self) -> Self::Err;
281}
282
283pub(crate) trait ReturnLog {
284 type ProgramId;
285 type Data;
286
287 fn program_id(&self) -> Self::ProgramId;
288 fn data(&self) -> Self::Data;
289}
290
291pub(crate) trait ComputeUnitsLog {
292 type ProgramId;
293
294 fn program_id(&self) -> Self::ProgramId;
295}
296
297struct FrameBuilder<Id, ProgramResult, ProgramLog, DataLog, ReturnData, RawLog> {
298 program_id: Id,
299 depth: u8,
300 program_logs: Vec<ProgramLog>,
301 data_logs: Vec<DataLog>,
302 return_data: Option<ReturnData>,
303 compute_log: Option<ComputeUnits>,
304 raw_logs: Vec<RawLog>,
305 cpi_logs: Vec<StructuredLog<Id, ProgramResult, ProgramLog, DataLog, ReturnData, RawLog>>,
306}
307
308impl<Id, ProgramResult, ProgramLog, DataLog, ReturnData, RawLog>
309 FrameBuilder<Id, ProgramResult, ProgramLog, DataLog, ReturnData, RawLog>
310where
311 Id: Eq + Debug + Display,
312 ProgramLog: Log<RawLog = RawLog>,
313 DataLog: Log<RawLog = RawLog>,
314{
315 fn new(program_id: Id, depth: u8, raw: RawLog) -> Self {
316 Self {
317 program_id,
318 depth,
319 program_logs: vec![],
320 data_logs: vec![],
321 return_data: None,
322 compute_log: None,
323 raw_logs: vec![raw],
324 cpi_logs: vec![],
325 }
326 }
327
328 fn push_program_log(&mut self, log: ProgramLog) {
329 self.raw_logs.push(log.raw_log());
330 self.program_logs.push(log);
331 }
332
333 fn push_data_log(&mut self, log: DataLog) {
334 self.raw_logs.push(log.raw_log());
335 self.data_logs.push(log);
336 }
337
338 fn push_raw(&mut self, raw: RawLog) {
339 self.raw_logs.push(raw);
340 }
341
342 fn set_return_data(&mut self, data: ReturnData, raw: RawLog) {
343 self.raw_logs.push(raw);
344 self.return_data = Some(data);
345 }
346
347 fn set_compute_log<ComputeLog>(&mut self, log: ComputeLog)
348 where
349 ComputeLog: Log<RawLog = RawLog> + Into<ComputeUnits>,
350 {
351 self.raw_logs.push(log.raw_log());
352 self.compute_log = Some(log.into());
353 }
354
355 fn finalize(
356 mut self,
357 result: ProgramResult,
358 final_raw: RawLog,
359 ) -> StructuredLog<Id, ProgramResult, ProgramLog, DataLog, ReturnData, RawLog> {
360 self.raw_logs.push(final_raw);
361 self.raw_logs.shrink_to_fit();
362 self.cpi_logs.shrink_to_fit();
363 self.data_logs.shrink_to_fit();
364 self.program_logs.shrink_to_fit();
365
366 StructuredLog {
367 program_id: self.program_id,
368 depth: self.depth,
369 result,
370 program_logs: self.program_logs,
371 data_logs: self.data_logs,
372 return_data: self.return_data,
373 compute_log: self.compute_log,
374 cpi_logs: self.cpi_logs,
375 raw_logs: self.raw_logs,
376 }
377 }
378}