Files
settled-reach/server/src/bin/atlas/main.rs
T

295 lines
8.4 KiB
Rust

//! Atlas CLI — query and manage celestial bodies and stations in systems.db.
//!
//! # Usage
//!
//! ```sh
//! # Via wrapper script (recommended):
//! tooling/atlas list-bodies --system "GJ 15A"
//! tooling/atlas list-bodies --type planet --inhabited
//! tooling/atlas show-system "GJ 15A"
//! tooling/atlas show-body "GJ 15Ab"
//! tooling/atlas add-body --system "GJ 15A" --type planet --orbit 1 --id "GJ 15Ab"
//! tooling/atlas stats
//! tooling/atlas corridor-status # remaining systems by corridor/hop
//! tooling/atlas populate # bulk classifier pass
//!
//! # Direct:
//! cargo run --bin atlas -- <subcommand> [args]
//! ```
use std::path::PathBuf;
use clap::{Parser, Subcommand};
mod author;
mod common;
mod mutate;
mod show;
mod stats;
mod sync_wiki;
mod systems;
use common::{open_db, resolve_db_path};
// ---------------------------------------------------------------------------
// CLI structure
// ---------------------------------------------------------------------------
#[derive(Parser)]
#[command(
name = "atlas",
about = "Query and manage celestial bodies and stations in systems.db"
)]
struct Cli {
/// Path to the SQLite database (default: auto-detect from worktree)
#[arg(long, global = true)]
db: Option<PathBuf>,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Show full system hierarchy (star → bodies → stations)
ShowSystem {
/// System ID (e.g., "GJ 15A")
system_id: String,
},
/// Show a single body's details
ShowBody {
/// Body ID (e.g., "GJ 15Ab")
body_id: String,
},
/// Show a single station's details
ShowStation {
/// Station ID (e.g., "GJ 15Ab-S1")
station_id: String,
},
/// List bodies with optional filters
ListBodies {
#[arg(long)]
system: Option<String>,
#[arg(long, value_name = "TYPE")]
r#type: Option<String>,
#[arg(long)]
inhabited: bool,
#[arg(long)]
unnamed: bool,
},
/// List stations with optional filters
ListStations {
#[arg(long)]
system: Option<String>,
#[arg(long, value_name = "TYPE")]
r#type: Option<String>,
},
/// Add a body record
AddBody {
#[arg(long)]
id: String,
#[arg(long)]
system: String,
#[arg(long, value_name = "TYPE")]
r#type: String,
#[arg(long)]
orbit: Option<i32>,
#[arg(long)]
name: Option<String>,
#[arg(long)]
parent: Option<String>,
#[arg(long)]
mass_class: Option<String>,
#[arg(long)]
atmosphere: Option<String>,
#[arg(long)]
gravity: Option<f64>,
#[arg(long)]
biome: Option<String>,
#[arg(long)]
inhabited: bool,
#[arg(long)]
population: Option<i64>,
},
/// Add a station record
AddStation {
#[arg(long)]
id: String,
#[arg(long)]
system: String,
#[arg(long)]
orbits: Option<String>,
#[arg(long, value_name = "TYPE")]
r#type: String,
#[arg(long)]
name: Option<String>,
#[arg(long)]
population: Option<i64>,
#[arg(long)]
docking: Option<String>,
#[arg(long)]
gate: bool,
},
/// Database statistics
Stats,
/// Generate a body/station proposal for a single system (writes JSON file for review)
Author {
/// System ID (e.g., "GJ 71")
system_id: String,
/// Output directory for proposal JSON (default: docs/atlas/proposals/)
#[arg(long, default_value = "docs/atlas/proposals")]
outdir: String,
/// Path to wiki directory (default: wiki/star-systems/)
#[arg(long, default_value = "wiki/star-systems")]
wiki: String,
},
/// Commit an approved proposal JSON to the database
CommitSystem {
/// Path to the proposal JSON file
path: String,
},
/// Wipe all bodies and stations for a single system
WipeSystem {
/// System ID
system_id: String,
},
/// List systems with optional filters
ListSystems {
/// Filter by geographic sector (e.g., "west_reach", "east_reach")
#[arg(long)]
sector: Option<String>,
/// Filter by hop distance
#[arg(long)]
hop: Option<i32>,
/// Only show systems that have bodies authored
#[arg(long)]
finished: bool,
/// Only show systems that have NO bodies yet
#[arg(long)]
unfinished: bool,
},
/// List systems that have no bodies yet
Unfinished {
/// Filter by geographic sector
#[arg(long)]
sector: Option<String>,
},
/// List unfinished systems at a specific hop distance (or next available hop)
Next {
/// Hop distance (omit to find the lowest hop with unfinished systems)
hop: Option<i32>,
/// Filter by geographic sector
#[arg(long)]
sector: Option<String>,
},
/// Sync body/station data into wiki page for a system (or all systems)
SyncWiki {
/// System ID (omit for all systems with bodies)
system_id: Option<String>,
/// Path to wiki directory (default: wiki/star-systems/)
#[arg(long, default_value = "wiki/star-systems")]
wiki: String,
},
/// Show remaining unfinished systems grouped by geographic sector and hop distance
CorridorStatus,
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
fn main() {
let cli = Cli::parse();
let db_path = resolve_db_path(cli.db);
let conn = open_db(&db_path);
match &cli.command {
Commands::ShowSystem { system_id } => show::cmd_show_system(&conn, system_id),
Commands::ShowBody { body_id } => show::cmd_show_body(&conn, body_id),
Commands::ShowStation { station_id } => show::cmd_show_station(&conn, station_id),
Commands::ListBodies {
system,
r#type,
inhabited,
unnamed,
} => systems::cmd_list_bodies(
&conn,
system.as_deref(),
r#type.as_deref(),
*inhabited,
*unnamed,
),
Commands::ListStations { system, r#type } => {
systems::cmd_list_stations(&conn, system.as_deref(), r#type.as_deref())
}
Commands::AddBody {
id,
system,
r#type,
orbit,
name,
parent,
mass_class,
atmosphere,
gravity,
biome,
inhabited,
population,
} => mutate::cmd_add_body(
&conn,
id,
system,
r#type,
*orbit,
name,
parent,
mass_class,
atmosphere,
*gravity,
biome,
*inhabited,
*population,
),
Commands::AddStation {
id,
system,
orbits,
r#type,
name,
population,
docking,
gate,
} => mutate::cmd_add_station(
&conn,
id,
system,
orbits,
r#type,
name,
*population,
docking,
*gate,
),
Commands::Stats => stats::cmd_stats(&conn),
Commands::Author {
system_id,
outdir,
wiki,
} => author::cmd_author(&conn, system_id, outdir, wiki),
Commands::CommitSystem { path } => author::cmd_commit_system(&conn, path),
Commands::WipeSystem { system_id } => mutate::cmd_wipe_system(&conn, system_id),
Commands::ListSystems {
sector,
hop,
finished,
unfinished,
} => systems::cmd_list_systems(&conn, sector.as_deref(), *hop, *finished, *unfinished),
Commands::Unfinished { sector } => systems::cmd_unfinished(&conn, sector.as_deref()),
Commands::Next { hop, sector } => systems::cmd_next(&conn, *hop, sector.as_deref()),
Commands::SyncWiki { system_id, wiki } => {
sync_wiki::cmd_sync_wiki(&conn, system_id.as_deref(), wiki)
}
Commands::CorridorStatus => systems::cmd_corridor_status(&conn),
}
}