use std::path::PathBuf;
use std::sync::Arc;
use anyhow::Context as _;
use reedline_repl_rs::clap::{ArgMatches, Command};
use reedline_repl_rs::Repl;
use s_macro::s;
use thiserror::Error;
use wildland_cargo_lib::api::cargo_user::CargoUser;
use wildland_cargo_lib::api::CargoConfig;
use wildland_corex::dfs::interface::DfsFrontend;
use crate::dropins::lss::sledlss::SledLss;
use crate::dropins::sfo::sfo_for_local_fs::SfoForLocalFS;
use crate::plugins::cargo::cargo_lib::CargoLib;
#[tracing::instrument(level = "trace")]
fn default_path() -> std::path::PathBuf {
std::path::PathBuf::from("/")
}
#[derive(derivative::Derivative)]
#[derivative(Default)] pub struct Context {
#[derivative(Default(value = "None"))]
pub lss: Option<SledLss>,
#[derivative(Default(value = "None"))]
pub sfo: Option<SfoForLocalFS>,
#[derivative(Default(value = "None"))]
pub cargo_cfg: Option<CargoConfig>,
#[derivative(Default(value = "None"))]
pub cargo: Option<CargoLib>,
#[derivative(Default(value = "None"))]
pub cargo_user: Option<CargoUser>,
#[derivative(Default(value = "default_path()"))]
pub current_path: PathBuf,
}
#[derive(Error, Debug, Clone)]
pub enum ContextError {
#[error("Cargo error: {0}")]
Cargo(String),
#[error("LSS error: {0}")]
Lss(String),
#[error("SFO error: {0}")]
Sfo(String),
#[error("Config error: {0}")]
Config(String),
#[error("CargoUser error: {0}")]
CargoUser(String),
#[error("Recoverable - {0}")]
Recoverable(Box<ContextError>),
}
type ContextResult<T> = Result<T, ContextError>;
impl Context {
#[tracing::instrument(level = "trace", skip_all)]
pub fn show_state(&self) {
match self.sfo {
Some(_) => println!("[o] SFO: loaded"),
None => println!("[x] SFO: not loaded"),
}
match self.lss {
Some(_) => println!("[o] LSS: loaded"),
None => println!("[x] LSS: not loaded"),
}
match self.cargo_cfg {
Some(_) => println!("[o] CFG: loaded"),
None => println!("[x] CFG: not loaded"),
}
match self.cargo {
Some(_) => println!("[o] CARGO: loaded"),
None => println!("[x] CARGO: not loaded"),
}
match self.cargo_user {
Some(_) => println!("[o] USER: loaded"),
None => println!("[x] USER: not loaded"),
}
}
pub fn get_dfs(&self) -> ContextResult<Arc<dyn DfsFrontend>> {
if self.cargo.is_none() {
Err(ContextError::Cargo(s!(
"DFS can not be enabled, no Cargo found"
)))
} else {
Ok(self.cargo.as_ref().unwrap().dfs_api().clone())
}
}
#[tracing::instrument(level = "trace", err(Debug), skip(self, lss))]
pub fn set_lss(&mut self, lss: SledLss) -> ContextResult<()> {
if self.cargo.is_some() {
return Err(ContextError::Cargo(s!(
"Cant set Cargo, its potentially used by Cargo Lib"
)));
}
self.lss = Some(lss);
Ok(())
}
#[tracing::instrument(level = "trace", err(Debug), skip(self))]
pub fn set_lss_from_path(&mut self, path: &String) -> ContextResult<()> {
if self.cargo.is_some() {
return Err(ContextError::Lss(s!(
"Cant set Cargo, its potentially used by Cargo Lib"
)));
}
let lss_instance = SledLss::new(s!(path));
self.lss = Some(lss_instance);
Ok(())
}
#[tracing::instrument(level = "trace", err(Debug), skip(self))]
pub fn init_sfo(&mut self, sfo_path: &str) -> ContextResult<()> {
if self.cargo.is_some() {
return Err(ContextError::Sfo(s!(
"Cant set Cargo, its potentially used by Cargo Lib"
)));
}
let sfo_instance = SfoForLocalFS::new(sfo_path);
self.sfo = Some(sfo_instance);
Ok(())
}
#[tracing::instrument(level = "trace", err(Debug), skip(self))]
pub fn set_cargo_config(&mut self, cargo_cfg: CargoConfig) -> ContextResult<()> {
if self.cargo.is_some() {
return Err(ContextError::Config(s!(
"Can't set Cargo, it's potentially used by Cargo Lib"
)));
}
if self.cargo_cfg.is_some() {
println!("replacing existing cargo config");
}
self.cargo_cfg = Some(cargo_cfg);
Ok(())
}
#[tracing::instrument(level = "trace", err(Debug), skip(self))]
pub fn set_cargo_config_from_path(&mut self, cargo_cfg: &str) -> ContextResult<()> {
let cargocfg = crate::dropins::cargo_cfg::handler::load_json_config_from_file(cargo_cfg)
.map_err(|e| ContextError::Config(e.to_string()))?;
self.set_cargo_config(cargocfg)
}
#[tracing::instrument(level = "trace", err(Debug), skip(self, cargo))]
pub fn set_cargo(&mut self, cargo: CargoLib) -> ContextResult<()> {
if self.cargo.is_some() {
return Err(ContextError::Cargo(s!(
"Cargo is present, can't replace it implicitly, wipe it first"
)));
}
self.cargo = Some(cargo);
Ok(())
}
#[tracing::instrument(level = "trace", err(Debug), skip(self))]
pub fn set_cargo_user(&mut self) -> ContextResult<()> {
tracing::debug!("Setting/restoring cargo user");
let cargo = self
.cargo
.as_ref()
.context("Cargo is not present")
.map_err(|e| ContextError::Cargo(e.to_string()))?;
let cargo_user_opt = cargo.user_api().get_user();
if let Ok(cargo_user) = cargo_user_opt {
tracing::debug!("found cargo user");
self.cargo_user = Some(cargo_user);
} else {
tracing::warn!("Cargo user not found, try creating one first");
return Err(ContextError::Recoverable(Box::new(
ContextError::CargoUser(s!("Cargo user not found, try creating one first")),
)));
}
Ok(())
}
#[allow(dead_code)]
pub fn set_all_refs(&mut self) -> ContextResult<()> {
self.set_cargo_user()?;
self.show_state();
Ok(())
}
pub fn set_full_cargo(&mut self, cargo: CargoLib) -> ContextResult<()> {
self.set_cargo(cargo)?;
self.set_cargo_user()?;
self.show_state();
Ok(())
}
}
#[tracing::instrument(level = "trace", err(Debug), skip(_args, context))]
fn aux_show_context(_args: ArgMatches, context: &mut Context) -> anyhow::Result<Option<String>> {
context.show_state();
Ok(Some(s!("")))
}
pub fn extend(repl: Repl<Context, anyhow::Error>) -> Repl<Context, anyhow::Error> {
repl.with_command(
Command::new("aux-ctx-show").about("show context"),
aux_show_context,
)
}
#[macro_export]
macro_rules! field_to_string {
(cargo_cfg) => {
"Cargo Config"
};
(sfo) => {
"Special File Operations Service"
};
(lss) => {
"Local Secure Storage"
};
(cargo) => {
"Cargo"
};
(cargo_user) => {
"Cargo User"
};
(dfs_api) => {
"DFS API"
};
}
#[macro_export]
macro_rules! build_mut_tuple {
($context:ident [] -> [$(,)? $($val:ident),*]) => {
($($context.$val.as_mut().unwrap()),*)
};
($context:ident [$val:ident => None, $($tail:tt)*] -> [$($body:tt)*]) => {
$crate::build_mut_tuple!($context [$($tail)*] -> [$($body)*])
};
($context:ident [$val:ident => Some(_), $($tail:tt)*] -> [$($body:tt)*]) => {
$crate::build_mut_tuple!($context [$($tail)*] -> [$($body)*, $val])
};
}
#[macro_export]
macro_rules! build_ifs {
($context:ident [$val:ident => Some(_), $($pattern:tt)*] $callback:ident($($args:tt)*)) => {
if $context.$val.is_none() {
Err(anyhow::Error::msg(
concat!($crate::field_to_string!($val), " should be set")
))
} else { $crate::build_ifs!($context [$($pattern)*] $callback($($args)*)) }
};
($context:ident [$val:ident => None, $($pattern:tt)*] $callback:ident($($args:tt)*)) => {
if $context.$val.is_some() {
Err(anyhow::Error::msg(
concat!($crate::field_to_string!($val)," should not be set")
))
} else { $crate::build_ifs!($context [$($pattern)*] $callback($($args)*)) }
};
($context:ident [] $callback:ident($($args:tt)*)) => {
Ok($crate::$callback!($context $($args)*))
};
}
#[macro_export]
macro_rules! match_context {
($context:ident, $($pattern:tt)+) => {{
$crate::build_ifs!($context [$($pattern)+,] build_mut_tuple([$($pattern)+,] -> []))
}};
}