use clap::{ArgMatches, Command};
use const_format::concatcp;
#[cfg(not(any(feature = "ui_tests", feature = "semantic_ui_tests")))]
use crate::cli::cmds::flight::SeaplaneFlightPlan;
use crate::{
cli::{
cmds::formation::{common, SeaplaneFormationFetch, SeaplaneFormationLaunch},
specs::{FLIGHT_SPEC, REGION_SPEC},
CliCommand,
},
context::{Ctx, FlightCtx},
error::{CliErrorKind, Context, Result},
ops::formation::{Formation, FormationConfiguration},
printer::Color,
};
static LONG_ABOUT: &str =
"Make a new local Formation Plan (and optionally launch an instance of it)
Include local Flight Plans by using `--include-flight-plan`. Multiple Flights may be included in a
Formation Plan using a SEMICOLON separated list, or using the argument multiple times.
You can also create a new Flight Plan using the INLINE-SPEC option of `--include-flight-plan`.
Flight Plans created using INLINE-SPEC are automatically included in the Formation Plan.";
#[allow(missing_debug_implementations)]
pub struct SeaplaneFormationPlanArgMatches<'a>(pub &'a ArgMatches);
#[derive(Copy, Clone, Debug)]
pub struct SeaplaneFormationPlan;
impl SeaplaneFormationPlan {
pub fn command() -> Command {
Command::new("plan")
.after_help(concatcp!(FLIGHT_SPEC, "\n\n", REGION_SPEC))
.visible_aliases(["create", "add"])
.about("Create a Seaplane Formation")
.long_about(LONG_ABOUT)
.args(common::args())
.arg(arg!(--force).help("Override any existing Formation with the same NAME"))
.arg(arg!(--fetch|sync|synchronize - ('F')).help("Fetch remote instances prior to creating this plan to check for conflicts (by default only local references are considered)"))
}
}
impl CliCommand for SeaplaneFormationPlan {
fn run(&self, ctx: &mut Ctx) -> Result<()> {
if ctx.args.fetch {
let old_name = ctx.args.name_id.take();
ctx.internal_run = true;
SeaplaneFormationFetch.run(ctx)?;
ctx.internal_run = false;
ctx.args.name_id = old_name;
}
let formation_ctx = ctx.formation_ctx.get_or_init();
let name = &formation_ctx.name_id;
if ctx.db.formations.contains_name(name) {
if !ctx.args.force {
let mut err = CliErrorKind::DuplicateName(name.to_owned())
.into_err()
.context("(hint: try '")
.color_context(Color::Green, format!("seaplane formation edit {}", &name))
.context("' instead)\n");
if ctx.db.needs_persist {
err = err.context("\nRolling back created Flight Plans!\n");
}
return Err(err);
}
ctx.db.formations.remove_name(name);
}
if ctx.db.needs_persist {
ctx.persist_flights()?;
ctx.db.needs_persist = false;
}
let mut new_formation = Formation::new(&formation_ctx.name_id);
let cfg = formation_ctx.configuration_model(ctx)?;
let formation_cfg = FormationConfiguration::new(cfg);
new_formation.local.insert(formation_cfg.id);
ctx.db.formations.configurations.push(formation_cfg);
let id = new_formation.id.to_string();
ctx.db.formations.formations.push(new_formation);
ctx.persist_formations()?;
cli_print!("Successfully created local Formation Plan '");
cli_print!(@Green, "{}", &formation_ctx.name_id);
cli_print!("' with ID '");
cli_print!(@Green, "{}", &id[..8]);
cli_println!("'");
if formation_ctx.launch || formation_ctx.grounded {
ctx.args.name_id = Some(formation_ctx.name_id.clone());
ctx.args.exact = true;
ctx.args.fetch = false;
SeaplaneFormationLaunch.run(ctx)?;
}
Ok(())
}
fn update_ctx(&self, matches: &ArgMatches, ctx: &mut Ctx) -> Result<()> {
ctx.args.fetch = matches.get_flag("fetch");
ctx.args.force = matches.get_flag("force");
let mut flights: Vec<_> = matches
.get_many::<String>("include-flight-plan")
.unwrap_or_default()
.collect();
let inline_flights = vec_remove_if!(flights, |f: &str| f.contains('='));
for flight in inline_flights {
let mut cloned_ctx = ctx.clone();
cloned_ctx.args.stateless = true;
cloned_ctx.internal_run = true;
cloned_ctx
.flight_ctx
.init(FlightCtx::from_inline_flight(flight, &ctx.registry)?);
#[cfg(not(any(feature = "ui_tests", feature = "semantic_ui_tests")))]
{
let flight_plan: Box<dyn CliCommand> = Box::new(SeaplaneFlightPlan);
flight_plan.run(&mut cloned_ctx)?;
}
let name = cloned_ctx.flight_ctx.get_or_init().name_id.clone();
#[cfg(not(any(feature = "ui_tests", feature = "semantic_ui_tests")))]
ctx.db
.flights
.add_flight(cloned_ctx.db.flights.remove_flight(&name, true).unwrap());
ctx.formation_ctx
.get_mut_or_init()
.cfg_ctx
.flights
.push(name);
ctx.db.needs_persist = true;
}
#[cfg(not(any(feature = "ui_tests", feature = "semantic_ui_tests")))]
for name in ctx
.db
.flights
.add_from_at_strs(vec_remove_if!(flights, |f: &str| f.starts_with('@')))?
{
ctx.formation_ctx
.get_mut_or_init()
.cfg_ctx
.flights
.push(name);
ctx.db.needs_persist = true;
}
ctx.formation_ctx
.get_mut_or_init()
.update_from_formation_plan(
&SeaplaneFormationPlanArgMatches(matches),
&ctx.db.flights,
)?;
Ok(())
}
}