use super::context::{fail, fail_for};
use super::output::{CommandData, CommandFailure, CommandOutput};
use crate::args::{
CommandKind, CurrentArgs, NamespaceCommand, NamespaceCreateArgs, NamespaceDeleteArgs,
NamespaceForkArgs, NamespaceUseArgs, RuntimeBehavior,
};
use crate::config::save_config;
use crate::error::CliError;
use crate::profiles::{default_namespace, set_default_namespace};
use crate::prompt::prompt_line;
use crate::resolve::{
load_cli_config, parse_namespace_id, resolve_target_profile, resolve_target_profile_from_config,
};
use std::path::Path;
pub(crate) async fn run_namespace_command(
kind: CommandKind,
config_path: &Path,
command: NamespaceCommand,
runtime: RuntimeBehavior,
) -> Result<CommandOutput, CommandFailure> {
match command {
NamespaceCommand::Create(args) => run_namespace_create(kind, config_path, args).await,
NamespaceCommand::Delete(args) => {
run_namespace_delete(kind, config_path, args, runtime).await
}
NamespaceCommand::Fork(args) => run_namespace_fork(kind, config_path, args).await,
}
}
async fn run_namespace_create(
kind: CommandKind,
config_path: &Path,
args: NamespaceCreateArgs,
) -> Result<CommandOutput, CommandFailure> {
let explicit_profile = args.profile.profile.as_deref();
let resolved = resolve_target_profile(config_path, explicit_profile, args.profile.no_retry)
.await
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let mode = resolved.target.mode_str().to_owned();
let namespace_id = parse_namespace_id(&args.namespace_id)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
let namespace = resolved
.target
.create_namespace(&namespace_id)
.await
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
Ok(CommandOutput {
kind,
profile: Some(resolved.profile_name),
mode: Some(mode),
data: CommandData::NamespaceSummary(namespace),
})
}
async fn run_namespace_delete(
kind: CommandKind,
config_path: &Path,
args: NamespaceDeleteArgs,
runtime: RuntimeBehavior,
) -> Result<CommandOutput, CommandFailure> {
let explicit_profile = args.profile.profile.as_deref();
let resolved = resolve_target_profile(config_path, explicit_profile, args.profile.no_retry)
.await
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let mode = resolved.target.mode_str().to_owned();
let namespace_id = parse_namespace_id(&args.namespace_id)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
if !args.yes {
if !runtime.interactive {
return Err(fail(
kind,
Some(resolved.profile_name),
Some(mode),
CliError::non_interactive_input_required(
"deleting a namespace requires confirmation: pass --yes, or run \
interactively to confirm at the prompt",
),
));
}
let typed = prompt_line(&format!(
"deleting `{}` is permanent and retires the id; type the namespace id to confirm",
args.namespace_id
))
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
if typed.trim() != args.namespace_id {
return Err(fail(
kind,
Some(resolved.profile_name),
Some(mode),
CliError::invalid_input(format!(
"confirmation `{typed}` does not match namespace id `{}`",
args.namespace_id
)),
));
}
}
let response = resolved
.target
.delete_namespace(
&namespace_id,
args.expected_head_seq.map(loonfs_api::ChangeSeq),
)
.await
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
Ok(CommandOutput {
kind,
profile: Some(resolved.profile_name),
mode: Some(mode),
data: CommandData::NamespaceDeleted(response),
})
}
async fn run_namespace_fork(
kind: CommandKind,
config_path: &Path,
args: NamespaceForkArgs,
) -> Result<CommandOutput, CommandFailure> {
let explicit_profile = args.profile.profile.as_deref();
let resolved = resolve_target_profile(config_path, explicit_profile, args.profile.no_retry)
.await
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let mode = resolved.target.mode_str().to_owned();
let source_namespace_id = parse_namespace_id(&args.source)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
let new_namespace_id = parse_namespace_id(&args.new_namespace_id)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
let namespace = resolved
.target
.fork_namespace(&source_namespace_id, &new_namespace_id)
.await
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
Ok(CommandOutput {
kind,
profile: Some(resolved.profile_name),
mode: Some(mode),
data: CommandData::NamespaceSummary(namespace),
})
}
pub(crate) async fn run_namespace_use(
kind: CommandKind,
config_path: &Path,
args: NamespaceUseArgs,
) -> Result<CommandOutput, CommandFailure> {
let explicit_profile = args.profile.profile.as_deref();
let mut loaded = load_cli_config(config_path)
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let resolved =
resolve_target_profile_from_config(&loaded.config, explicit_profile, args.profile.no_retry)
.await
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let mode = resolved.target.mode_str().to_owned();
let namespace_id = parse_namespace_id(&args.namespace)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
resolved
.target
.namespace_status(&namespace_id)
.await
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
set_default_namespace(&mut loaded.config, &resolved.profile_name, &args.namespace)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
save_config(&loaded.path, &loaded.config)
.map_err(|error| fail_for(kind, &resolved.profile_name, &mode, error))?;
Ok(CommandOutput {
kind,
profile: Some(resolved.profile_name.clone()),
mode: Some(mode),
data: CommandData::DefaultNamespace {
profile: resolved.profile_name,
namespace: args.namespace,
},
})
}
pub(crate) async fn run_namespace_current(
kind: CommandKind,
config_path: &Path,
args: CurrentArgs,
) -> Result<CommandOutput, CommandFailure> {
let explicit_profile = args.profile.profile.as_deref();
let loaded = load_cli_config(config_path)
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let (profile_name, profile) =
crate::profiles::resolve_profile(&loaded.config, explicit_profile)
.map_err(|error| fail(kind, explicit_profile.map(ToOwned::to_owned), None, error))?;
let mode = profile.mode_str().to_owned();
Ok(CommandOutput {
kind,
profile: Some(profile_name.to_owned()),
mode: Some(mode),
data: CommandData::Current {
profile: profile_name.to_owned(),
namespace: default_namespace(profile).map(ToOwned::to_owned),
},
})
}