1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//! `liminal-server checkpoint-census` (DUR-024 R5): what one conversation's
//! checkpoint holds, counted and printed, never published; and without a
//! conversation (DUR-028 R6), what the whole store holds.
//!
//! The command opens the configured store (or a copy the operator names),
//! replays the conversation exactly as a rebuild would, prepares its
//! checkpoint capture exactly as a publication would, and prints one plain
//! line per figure: history nodes by kind with their bytes, the identities
//! captured, the operation log and outbox rows ahead of and behind the
//! latest locator, the body's bytes, the bound and the batches a publication
//! would commit. It writes nothing, prints no row body, and is reachable
//! from the command line only.
use std::path::Path;
use crate::error::ServerError;
use crate::server::participant::{checkpoint_census, store_census};
/// Counts one conversation's checkpoint, or the whole store when no
/// conversation is named, and answers the command's lines.
///
/// The store read is `store_path` when one is named and the configuration's
/// persistence path otherwise; the figures come back as the lines the
/// command prints.
///
/// # Errors
/// [`ServerError::ConfigValidation`] when the configuration names no
/// persistence path (and none was given) or no `[participant]` section, when
/// the store cannot be opened, or when the census refuses a step, named.
pub fn run_checkpoint_census(
config_path: &Path,
conversation_id: Option<u64>,
store_path: Option<&Path>,
) -> Result<Vec<String>, ServerError> {
let config = crate::config::file::load_config(config_path)?;
let Some(participant) = config.participant.as_ref() else {
return Err(ServerError::ConfigValidation {
message: "the checkpoint census needs the [participant] section".to_owned(),
});
};
let Some(persistence_path) = store_path.or(config.persistence_path.as_deref()) else {
return Err(ServerError::ConfigValidation {
message: "the checkpoint census needs persistence_path or --store; an ephemeral store has no checkpoint to count".to_owned(),
});
};
let store = crate::server::connection::services::open_persistent_store(persistence_path)?;
let Some(conversation_id) = conversation_id else {
return store_census(&store)
.map(|census| census.lines())
.map_err(|error| ServerError::ConfigValidation {
message: format!("store census: {error}"),
});
};
let census = checkpoint_census(&store, participant, conversation_id).map_err(|error| {
ServerError::ConfigValidation {
message: format!("checkpoint census of conversation {conversation_id}: {error}"),
}
})?;
Ok(census.lines())
}
#[cfg(test)]
#[path = "checkpoint_census_tests.rs"]
mod tests;