1use std::fs;
2use std::path::Path;
3
4use kaya_core::Result;
5
6use crate::{decode_record, DecodeRecordResult, WalRecordType, WalWarning};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct WalInspectionRow {
10 pub offset: u64,
11 pub lsn: u64,
12 pub sequence: u64,
13 pub record_type: WalRecordType,
14 pub key_len: Option<usize>,
15 pub value_len: Option<usize>,
16}
17
18#[derive(Debug, Clone, Default, PartialEq, Eq)]
19pub struct WalInspection {
20 pub segment: String,
21 pub rows: Vec<WalInspectionRow>,
22 pub warnings: Vec<WalWarning>,
23}
24
25pub fn inspect_wal_path(path: impl AsRef<Path>, max_record_bytes: u32) -> Result<WalInspection> {
26 let path = path.as_ref();
27 let bytes = fs::read(path)?;
28 let segment = path
29 .file_name()
30 .map(|name| name.to_string_lossy().into_owned())
31 .unwrap_or_else(|| path.display().to_string());
32 let mut offset = 0_usize;
33 let mut rows = Vec::new();
34 let mut warnings = Vec::new();
35
36 while offset < bytes.len() {
37 match decode_record(&bytes[offset..], offset as u64, max_record_bytes) {
38 DecodeRecordResult::Complete { record, bytes_read } => {
39 rows.push(WalInspectionRow {
40 offset: offset as u64,
41 lsn: record.lsn.get(),
42 sequence: record.sequence.get(),
43 record_type: record.record_type(),
44 key_len: record.payload.key_len(),
45 value_len: record.payload.value_len(),
46 });
47 offset += bytes_read;
48 }
49 DecodeRecordResult::Incomplete { warning }
50 | DecodeRecordResult::Invalid { warning } => {
51 warnings.push(warning);
52 break;
53 }
54 }
55 }
56
57 Ok(WalInspection {
58 segment,
59 rows,
60 warnings,
61 })
62}