seaplane_cli/cli/cmds/flight/
plan.rs1use clap::{ArgMatches, Command};
2
3use crate::{
4 cli::{
5 cmds::{
6 flight::{
7 common::{self, SeaplaneFlightCommonArgMatches},
8 IMAGE_SPEC,
9 },
10 formation::SeaplaneFormationFetch,
11 },
12 CliCommand,
13 },
14 context::FlightCtx,
15 error::{CliErrorKind, Context, Result},
16 ops::flight::Flight,
17 printer::Color,
18 Ctx,
19};
20
21#[derive(Copy, Clone, Debug)]
22pub struct SeaplaneFlightPlan;
23
24impl SeaplaneFlightPlan {
25 pub fn command() -> Command {
26 Command::new("plan")
28 .visible_aliases(["create", "add"])
29 .after_help(IMAGE_SPEC)
30 .about("Make a new local Flight Plan that Formations can include and reference")
31 .arg(arg!(--force - ('f')).help("Override any existing Flights Plans with the same NAME"))
32 .arg(arg!(--fetch|sync|synchronize - ('F')).help("Fetch and synchronize remote Formation Instances (which reference Flight Plans) prior to creating this plan to check for conflicts (by default only local plans are checked)"))
33 .args(common::args(true))
34 }
35}
36
37impl CliCommand for SeaplaneFlightPlan {
38 fn run(&self, ctx: &mut Ctx) -> Result<()> {
39 if ctx.args.stateless && !ctx.internal_run {
40 cli_eprint!(@Red, "error: ");
41 cli_eprint!("'");
42 cli_eprint!(@Yellow, "--stateless");
43 cli_eprint!("' cannot be used with '");
44 cli_eprint!(@Yellow, "seaplane flight plan");
45 cli_eprintln!("'");
46 cli_eprintln!("(hint: 'seaplane flight plan' only modifies local plans)");
47 cli_eprint!("(hint: you may want 'seaplane ");
48 cli_eprint!(@Green, "formation ");
49 cli_eprintln!("plan' instead)");
50 std::process::exit(1);
51 }
52
53 if ctx.args.fetch {
54 let old_name = ctx.args.name_id.take();
55 SeaplaneFormationFetch.run(ctx)?;
56 ctx.args.name_id = old_name;
57 }
58
59 let new_flight = ctx.flight_ctx.get_or_init().model();
60
61 let name = new_flight.name();
63 let indices = ctx.db.flights.indices_of_matches(name);
64 if !indices.is_empty() {
65 if !ctx.args.force {
68 return Err(CliErrorKind::DuplicateName(name.into())
69 .into_err()
70 .context("(hint: try '")
71 .color_context(Color::Green, format!("seaplane flight edit {name}"))
72 .context("' to edit an existing local Flight Plan)\n")
73 .context("(hint: you can also use '")
74 .color_context(Color::Green, "--force")
75 .context("' to overwrite existing items)\n"));
76 }
77
78 ctx.db.flights.remove_indices(&indices);
84 }
85
86 let new_flight_name = new_flight.name().to_owned();
87 let new_flight = Flight::new(new_flight);
89 let id = new_flight.id;
90 ctx.db.flights.add_flight(new_flight);
91
92 ctx.persist_flights()?;
93
94 cli_print!("Successfully created Flight Plan '");
95 cli_print!(@Green, "{new_flight_name}");
96 cli_print!("' with ID '");
97 cli_print!(@Green, "{}", &id.to_string()[..8]);
98 cli_println!("'");
99
100 Ok(())
101 }
102
103 fn update_ctx(&self, matches: &ArgMatches, ctx: &mut Ctx) -> Result<()> {
104 ctx.flight_ctx
105 .init(FlightCtx::from_flight_common(&SeaplaneFlightCommonArgMatches(matches), ctx)?);
106 ctx.args.force = matches.get_flag("force");
107 ctx.args.fetch = matches.get_flag("fetch");
108 Ok(())
109 }
110}