use std::path::PathBuf;
#[path = "cli/parse.rs"]
mod parse;
#[cfg(test)]
#[path = "cli/tests.rs"]
mod tests;
#[derive(Debug, PartialEq)]
pub(crate) enum Command {
Help,
PutEdge {
node_id: u64,
version: u64,
payload: Vec<u8>,
},
PutVector {
node_id: u64,
version: u64,
payload: Vec<u8>,
},
PutFull {
node_id: u64,
version: u64,
adjacency: Vec<u64>,
},
Get {
node_id: u64,
},
Traverse {
node_id: u64,
},
Recover,
Stats,
Health,
Metrics,
MetricsProm,
Status,
TriggerCompaction {
level: Option<u32>,
},
Recluster {
force: bool,
},
IngestEdge {
node_id: u64,
version: u64,
payload: Vec<u8>,
},
IngestFile {
path: PathBuf,
batch_size: usize,
},
IngestVector {
node_id: u64,
version: u64,
payload: Vec<u8>,
},
IngestNode {
node_id: u64,
version: u64,
adjacency: Vec<u64>,
},
IngestBatchEdge {
start_node_id: u64,
count: u64,
version: u64,
payload_prefix: String,
},
IngestBatchEdgeLoop {
start_node_id: u64,
count: u64,
rounds: u64,
version: u64,
payload_prefix: String,
},
Parse {
query: String,
},
Explain {
query: String,
},
Query {
query: String,
},
ContractReport {
scenario: String,
},
EvidenceReport {
surface: String,
},
ServiceReport {
listen: Option<String>,
},
ServiceValidate {
listen: Option<String>,
telemetry_endpoint: Option<String>,
tls: String,
admin_token: Option<String>,
max_requests: usize,
},
ServiceServe {
listen: Option<String>,
telemetry_endpoint: Option<String>,
tls: String,
admin_token: Option<String>,
max_requests: usize,
},
Bench {
iterations: u64,
seed: u64,
json_out: Option<PathBuf>,
},
CollectStats,
}
#[derive(Debug, PartialEq)]
pub(crate) struct ParsedArgs {
pub command: Command,
pub data_dir: Option<PathBuf>,
pub wal_sync_policy: Option<String>,
pub wal_sync_interval_ms: Option<u64>,
}
pub(crate) fn help_text() -> &'static str {
concat!(
"Iridium CLI\n\n",
"Usage:\n",
" ir <command> [options]\n",
" ir --data <path> <command> [options]\n",
" ir --help\n\n",
"Global Options:\n",
" --data <path> Data root directory (contains wal/sst/manifest)\n\n",
" --wal-sync-policy <always|interval|manual> WAL durability policy\n",
" --wal-sync-interval-ms <u64> WAL interval sync millis (interval policy)\n\n",
"Core Commands:\n",
" help | -h | --help Show this help text\n",
" parse <query> Parse query and print AST JSON\n",
" explain <query> Explain query plan JSON\n",
" query <query> Execute query and print row stream JSON\n",
" contract-report <acceptance|retrieval-quality>\n",
" Print shared Alloy assurance/telemetry contract JSON\n",
" evidence-report <restart-requery>\n",
" Print typed Iridium scenario evidence JSON\n",
" service-report [--listen <host:port>]\n",
" Print first service-path contract JSON\n",
" service-validate [--listen <host:port>] [--telemetry-endpoint <target>] [--tls <disabled|operator-optional>] [--admin-token <token>] [--max-requests <n>]\n",
" Validate the first service candidate config surface\n",
" service-serve [--listen <host:port>] [--telemetry-endpoint <target>] [--tls <disabled|operator-optional>] [--admin-token <token>] [--max-requests <n>]\n",
" Run the first minimal service candidate surface\n",
" bench <iterations> [--seed <u64>] [--json <path>]\n",
" Run benchmark suite\n\n",
"Storage Commands:\n",
" put-edge <node_id> <version> <payload>\n",
" put-vector <node_id> <version> <csv_f32>\n",
" put-full <node_id> <version> <adjacency_csv>\n",
" get <node_id>\n",
" traverse <node_id>\n",
" recover\n",
" stats\n",
" status\n",
" trigger-compaction [--level <u32>]\n",
" recluster [--force]\n\n",
"Ingest Commands:\n",
" ingest-edge <node_id> <version> <payload>\n",
" ingest-vector <node_id> <version> <csv_f32>\n",
" ingest-node <node_id> <version> <adjacency_csv>\n",
" ingest-batch-edge <start_node_id> <count> <version> <payload_prefix>\n",
" ingest-batch-edge-loop <start_node_id> <count> <rounds> <version> <payload_prefix>\n",
" ingest --file <path> [--batch-size <n>]\n\n",
"Ops Commands:\n",
" health\n",
" metrics\n",
" metrics-prom\n\n",
"Planner Commands:\n",
" collect-stats Collect planner statistics from storage and arm the cost-based optimizer\n\n",
"Notes:\n",
" - Default data root is ./data for storage/query/ingest commands.\n",
" - Query runtime tuning env vars: IR_QUERY_MORSEL_SIZE,\n",
" IR_QUERY_PARALLEL_WORKERS, IR_QUERY_SCAN_LIMIT_MULTIPLIER,\n",
" IR_QUERY_SCAN_MIN.\n",
)
}
pub(crate) fn parse_args(args: &[String]) -> Result<ParsedArgs, String> {
if args.len() < 2 {
return Ok(ParsedArgs {
command: Command::Help,
data_dir: None,
wal_sync_policy: None,
wal_sync_interval_ms: None,
});
}
let mut idx = 1usize;
let mut data_dir: Option<PathBuf> = None;
let mut wal_sync_policy: Option<String> = None;
let mut wal_sync_interval_ms: Option<u64> = None;
while idx < args.len() {
match args[idx].as_str() {
"--data" => {
if idx + 1 >= args.len() {
return Err("missing value for --data".to_string());
}
data_dir = Some(PathBuf::from(&args[idx + 1]));
idx += 2;
}
"--wal-sync-policy" => {
if idx + 1 >= args.len() {
return Err("missing value for --wal-sync-policy".to_string());
}
wal_sync_policy = Some(args[idx + 1].to_ascii_lowercase());
idx += 2;
}
"--wal-sync-interval-ms" => {
if idx + 1 >= args.len() {
return Err("missing value for --wal-sync-interval-ms".to_string());
}
wal_sync_interval_ms = Some(
args[idx + 1]
.parse::<u64>()
.map_err(|_| "invalid --wal-sync-interval-ms".to_string())?,
);
idx += 2;
}
"help" | "-h" | "--help" => {
return Ok(ParsedArgs {
command: Command::Help,
data_dir,
wal_sync_policy,
wal_sync_interval_ms,
});
}
_ => break,
}
}
if idx >= args.len() {
return Ok(ParsedArgs {
command: Command::Help,
data_dir,
wal_sync_policy,
wal_sync_interval_ms,
});
}
let mut command_args = Vec::with_capacity(args.len() - idx + 1);
command_args.push(args[0].clone());
command_args.extend_from_slice(&args[idx..]);
let command = parse::parse_command_args(&command_args)?;
Ok(ParsedArgs {
command,
data_dir,
wal_sync_policy,
wal_sync_interval_ms,
})
}