seaplane_cli/cli/cmds/flight/
delete.rs

1use clap::{ArgMatches, Command};
2
3use crate::{
4    cli::{
5        errors,
6        validator::{validate_flight_name, validate_name_id},
7        CliCommand,
8    },
9    context::Ctx,
10    error::{CliErrorKind, Result},
11};
12
13#[derive(Copy, Clone, Debug)]
14pub struct SeaplaneFlightDelete;
15
16impl SeaplaneFlightDelete {
17    pub fn command() -> Command {
18        let validator = |s: &str| validate_name_id(validate_flight_name, s);
19        // TODO: add a --local[-only] flag or similar that combines with --force to only remove
20        // local
21        Command::new("delete")
22            .visible_aliases(["del", "remove", "rm"])
23            .about("Delete a local Flight Plan")
24            .arg(arg!(flight required =["NAME|ID"])
25                .value_parser(validator)
26                .help("The name or ID of the Flight Plan to remove, must be unambiguous"))
27            .arg(arg!(--force -('f'))
28                .help("Delete this Flight Plan even if referenced by a local Formation Plan, or deletes ALL Flight Plan referenced by the name or ID even if ambiguous"))
29            .arg(arg!(--all -('a'))
30                .help("Delete all matching Flight Plans even when the name or ID is ambiguous"))
31    }
32}
33
34impl CliCommand for SeaplaneFlightDelete {
35    fn run(&self, ctx: &mut Ctx) -> Result<()> {
36        if ctx.args.stateless {
37            cli_eprint!(@Red, "error: ");
38            cli_eprint!("'");
39            cli_eprint!(@Yellow, "--stateless");
40            cli_eprint!("' cannot be used with '");
41            cli_eprint!(@Yellow, "seaplane flight delete");
42            cli_eprintln!("'");
43            cli_eprintln!("(hint: 'seaplane flight delete' only modifies local plans)");
44            cli_eprint!("(hint: you may want 'seaplane ");
45            cli_eprint!(@Green, "formation ");
46            cli_eprintln!("delete' instead)");
47            std::process::exit(1);
48        }
49
50        // Get the indices of any flights that match the given name/ID
51        let indices = if ctx.args.all {
52            ctx.db
53                .flights
54                .indices_of_left_matches(ctx.args.name_id.as_ref().unwrap())
55        } else {
56            ctx.db
57                .flights
58                .indices_of_matches(ctx.args.name_id.as_ref().unwrap())
59        };
60
61        match indices.len() {
62            0 => errors::no_matching_item(ctx.args.name_id.clone().unwrap(), false, ctx.args.all)?,
63            1 => (),
64            _ => {
65                if !(ctx.args.all || ctx.args.force) {
66                    errors::ambiguous_item(ctx.args.name_id.clone().unwrap(), true)?;
67                }
68            }
69        }
70
71        // Check if any of the requested flights are referenced in Formations
72        let flights_in_use: Vec<String> = ctx
73            .db
74            .flights
75            .iter()
76            .enumerate()
77            .filter(|(i, _)| indices.contains(i))
78            .filter_map(|(_, flight)| {
79                if ctx.db.formations.has_flight(flight.model.name()) {
80                    Some(flight.model.name().to_owned())
81                } else {
82                    None
83                }
84            })
85            .collect();
86        // If so, and `--force` wasn't used issue an error
87        if !flights_in_use.is_empty() {
88            if !ctx.args.force {
89                return Err(CliErrorKind::FlightsInUse(flights_in_use).into_err());
90            }
91            // Remove the flights from any Formations
92            for flight in flights_in_use {
93                ctx.db.formations.remove_flight(&flight);
94                // TODO: we should also go through and delete all endpoints that reference this
95                // flight...but we don't have endpoints that are that smart yet
96            }
97            ctx.persist_formations()?;
98        }
99
100        // Remove the flights
101        ctx.db
102            .flights
103            .remove_indices(&indices)
104            .iter()
105            .for_each(|flight| {
106                cli_println!("Deleted local Flight Plan {}", &flight.id.to_string());
107            });
108
109        ctx.persist_flights()?;
110
111        if !ctx.internal_run {
112            cli_println!(
113                "\nSuccessfully removed {} item{}",
114                indices.len(),
115                if indices.len() > 1 { "s" } else { "" }
116            );
117        }
118        Ok(())
119    }
120
121    fn update_ctx(&self, matches: &ArgMatches, ctx: &mut Ctx) -> Result<()> {
122        ctx.args.force = matches.get_flag("force");
123        ctx.args.all = matches.get_flag("all");
124        ctx.args.name_id = matches.get_one::<String>("flight").map(ToOwned::to_owned);
125        Ok(())
126    }
127}