seaplane_cli/cli/cmds/flight/
plan.rs

1use 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        // TODO: add --from
27        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        // Check for duplicates and suggest `seaplane flight edit`
62        let name = new_flight.name();
63        let indices = ctx.db.flights.indices_of_matches(name);
64        if !indices.is_empty() {
65            // TODO: We should check if these ones we remove are referenced remote or not
66
67            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            // We have duplicates, but the user passed --force. So first we remove the existing
79            // Flights and "re-add" them
80
81            // TODO: if more than one flight has the exact same name, we remove them all; that's
82            // *probably* what we want? But more thought should go into this...
83            ctx.db.flights.remove_indices(&indices);
84        }
85
86        let new_flight_name = new_flight.name().to_owned();
87        // Add the new Flight
88        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}