seaplane_cli/cli/cmds/flight/
copy.rs

1use clap::{ArgMatches, Command};
2
3use super::SeaplaneFlightCommonArgMatches;
4use crate::{
5    cli::{
6        cmds::flight::common,
7        errors::wrap_cli_context,
8        specs::IMAGE_SPEC,
9        validator::{validate_flight_name, validate_name_id},
10        CliCommand,
11    },
12    context::{Ctx, FlightCtx},
13    error::Result,
14};
15
16#[derive(Copy, Clone, Debug)]
17pub struct SeaplaneFlightCopy;
18
19impl SeaplaneFlightCopy {
20    pub fn command() -> Command {
21        let validator = |s: &str| validate_name_id(validate_flight_name, s);
22
23        // TODO: add --from
24        Command::new("copy")
25            .visible_alias("clone")
26            .about("Copy a local Flight Plan (optionally make changes to the copy)")
27            .after_help(IMAGE_SPEC)
28            .override_usage(
29                "
30    seaplane flight copy [OPTIONS] <NAME|ID> --name=<DEST_NAME>
31    seaplane flight copy [OPTION] <NAME|ID> [OPTIONS]",
32            )
33            .arg(
34                arg!(name_id =["NAME|ID"] required)
35                    .value_parser(validator)
36                    .help("The source name or ID of the Flight Plan to copy"),
37            )
38            .arg(arg!(--exact - ('x')).help("The given SOURCE must be an exact match"))
39            .args(common::args(false))
40    }
41}
42
43impl CliCommand for SeaplaneFlightCopy {
44    fn run(&self, ctx: &mut Ctx) -> Result<()> {
45        if ctx.args.stateless {
46            cli_eprint!(@Red, "error: ");
47            cli_eprint!("'");
48            cli_eprint!(@Yellow, "--stateless");
49            cli_eprint!("' cannot be used with the '");
50            cli_eprint!(@Yellow, "seaplane flight copy");
51            cli_eprintln!("' command");
52            cli_eprintln!("(hint: 'seaplane flight ...' only modifies local plans)");
53            std::process::exit(1);
54        }
55        // name_id cannot be None in `flight copy`
56        let mut dest_flight = match ctx
57            .db
58            .flights
59            .clone_flight(ctx.args.name_id.as_ref().unwrap(), ctx.args.exact)
60        {
61            Ok(f) => f,
62            Err(e) => return wrap_cli_context(e, ctx.args.exact, false),
63        };
64
65        // Now we just edit the newly copied Flight to match the given CLI params...
66        dest_flight.update_from(ctx.flight_ctx.get_or_init(), false)?;
67
68        let id = dest_flight.id.to_string();
69        let name = dest_flight.model.name().to_owned();
70
71        // Add the new Flight
72        ctx.db.flights.add_flight(dest_flight);
73
74        ctx.persist_flights()?;
75
76        cli_print!("Successfully copied Flight Plan '");
77        cli_print!(@Yellow, "{}", ctx.args.name_id.as_ref().unwrap());
78        cli_print!("' to new Flight Plan '");
79        cli_print!(@Green, "{name}");
80        cli_print!("' with ID '");
81        cli_print!(@Green, "{}", &id[..8]);
82        cli_println!("'");
83
84        Ok(())
85    }
86
87    fn update_ctx(&self, matches: &ArgMatches, ctx: &mut Ctx) -> Result<()> {
88        // clap will not let "source" be None
89        ctx.args.name_id = matches.get_one::<String>("name_id").map(ToOwned::to_owned);
90        ctx.flight_ctx
91            .init(FlightCtx::from_flight_common(&SeaplaneFlightCommonArgMatches(matches), ctx)?);
92        Ok(())
93    }
94}