use clap::{Args, Subcommand, ValueEnum};
use homeboy::log_status;
use std::path::Path;
use super::CmdResult;
use homeboy::project::{self};
#[derive(Args)]
pub struct ProjectArgs {
#[command(subcommand)]
command: ProjectCommand,
}
#[derive(Subcommand)]
enum ProjectCommand {
List,
Show {
project_id: String,
},
Create {
#[arg(long)]
json: Option<String>,
#[arg(long)]
skip_existing: bool,
id: Option<String>,
domain: Option<String>,
#[arg(long)]
server_id: Option<String>,
#[arg(long)]
base_path: Option<String>,
#[arg(long)]
table_prefix: Option<String>,
},
#[command(visible_aliases = ["edit", "merge"])]
Set {
#[command(flatten)]
args: super::DynamicSetArgs,
},
Remove {
project_id: Option<String>,
spec: Option<String>,
#[arg(long, value_name = "JSON")]
json: Option<String>,
},
Rename {
project_id: String,
new_id: String,
},
Components {
#[command(subcommand)]
command: ProjectComponentsCommand,
},
Pin {
#[command(subcommand)]
command: ProjectPinCommand,
},
Delete {
project_id: String,
},
Status {
project_id: String,
#[arg(long)]
health_only: bool,
},
}
#[derive(Subcommand)]
enum ProjectComponentsCommand {
List {
project_id: String,
},
Set {
project_id: String,
#[arg(long)]
json: String,
},
AttachPath {
project_id: String,
local_path: String,
},
Remove {
project_id: String,
component_ids: Vec<String>,
},
Clear {
project_id: String,
},
}
#[derive(Subcommand)]
enum ProjectPinCommand {
List {
project_id: String,
#[arg(long, value_enum)]
r#type: ProjectPinType,
},
Add {
project_id: String,
path: String,
#[arg(long, value_enum)]
r#type: ProjectPinType,
#[arg(long)]
label: Option<String>,
#[arg(long, default_value = "100")]
tail: u32,
},
Remove {
project_id: String,
path: String,
#[arg(long, value_enum)]
r#type: ProjectPinType,
},
}
#[derive(Clone, Copy, ValueEnum)]
enum ProjectPinType {
File,
Log,
}
pub type ProjectOutput = homeboy::project::ProjectReportOutput;
pub fn run(args: ProjectArgs, _global: &crate::commands::GlobalArgs) -> CmdResult<ProjectOutput> {
match args.command {
ProjectCommand::List => list(),
ProjectCommand::Show { project_id } => show(&project_id),
ProjectCommand::Create {
json,
skip_existing,
id,
domain,
server_id,
base_path,
table_prefix,
} => {
let json_spec = if let Some(spec) = json {
spec
} else {
let id = id.ok_or_else(|| {
homeboy::Error::validation_invalid_argument(
"id",
"Missing required argument: id",
None,
None,
)
})?;
let new_project = project::Project {
id: id.clone(),
domain,
server_id,
base_path,
table_prefix,
..Default::default()
};
homeboy::config::serialize_with_id(&new_project, &id)?
};
Ok(project::build_create_output(project::create(
&json_spec,
skip_existing,
)?))
}
ProjectCommand::Set { args } => set(args),
ProjectCommand::Remove {
project_id,
spec,
json,
} => {
let json_spec = json.or(spec).ok_or_else(|| {
homeboy::Error::validation_invalid_argument(
"spec",
"Provide JSON spec or use --json flag",
None,
None,
)
})?;
remove(project_id.as_deref(), &json_spec)
}
ProjectCommand::Rename { project_id, new_id } => rename(&project_id, &new_id),
ProjectCommand::Components { command } => components(command),
ProjectCommand::Pin { command } => pin(command),
ProjectCommand::Delete { project_id } => delete(&project_id),
ProjectCommand::Status {
project_id,
health_only,
} => status(&project_id, health_only),
}
}
fn list() -> CmdResult<ProjectOutput> {
Ok((project::build_list_output(project::list_report()?), 0))
}
fn show(project_id: &str) -> CmdResult<ProjectOutput> {
Ok((
project::build_show_output(project::show_report(project_id)?),
0,
))
}
fn set(args: super::DynamicSetArgs) -> CmdResult<ProjectOutput> {
let merged = super::merge_dynamic_args(&args)?.ok_or_else(|| {
homeboy::Error::validation_invalid_argument(
"spec",
"Provide JSON spec, --json flag, --base64 flag, or --key value flags",
None,
None,
)
})?;
let (json_string, replace_fields) = super::finalize_set_spec(&merged, &args.replace)?;
project::build_set_output(project::merge(
args.id.as_deref(),
&json_string,
&replace_fields,
)?)
}
fn remove(project_id: Option<&str>, json: &str) -> CmdResult<ProjectOutput> {
Ok((
project::build_remove_output(project::remove_from_json(project_id, json)?)?,
0,
))
}
fn rename(project_id: &str, new_id: &str) -> CmdResult<ProjectOutput> {
Ok((
project::build_rename_output(project::rename(project_id, new_id)?),
0,
))
}
fn delete(project_id: &str) -> CmdResult<ProjectOutput> {
project::delete(project_id)?;
Ok((project::build_delete_output(project_id), 0))
}
fn components(command: ProjectComponentsCommand) -> CmdResult<ProjectOutput> {
match command {
ProjectComponentsCommand::List { project_id } => components_list(&project_id),
ProjectComponentsCommand::Set { project_id, json } => components_set(&project_id, &json),
ProjectComponentsCommand::AttachPath {
project_id,
local_path,
} => components_attach_path(&project_id, &local_path),
ProjectComponentsCommand::Remove {
project_id,
component_ids,
} => components_remove(&project_id, component_ids),
ProjectComponentsCommand::Clear { project_id } => components_clear(&project_id),
}
}
fn components_list(project_id: &str) -> CmdResult<ProjectOutput> {
Ok((
project::build_components_output(project_id, "list", project::list_components(project_id)?),
0,
))
}
fn components_set(project_id: &str, json: &str) -> CmdResult<ProjectOutput> {
let components = project::set_components(project_id, json)?;
Ok(write_project_components_response(
project_id, "set", components,
))
}
fn components_attach_path(project_id: &str, local_path: &str) -> CmdResult<ProjectOutput> {
let components = project::attach_component_path_report(project_id, Path::new(local_path))?;
Ok(write_project_components_response(
project_id,
"attach_path",
components,
))
}
fn components_remove(project_id: &str, component_ids: Vec<String>) -> CmdResult<ProjectOutput> {
let components = project::remove_components_report(project_id, component_ids)?;
Ok(write_project_components_response(
project_id, "remove", components,
))
}
fn components_clear(project_id: &str) -> CmdResult<ProjectOutput> {
let components = project::clear_components(project_id)?;
Ok(write_project_components_response(
project_id, "clear", components,
))
}
fn write_project_components_response(
project_id: &str,
action: &str,
components: homeboy::project::ProjectComponentsOutput,
) -> (ProjectOutput, i32) {
(
project::build_components_output(project_id, action, components),
0,
)
}
fn pin(command: ProjectPinCommand) -> CmdResult<ProjectOutput> {
match command {
ProjectPinCommand::List { project_id, r#type } => pin_list(&project_id, r#type),
ProjectPinCommand::Add {
project_id,
path,
r#type,
label,
tail,
} => pin_add(&project_id, &path, r#type, label, tail),
ProjectPinCommand::Remove {
project_id,
path,
r#type,
} => pin_remove(&project_id, &path, r#type),
}
}
fn pin_list(project_id: &str, pin_type: ProjectPinType) -> CmdResult<ProjectOutput> {
Ok((
project::build_pin_output(
"project.pin.list",
project_id,
project::list_pins(project_id, map_pin_type(pin_type))?,
),
0,
))
}
fn pin_add(
project_id: &str,
path: &str,
pin_type: ProjectPinType,
label: Option<String>,
tail: u32,
) -> CmdResult<ProjectOutput> {
let pin = project::add_pin(
project_id,
map_pin_type(pin_type),
path,
project::PinOptions {
label,
tail_lines: tail,
},
)?;
Ok((
project::build_pin_output("project.pin.add", project_id, pin),
0,
))
}
fn pin_remove(project_id: &str, path: &str, pin_type: ProjectPinType) -> CmdResult<ProjectOutput> {
let pin = project::remove_pin(project_id, map_pin_type(pin_type), path)?;
Ok((
project::build_pin_output("project.pin.remove", project_id, pin),
0,
))
}
fn map_pin_type(pin_type: ProjectPinType) -> project::PinType {
match pin_type {
ProjectPinType::File => project::PinType::File,
ProjectPinType::Log => project::PinType::Log,
}
}
fn status(project_id: &str, health_only: bool) -> CmdResult<ProjectOutput> {
log_status!("project", "Checking '{}'...", project_id);
Ok((
project::build_status_output(project_id, project::status_report(project_id, health_only)?),
0,
))
}