seaplane_cli/cli/cmds/formation/
land.rs

1use clap::{ArgMatches, Command};
2
3use crate::{
4    api::FormationsReq,
5    cli::{
6        cmds::formation::SeaplaneFormationFetch,
7        errors,
8        validator::{validate_formation_name, validate_name_id},
9        CliCommand,
10    },
11    error::Result,
12    Ctx,
13};
14
15#[derive(Copy, Clone, Debug)]
16pub struct SeaplaneFormationLand;
17
18impl SeaplaneFormationLand {
19    pub fn command() -> Command {
20        let validator = |s: &str| validate_name_id(validate_formation_name, s);
21        Command::new("land")
22            .visible_alias("stop")
23            .about("Land (Stop) all configurations of a remote Formation Instance")
24            .arg(
25                arg!(name_id =["NAME|ID"] required)
26                    .help("The name or ID of the Formation Instance to land")
27                    .value_parser(validator),
28            )
29            .arg(
30                arg!(--all - ('a'))
31                    .help("Stop all matching Formations even when FORMATION is ambiguous"),
32            )
33            .arg(arg!(--fetch|sync|synchronize - ('F')).help(
34                "Fetch remote Formation Instances and synchronize local Plan definitions prior to attempting to land",
35            ))
36    }
37}
38
39impl CliCommand for SeaplaneFormationLand {
40    fn run(&self, ctx: &mut Ctx) -> Result<()> {
41        if ctx.args.fetch {
42            let old_name = ctx.args.name_id.take();
43            ctx.internal_run = true;
44            SeaplaneFormationFetch.run(ctx)?;
45            ctx.internal_run = false;
46            ctx.args.name_id = old_name;
47        }
48        let name = ctx.args.name_id.as_ref().unwrap();
49        // Get the indices of any formations that match the given name/ID
50        let indices = if ctx.args.all {
51            ctx.db.formations.formation_indices_of_left_matches(name)
52        } else {
53            ctx.db.formations.formation_indices_of_matches(name)
54        };
55
56        match indices.len() {
57            0 => errors::no_matching_item(name.to_string(), false, ctx.args.all)?,
58            1 => (),
59            _ => {
60                if !ctx.args.all {
61                    errors::ambiguous_item(name.to_string(), true)?;
62                }
63            }
64        }
65
66        let mut req = FormationsReq::new_delay_token(ctx)?;
67        for idx in indices {
68            // re unwrap: the indices returned came from Formations so they have to be valid
69            let formation = ctx.db.formations.get_formation_mut(idx).unwrap();
70            req.set_name(formation.name.as_ref().unwrap())?;
71
72            // re unwrap: We got the formation from the local DB so it has to have a name
73            req.stop()?;
74
75            // Move all configurations from in air to grounded
76            let ids: Vec<_> = formation.in_air.drain().collect();
77            for id in ids {
78                formation.grounded.insert(id);
79            }
80
81            ctx.persist_formations()?;
82
83            cli_print!("Successfully Landed remote Formation Instance '");
84            cli_print!(@Green, "{}", &name);
85            cli_println!("'");
86        }
87
88        Ok(())
89    }
90
91    fn update_ctx(&self, matches: &ArgMatches, ctx: &mut Ctx) -> Result<()> {
92        ctx.args.all = matches.get_flag("all");
93        ctx.args.name_id = matches.get_one::<String>("name_id").map(ToOwned::to_owned);
94        ctx.args.fetch = matches.get_flag("fetch");
95        Ok(())
96    }
97}