pub mod flight;
pub use flight::FlightCtx;
pub mod formation;
pub use formation::{FormationCfgCtx, FormationCtx};
pub mod metadata;
pub use metadata::MetadataCtx;
pub mod locks;
pub use locks::LocksCtx;
pub mod restrict;
use std::path::{Path, PathBuf};
use clap_complete::Shell;
use once_cell::unsync::OnceCell;
use reqwest::Url;
pub use restrict::RestrictCtx;
use crate::{
config::RawConfig,
error::{CliErrorKind, Context, Result},
fs::{self, FromDisk, ToDisk},
ops::{flight::Flights, formation::Formations},
printer::{ColorChoice, OutputFormat},
};
const FLIGHTS_FILE: &str = "flights.json";
const FORMATIONS_FILE: &str = "formations.json";
pub const DEFAULT_IMAGE_REGISTRY_URL: &str = "registry.cplane.cloud";
#[derive(Debug, Default, Clone)]
pub struct Args {
pub color: ColorChoice,
pub name_id: Option<String>,
pub overwrite: Vec<String>,
pub exact: bool,
pub all: bool,
pub third_party: bool,
pub shell: Option<Shell>,
pub out_format: OutputFormat,
pub force: bool,
pub stateless: bool,
pub api_key: Option<String>,
pub fetch: bool,
}
impl Args {
pub fn api_key(&self) -> Result<&str> {
self.api_key
.as_deref()
.ok_or_else(|| CliErrorKind::MissingApiKey.into_err())
}
}
#[derive(Debug)]
pub struct Ctx {
data_dir: PathBuf,
pub flight_ctx: LateInit<FlightCtx>,
pub formation_ctx: LateInit<FormationCtx>,
pub md_ctx: LateInit<MetadataCtx>,
pub locks_ctx: LateInit<LocksCtx>,
pub restrict_ctx: LateInit<RestrictCtx>,
pub conf_files: Vec<PathBuf>,
pub args: Args,
pub db: Db,
pub internal_run: bool,
pub did_init: bool,
pub disable_pb: bool,
pub registry: String,
pub compute_url: Option<Url>,
pub identity_url: Option<Url>,
pub metadata_url: Option<Url>,
pub locks_url: Option<Url>,
pub insecure_urls: bool,
pub invalid_certs: bool,
}
impl Clone for Ctx {
fn clone(&self) -> Self {
Self {
data_dir: self.data_dir.clone(),
flight_ctx: if self.flight_ctx.get().is_some() {
let li = LateInit::default();
li.init(self.flight_ctx.get().cloned().unwrap());
li
} else {
LateInit::default()
},
formation_ctx: if self.formation_ctx.get().is_some() {
let li = LateInit::default();
li.init(self.formation_ctx.get().cloned().unwrap());
li
} else {
LateInit::default()
},
md_ctx: if self.md_ctx.get().is_some() {
let li = LateInit::default();
li.init(self.md_ctx.get().cloned().unwrap());
li
} else {
LateInit::default()
},
locks_ctx: if self.locks_ctx.get().is_some() {
let li = LateInit::default();
li.init(self.locks_ctx.get().cloned().unwrap());
li
} else {
LateInit::default()
},
restrict_ctx: if self.restrict_ctx.get().is_some() {
let li = LateInit::default();
li.init(self.restrict_ctx.get().cloned().unwrap());
li
} else {
LateInit::default()
},
conf_files: self.conf_files.clone(),
args: self.args.clone(),
db: self.db.clone(),
internal_run: self.internal_run,
did_init: self.did_init,
disable_pb: self.disable_pb,
registry: self.registry.clone(),
compute_url: self.compute_url.clone(),
identity_url: self.identity_url.clone(),
metadata_url: self.metadata_url.clone(),
locks_url: self.locks_url.clone(),
insecure_urls: self.insecure_urls,
invalid_certs: self.invalid_certs,
}
}
}
impl Default for Ctx {
fn default() -> Self {
Self {
data_dir: fs::data_dir(),
flight_ctx: LateInit::default(),
formation_ctx: LateInit::default(),
md_ctx: LateInit::default(),
locks_ctx: LateInit::default(),
restrict_ctx: LateInit::default(),
conf_files: Vec::new(),
args: Args::default(),
db: Db::default(),
internal_run: false,
did_init: false,
disable_pb: false,
compute_url: None,
identity_url: None,
metadata_url: None,
locks_url: None,
insecure_urls: false,
invalid_certs: false,
registry: DEFAULT_IMAGE_REGISTRY_URL.into(),
}
}
}
impl From<RawConfig> for Ctx {
fn from(cfg: RawConfig) -> Self {
Self {
data_dir: fs::data_dir(),
conf_files: cfg.loaded_from.clone(),
args: Args {
color: cfg.seaplane.color.unwrap_or_default(),
api_key: cfg.account.api_key,
..Default::default()
},
registry: cfg
.seaplane
.default_registry_url
.unwrap_or_else(|| DEFAULT_IMAGE_REGISTRY_URL.into())
.trim_end_matches('/')
.to_string(),
compute_url: cfg.api.compute_url,
identity_url: cfg.api.identity_url,
metadata_url: cfg.api.metadata_url,
locks_url: cfg.api.locks_url,
did_init: cfg.did_init,
#[cfg(feature = "allow_insecure_urls")]
insecure_urls: cfg.danger_zone.allow_insecure_urls,
#[cfg(feature = "allow_invalid_certs")]
invalid_certs: cfg.danger_zone.allow_invalid_certs,
..Self::default()
}
}
}
impl Ctx {
pub fn update_from_env(&mut self) -> Result<()> {
Ok(())
}
#[inline]
pub fn data_dir(&self) -> &Path { &self.data_dir }
pub fn conf_files(&self) -> &[PathBuf] { &self.conf_files }
pub fn flights_file(&self) -> PathBuf { self.data_dir.join(FLIGHTS_FILE) }
pub fn formations_file(&self) -> PathBuf { self.data_dir.join(FORMATIONS_FILE) }
pub fn persist_formations(&self) -> Result<()> {
self.db
.formations
.persist_if(!self.args.stateless)
.with_context(|| format!("Path: {:?}\n", self.formations_file()))
}
pub fn persist_flights(&self) -> Result<()> {
self.db
.flights
.persist_if(!self.args.stateless)
.with_context(|| format!("Path: {:?}\n", self.flights_file()))
}
}
#[derive(Debug, Default, Clone)]
pub struct Db {
pub flights: Flights,
pub formations: Formations,
pub needs_persist: bool,
}
impl Db {
pub fn load<P: AsRef<Path>>(flights: P, formations: P) -> Result<Self> {
Self::load_if(flights, formations, true)
}
pub fn load_if<P: AsRef<Path>>(flights: P, formations: P, yes: bool) -> Result<Self> {
Ok(Self {
flights: FromDisk::load_if(flights, yes).unwrap_or_else(|| Ok(Flights::default()))?,
formations: FromDisk::load_if(formations, yes)
.unwrap_or_else(|| Ok(Formations::default()))?,
needs_persist: false,
})
}
}
#[derive(Debug)]
pub struct LateInit<T> {
inner: OnceCell<T>,
}
impl<T> Default for LateInit<T> {
fn default() -> Self { Self { inner: OnceCell::default() } }
}
impl<T> LateInit<T> {
pub fn init(&self, val: T) { assert!(self.inner.set(val).is_ok()) }
pub fn get(&self) -> Option<&T> { self.inner.get() }
pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut() }
}
impl<T: Default> LateInit<T> {
pub fn get_or_init(&self) -> &T { self.inner.get_or_init(|| T::default()) }
pub fn get_mut_or_init(&mut self) -> &mut T {
self.inner.get_or_init(|| T::default());
self.inner.get_mut().unwrap()
}
}