use std::process::ExitCode;
use clap::Parser;
use memstead_cli::CliError;
use memstead_cli::cli::{Cli, Command};
use memstead_cli::commands;
use memstead_cli::output::{ExitKind, print_cli_error};
use memstead_cli::setup;
fn main() -> ExitCode {
let cli = Cli::parse();
let json_mode = cli.json;
let verb = cli.command.verb();
match run(cli) {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
let cli_err = e.downcast_ref::<CliError>();
let kind = cli_err.map(|c| c.kind).unwrap_or(ExitKind::Generic);
let code = cli_err.map(|c| c.effective_code()).unwrap_or("INTERNAL");
let details = cli_err.and_then(|c| c.details.as_ref());
if let Ok(cwd) = std::env::current_dir()
&& let Some(root) = setup::find_workspace_root(&cwd)
{
memstead_base::friction::FrictionLedger::for_workspace(&root).record(
"cli",
verb,
code,
memstead_base::friction::closed_reason(code, details),
);
}
print_cli_error(code, &format!("{e:#}"), kind, json_mode, details);
ExitCode::from(kind as u8)
}
}
}
fn run(cli: Cli) -> anyhow::Result<()> {
let override_path = cli
.workspace
.clone()
.or_else(|| std::env::var_os("MEMSTEAD_WORKSPACE").map(std::path::PathBuf::from));
if let Some(root) = override_path {
if !memstead_base::is_workspace_root(&root) {
return Err(setup::workspace_not_initialised_error(&format!(
"workspace override `{}` (from --workspace or MEMSTEAD_WORKSPACE) does not \
carry `.memstead/workspace.toml` — refusing rather than falling back to \
the directory walk",
root.display()
))
.into());
}
std::env::set_current_dir(&root).map_err(|e| {
CliError::new(
ExitKind::Generic,
"INVALID_INPUT",
format!("cannot enter workspace override {}: {e}", root.display()),
)
})?;
}
let role = match cli.role.as_deref() {
None => memstead_base::vcs::Role::Unspecified,
Some(s) => match memstead_base::vcs::Role::from_wire(s) {
Some(r) => r,
None => {
return Err(CliError::new(
ExitKind::Validation,
"INVALID_ROLE",
format!(
"unknown role {s:?} — declarable roles: {}",
memstead_base::vcs::Role::DECLARABLE.join(", ")
),
)
.into());
}
},
};
let ctx = setup::CliContext {
json: cli.json,
quiet: cli.quiet,
role,
};
match cli.command {
Command::Status => commands::status::run(&ctx),
Command::Entity(args) => commands::entity::run(&ctx, args),
Command::Relations(args) => commands::relations::run(&ctx, args),
Command::Search(args) => commands::search::run(&ctx, args),
Command::List(args) => commands::list::run(&ctx, args),
Command::Context(args) => commands::context::run(&ctx, args),
Command::Overview(args) => commands::overview::run(&ctx, args),
Command::Type(args) => commands::type_cmd::run(&ctx, args),
Command::Health(args) => commands::health::run(&ctx, args),
Command::Due(args) => commands::due::run(&ctx, args),
Command::Export(args) => commands::export::run(&ctx, args),
Command::Init(args) => commands::init::run(&ctx, args),
Command::Quickstart(args) => commands::quickstart::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Install(args) => commands::install::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Uninstall(args) => commands::uninstall::run(&ctx, args),
Command::VerifyAnchors(args) => commands::verify_anchors::run(&ctx, args),
Command::Link(args) => commands::link::run(&ctx, args),
Command::Publish(args) => commands::publish::run(&ctx, args),
Command::Unpublish(args) => commands::unpublish::run(&ctx, args),
Command::Domain { action } => commands::domain::run(&ctx, action),
Command::Admin { action } => match action {
commands::admin::AdminAction::Takedown(args) => {
commands::admin::run_takedown(&ctx, args)
}
commands::admin::AdminAction::Denylist(args) => {
commands::admin::run_denylist(&ctx, args)
}
},
Command::Login(args) => commands::login::run(&ctx, args),
Command::Logout(args) => commands::logout::run(&ctx, args),
Command::Create(args) => commands::create::run(&ctx, args),
Command::Update(args) => commands::update::run(&ctx, args),
Command::Relate(args) => commands::relate::run(&ctx, args),
Command::Delete(args) => commands::delete::run(&ctx, args),
Command::Rename(args) => commands::rename::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::BatchUpdate(args) => commands::batch_update::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::BatchCreate(args) => commands::batch_create::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::BatchRelate(args) => commands::batch_relate::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Recover(args) => commands::recover::run(&ctx, args),
Command::Anchors(args) => commands::anchors::run(&ctx, args),
Command::Changes(args) => commands::changes::run(&ctx, args),
Command::Check(args) => commands::check::run(&ctx, args),
Command::ReviewMark(args) => commands::review_mark::run(&ctx, args),
Command::Reload(args) => commands::reload::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Fetch(args) => commands::transport::run_fetch(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Pull(args) => commands::transport::run_pull(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Push(args) => commands::transport::run_push(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::BranchReset(args) => commands::branch_reset::run(&ctx, args),
#[cfg(feature = "mem-repo")]
Command::Mem { action } => match action {
commands::mem::MemAction::Init(args) => commands::mem::run(&ctx, args),
commands::mem::MemAction::Unregister(args) => commands::mem::run_unregister(&ctx, args),
commands::mem::MemAction::Delete(args) => commands::mem::run_delete(&ctx, args),
commands::mem::MemAction::Rename(args) => commands::mem::run_rename(&ctx, args),
commands::mem::MemAction::SetVersion(args) => {
commands::mem::run_set_version(&ctx, args)
}
commands::mem::MemAction::SetSchema(args) => commands::mem::run_set_schema(&ctx, args),
commands::mem::MemAction::SetDescription(args) => {
commands::mem::run_set_description(&ctx, args)
}
commands::mem::MemAction::SetTitle(args) => commands::mem::run_set_title(&ctx, args),
commands::mem::MemAction::SetSubject(args) => {
commands::mem::run_set_subject(&ctx, args)
}
commands::mem::MemAction::SetInternal(args) => {
commands::mem::run_set_internal(&ctx, args)
}
commands::mem::MemAction::SetSyncState(args) => {
commands::mem::run_set_sync_state(&ctx, args)
}
commands::mem::MemAction::List(args) => commands::mem::run_list(&ctx, args),
},
#[cfg(feature = "mem-repo")]
Command::MemRepo { action } => commands::mem_repo::run(&ctx, action),
#[cfg(feature = "mem-repo")]
Command::Workspace { action } => commands::workspace::run(&ctx, action),
Command::Schema(args) => commands::schema::run(&ctx, args),
Command::Projection(args) => commands::projection::run(&ctx, args),
}
}