use std::sync::Arc;
use nodedb_columnar::ColumnarError;
use crate::storage::quarantine::error::QuarantineError;
use crate::storage::quarantine::registry::{QuarantineEngine, QuarantineRegistry, SegmentKey};
pub fn open_segment_with_quarantine<'a>(
registry: &Arc<QuarantineRegistry>,
seg_bytes: &'a [u8],
collection: &str,
segment_id: &str,
) -> Result<nodedb_columnar::reader::SegmentReader<'a>, ColumnarOrQuarantine> {
match nodedb_columnar::SegmentReader::open(seg_bytes) {
Ok(reader) => {
let key = SegmentKey {
engine: QuarantineEngine::Columnar,
collection: collection.to_string(),
segment_id: segment_id.to_string(),
};
registry.record_success(&key);
Ok(reader)
}
Err(e) if is_crc_class(&e) => {
let key = SegmentKey {
engine: QuarantineEngine::Columnar,
collection: collection.to_string(),
segment_id: segment_id.to_string(),
};
registry
.record_failure(key, &e.to_string(), None)
.map_err(ColumnarOrQuarantine::Quarantined)?;
Err(ColumnarOrQuarantine::Columnar(e))
}
Err(e) => Err(ColumnarOrQuarantine::Columnar(e)),
}
}
#[derive(Debug, thiserror::Error)]
pub enum ColumnarOrQuarantine {
#[error(transparent)]
Columnar(#[from] ColumnarError),
#[error(transparent)]
Quarantined(#[from] QuarantineError),
}
fn is_crc_class(e: &ColumnarError) -> bool {
matches!(
e,
ColumnarError::FooterCrcMismatch { .. }
| ColumnarError::Corruption { .. }
| ColumnarError::TruncatedSegment { .. }
| ColumnarError::InvalidMagic(_)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_bytes_triggers_quarantine_path() {
let reg = Arc::new(QuarantineRegistry::new());
let r1 = open_segment_with_quarantine(®, &[], "coll", "seg1");
assert!(matches!(r1, Err(ColumnarOrQuarantine::Columnar(_))));
let r2 = open_segment_with_quarantine(®, &[], "coll", "seg1");
assert!(matches!(r2, Err(ColumnarOrQuarantine::Quarantined(_))));
}
}