use std::collections::HashSet;
use seaplane::api::{
compute::v1::FormationConfiguration as FormationConfigurationModel,
shared::v1::{Provider as ProviderModel, Region as RegionModel},
};
use crate::{
cli::{cmds::formation::SeaplaneFormationPlanArgMatches, Provider, Region},
context::Ctx,
error::{CliError, CliErrorKind, Context, Result},
ops::{flight::Flights, formation::Endpoint, generate_formation_name},
printer::Color,
};
fn no_matching_flight(flight: &str) -> CliError {
CliErrorKind::NoMatchingItem(flight.to_string())
.into_err()
.context("(hint: create the Flight Plan with '")
.with_color_context(|| (Color::Green, format!("seaplane flight plan {flight}")))
.context("')\n")
.context("(hint: or try fetching remote definitions with '")
.color_context(Color::Green, "seaplane formation fetch-remote")
.context("')\n")
}
#[derive(Debug, Clone)]
pub struct FormationCtx {
pub name_id: String,
pub launch: bool,
pub remote: bool,
pub local: bool,
pub grounded: bool,
pub recursive: bool,
pub cfg_ctx: FormationCfgCtx,
}
impl Default for FormationCtx {
fn default() -> Self {
Self {
name_id: generate_formation_name(),
launch: false,
cfg_ctx: FormationCfgCtx::default(),
remote: false,
local: true,
grounded: false,
recursive: false,
}
}
}
impl FormationCtx {
pub fn update_from_formation_plan(
&mut self,
matches: &SeaplaneFormationPlanArgMatches,
flights_db: &Flights,
) -> Result<()> {
let matches = matches.0;
let mut flight_names = Vec::new();
for flight in matches
.get_many::<String>("include-flight-plan")
.unwrap_or_default()
.filter(|f| !(f.starts_with('@') || f.contains('=')))
{
if let Some(flight) = flights_db.find_name_or_partial_id(flight) {
flight_names.push(flight.model.name().to_owned());
} else {
#[cfg(not(any(feature = "ui_tests", feature = "semantic_ui_tests",)))]
{
return Err(no_matching_flight(flight));
}
}
}
self.name_id = matches
.get_one::<String>("name_id")
.map(ToOwned::to_owned)
.unwrap_or_else(generate_formation_name);
self.grounded = matches.get_flag("grounded");
self.launch = matches.get_flag("launch");
self.cfg_ctx
.flights
.extend(flight_names.iter().map(|s| s.to_string()));
self.cfg_ctx.affinities.extend(
matches
.get_many::<String>("affinity")
.unwrap_or_default()
.map(ToOwned::to_owned),
);
self.cfg_ctx.connections.extend(
matches
.get_many::<String>("connection")
.unwrap_or_default()
.map(ToOwned::to_owned),
);
self.cfg_ctx.providers_allowed = matches
.get_many::<Provider>("provider")
.unwrap_or_default()
.filter_map(Provider::into_model)
.collect();
self.cfg_ctx.providers_denied = matches
.get_many::<Provider>("exclude-provider")
.unwrap_or_default()
.filter_map(Provider::into_model)
.collect();
self.cfg_ctx.regions_allowed = matches
.get_many::<Region>("region")
.unwrap_or_default()
.filter_map(Region::into_model)
.collect();
self.cfg_ctx.regions_denied = matches
.get_many::<Region>("exclude-region")
.unwrap_or_default()
.filter_map(Region::into_model)
.collect();
self.cfg_ctx.public_endpoints = matches
.get_many::<Endpoint>("public-endpoint")
.unwrap_or_default()
.cloned()
.collect();
self.cfg_ctx.formation_endpoints = matches
.get_many::<Endpoint>("formation-endpoint")
.unwrap_or_default()
.cloned()
.collect();
self.cfg_ctx.flight_endpoints = matches
.get_many::<Endpoint>("flight-endpoint")
.unwrap_or_default()
.cloned()
.collect();
Ok(())
}
pub fn configuration_model(&self, ctx: &Ctx) -> Result<FormationConfigurationModel> {
let mut f_model = FormationConfigurationModel::builder();
for flight_name in &self.cfg_ctx.flights {
let flight = ctx
.db
.flights
.find_name(flight_name)
.ok_or_else(|| no_matching_flight(flight_name))?;
f_model = f_model.add_flight(flight.model.clone());
}
for &item in &self.cfg_ctx.providers_allowed {
f_model = f_model.add_allowed_provider(item);
}
for &item in &self.cfg_ctx.providers_denied {
f_model = f_model.add_denied_provider(item);
}
for &item in &self.cfg_ctx.regions_allowed {
f_model = f_model.add_allowed_region(item);
}
for &item in &self.cfg_ctx.regions_denied {
f_model = f_model.add_denied_region(item);
}
for item in &self.cfg_ctx.public_endpoints {
f_model = f_model.add_public_endpoint(item.key(), item.value());
}
for item in &self.cfg_ctx.flight_endpoints {
f_model = f_model.add_flight_endpoint(item.key(), item.value());
}
#[cfg(feature = "unstable")]
{
for item in &self.cfg_ctx.affinities {
f_model = f_model.add_affinity(item);
}
for item in &self.cfg_ctx.connections {
f_model = f_model.add_connection(item);
}
for item in &self.cfg_ctx.formation_endpoints {
f_model = f_model.add_formation_endpoint(item.key(), item.value());
}
}
f_model.build().map_err(Into::into)
}
}
#[derive(Default, Debug, Clone)]
pub struct FormationCfgCtx {
pub flights: Vec<String>,
pub affinities: Vec<String>,
pub connections: Vec<String>,
pub providers_allowed: HashSet<ProviderModel>,
pub providers_denied: HashSet<ProviderModel>,
pub regions_allowed: HashSet<RegionModel>,
pub regions_denied: HashSet<RegionModel>,
pub public_endpoints: Vec<Endpoint>,
pub formation_endpoints: Vec<Endpoint>,
pub flight_endpoints: Vec<Endpoint>,
}