Introduce lifecycle-aware Layer::Empty variant and update callers
The `Layer` enum is transformed into a lifecycle-aware state machine with an `Empty` variant representing an unconstructed directory. Read and query operations now explicitly panic when invoked on this state, enforcing explicit progression through `create()` before use. Iterator methods are updated to handle the new variant exhaustively, and module visibility constants are adjusted to support the refactored structure.
This commit is contained in:
@@ -183,6 +183,7 @@ impl SiblingLayerExt for obilayeredmap::Layer {
|
||||
match self {
|
||||
obilayeredmap::Layer::Count(l) => l.iter_siblings(annex),
|
||||
obilayeredmap::Layer::Presence(l) => l.iter_siblings(annex),
|
||||
obilayeredmap::Layer::Empty { .. } => panic!("iter_siblings() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +191,7 @@ impl SiblingLayerExt for obilayeredmap::Layer {
|
||||
match self {
|
||||
obilayeredmap::Layer::Count(l) => l.iter_siblings_batch(annex, batch_size),
|
||||
obilayeredmap::Layer::Presence(l) => l.iter_siblings_batch(annex, batch_size),
|
||||
obilayeredmap::Layer::Empty { .. } => panic!("iter_siblings_batch() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +199,7 @@ impl SiblingLayerExt for obilayeredmap::Layer {
|
||||
match self {
|
||||
obilayeredmap::Layer::Count(l) => l.iter_minorants(annex),
|
||||
obilayeredmap::Layer::Presence(l) => l.iter_minorants(annex),
|
||||
obilayeredmap::Layer::Empty { .. } => panic!("iter_minorants() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +207,7 @@ impl SiblingLayerExt for obilayeredmap::Layer {
|
||||
match self {
|
||||
obilayeredmap::Layer::Count(l) => l.iter_minorants_batch(annex, batch_size),
|
||||
obilayeredmap::Layer::Presence(l) => l.iter_minorants_batch(annex, batch_size),
|
||||
obilayeredmap::Layer::Empty { .. } => panic!("iter_minorants_batch() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,25 +12,50 @@
|
||||
//! reimplement locally, minus its one sibling-specific method
|
||||
//! (`iter_minorants_batch`, which stays an extension trait over there —
|
||||
//! `obilayeredmap` has no business knowing about sibling annexes).
|
||||
//!
|
||||
//! `Layer` is meant to eventually represent a layer's *whole* life —
|
||||
//! empty shell, under construction, ready to read — not just the
|
||||
//! ready-to-read state, so the same type follows a layer from creation
|
||||
//! through querying instead of construction living as disconnected free
|
||||
//! functions elsewhere that happen to write into the same directory (see
|
||||
//! `DevDocMD/implementation/partition_layer_cache.md`). [`Layer::Empty`]
|
||||
//! is the first step: a directory and nothing else, able to hand out the
|
||||
//! paths a builder needs, not yet able to build anything itself.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use obicompactvec::{PersistentBitMatrix, PersistentCompactIntMatrix};
|
||||
use obikseq::CanonicalKmer;
|
||||
|
||||
use crate::error::OLMResult;
|
||||
use crate::layer::{LayerContent, TypedLayer, COUNTS_DIR};
|
||||
use crate::layer::{LayerContent, TypedLayer, COUNTS_DIR, PRESENCE_DIR};
|
||||
use crate::meta::IndexMode;
|
||||
use crate::mphf_layer::EvidenceKind;
|
||||
use crate::mphf_layer::{EvidenceKind, EVIDENCE_FILE, FINGERPRINT_FILE, MPHF_FILE, UNITIGS_FILE};
|
||||
|
||||
/// One layer, its content (`Count`/`Presence`) resolved at open time rather
|
||||
/// than at compile time — see the module docs.
|
||||
/// One layer, at any point in its life — see the module docs. Only
|
||||
/// [`Empty`](Layer::Empty) and the two ready-to-read states
|
||||
/// (`Count`/`Presence`) exist so far; states in between (unitigs written,
|
||||
/// MPHF built, evidence built, matrix not yet built) are not represented
|
||||
/// yet.
|
||||
pub enum Layer {
|
||||
/// Just a directory — nothing built yet. Every method below other than
|
||||
/// the path accessors panics on this variant: calling them means the
|
||||
/// caller assumed a layer was ready when it wasn't, an implementation
|
||||
/// error to surface loudly, not paper over with a default value.
|
||||
Empty { dir: PathBuf },
|
||||
Count(TypedLayer<PersistentCompactIntMatrix>),
|
||||
Presence(TypedLayer<PersistentBitMatrix>),
|
||||
}
|
||||
|
||||
impl Layer {
|
||||
/// An empty shell at `dir` — creates the directory (if it doesn't
|
||||
/// already exist) but nothing inside it. The starting state for
|
||||
/// building a new layer.
|
||||
pub fn create(dir: &Path) -> std::io::Result<Self> {
|
||||
std::fs::create_dir_all(dir)?;
|
||||
Ok(Layer::Empty { dir: dir.to_owned() })
|
||||
}
|
||||
|
||||
/// Open one layer, auto-detecting count vs. presence from what is
|
||||
/// actually on disk (`counts/` present and wanted, else presence) — the
|
||||
/// single source of truth every caller that opens a layer's own matrix
|
||||
@@ -46,10 +71,40 @@ impl Layer {
|
||||
TypedLayer::<PersistentBitMatrix>::open(layer_dir, mode).map(Layer::Presence)
|
||||
}
|
||||
|
||||
// ── Paths — only meaningful before anything is built ───────────────
|
||||
//
|
||||
// Once a layer is `Count`/`Presence`, its caller already knows the
|
||||
// directory (it had to pass it to `open`) — these exist for builder
|
||||
// code holding an `Empty` layer, so the `mphf.bin`/`unitigs.bin`/…
|
||||
// naming stays defined once, here, rather than re-declared as
|
||||
// string literals at every call site that writes into a layer
|
||||
// directory (the same duplication `layer_dir`/`index_dir` fixed one
|
||||
// level up — see `DevDocMD/implementation/partition_layer_cache.md`).
|
||||
|
||||
/// This layer's own directory. Panics on `Count`/`Presence` — by that
|
||||
/// point the caller already has the directory it opened with; asking
|
||||
/// again here would mean it lost track of its own state.
|
||||
pub fn dir(&self) -> &Path {
|
||||
match self {
|
||||
Layer::Empty { dir } => dir,
|
||||
_ => panic!("Layer::dir() only available on Empty — caller already has this path"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mphf_path(&self) -> PathBuf { self.dir().join(MPHF_FILE) }
|
||||
pub fn unitigs_path(&self) -> PathBuf { self.dir().join(UNITIGS_FILE) }
|
||||
pub fn evidence_path(&self) -> PathBuf { self.dir().join(EVIDENCE_FILE) }
|
||||
pub fn fingerprint_path(&self) -> PathBuf { self.dir().join(FINGERPRINT_FILE) }
|
||||
pub fn counts_dir(&self) -> PathBuf { self.dir().join(COUNTS_DIR) }
|
||||
pub fn presence_dir(&self) -> PathBuf { self.dir().join(PRESENCE_DIR) }
|
||||
|
||||
// ── Ready-only surface ───────────────────────────────────────────────
|
||||
|
||||
pub fn content(&self) -> LayerContent {
|
||||
match self {
|
||||
Layer::Count(_) => LayerContent::Count,
|
||||
Layer::Presence(_) => LayerContent::Presence,
|
||||
Layer::Empty { .. } => panic!("Layer::content() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +112,7 @@ impl Layer {
|
||||
match self {
|
||||
Layer::Count(l) => l.evidence_kind(),
|
||||
Layer::Presence(l) => l.evidence_kind(),
|
||||
Layer::Empty { .. } => panic!("Layer::evidence_kind() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +120,7 @@ impl Layer {
|
||||
match self {
|
||||
Layer::Count(l) => l.n(),
|
||||
Layer::Presence(l) => l.n(),
|
||||
Layer::Empty { .. } => panic!("Layer::n() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +128,7 @@ impl Layer {
|
||||
match self {
|
||||
Layer::Count(l) => l.find_slot(kmer),
|
||||
Layer::Presence(l) => l.find_slot(kmer),
|
||||
Layer::Empty { .. } => panic!("Layer::find_slot() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +139,7 @@ impl Layer {
|
||||
match self {
|
||||
Layer::Count(l) => l.index_batch(kmers),
|
||||
Layer::Presence(l) => l.index_batch(kmers),
|
||||
Layer::Empty { .. } => panic!("Layer::index_batch() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +147,7 @@ impl Layer {
|
||||
match self {
|
||||
Layer::Count(l) => l.n_cols(),
|
||||
Layer::Presence(l) => l.n_cols(),
|
||||
Layer::Empty { .. } => panic!("Layer::n_cols() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +168,7 @@ impl Layer {
|
||||
o.extend(c.iter().map(|&v| v != 0));
|
||||
}
|
||||
}
|
||||
Layer::Empty { .. } => panic!("Layer::fill_sub_matrix_carries() called on an Empty layer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::mphf_layer::MphfLayer;
|
||||
pub(crate) use crate::mphf_layer::UNITIGS_FILE;
|
||||
|
||||
pub(crate) const COUNTS_DIR: &str = "counts";
|
||||
const PRESENCE_DIR: &str = "presence";
|
||||
pub(crate) const PRESENCE_DIR: &str = "presence";
|
||||
|
||||
// ── Trait ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user