use colorful::Colorful;
use console::Term;
use miette::{miette, IntoDiagnostic};
use std::process::exit;
use std::sync::Arc;
use tokio::runtime::Runtime;
use tracing::{debug, info};
use ockam_api::colors::color_primary;
use ockam_api::logs::{
logging_configuration, Colored, ExportingConfiguration, LogLevelWithCratesFilter,
LoggingConfiguration, LoggingTracing, TracingGuard,
};
use ockam_api::terminal::{Terminal, TerminalStream};
use ockam_api::{fmt_err, fmt_log, fmt_ok, CliState};
use crate::subcommand::OckamSubcommand;
use crate::util::exitcode;
use crate::version::Version;
use crate::GlobalArgs;
#[derive(Clone, Debug)]
pub struct CommandGlobalOpts {
pub global_args: GlobalArgs,
pub state: CliState,
pub terminal: Terminal<TerminalStream<Term>>,
pub rt: Arc<Runtime>,
pub tracing_guard: Option<Arc<TracingGuard>>,
}
impl CommandGlobalOpts {
pub fn new(
arguments: &[String],
global_args: &GlobalArgs,
cmd: &OckamSubcommand,
) -> miette::Result<Self> {
let mut state = match CliState::from_env() {
Ok(state) => state,
Err(err) => {
if let OckamSubcommand::Reset(c) = cmd {
c.hard_reset();
println!("{}", fmt_ok!("Local Ockam configuration deleted"));
exit(exitcode::OK);
}
eprintln!("{}", fmt_err!("Failed to initialize local state"));
eprintln!(
"{}",
fmt_log!("Consider upgrading to the latest version of Ockam Command")
);
let ockam_home = std::env::var("OCKAM_HOME").unwrap_or("~/.ockam".to_string());
eprintln!(
"{}",
fmt_log!(
"You can also try removing the local state using {} \
or deleting the directory at {}",
color_primary("ockam reset"),
color_primary(ockam_home)
)
);
eprintln!("\n{:?}", miette!(err.to_string()));
exit(exitcode::SOFTWARE);
}
};
let logging_configuration =
Self::make_logging_configuration(global_args, cmd, Term::stdout().is_term())?;
let tracing_configuration = Self::make_tracing_configuration(&state, cmd)?;
let terminal = Terminal::new(
logging_configuration.is_enabled(),
logging_configuration.log_dir().is_some(),
global_args.quiet,
global_args.no_color,
global_args.no_input,
global_args.output_format(),
);
let tracing_guard =
Self::setup_logging_tracing(cmd, &logging_configuration, &tracing_configuration);
Self::log_inputs(
arguments,
global_args,
cmd,
&logging_configuration,
&tracing_configuration,
);
state = state.set_tracing_enabled(tracing_configuration.is_enabled());
let rt = Arc::new(Runtime::new().expect("cannot initialize the tokio runtime"));
Ok(Self {
global_args: global_args.clone(),
state,
terminal,
rt,
tracing_guard,
})
}
fn setup_logging_tracing(
cmd: &OckamSubcommand,
logging_configuration: &LoggingConfiguration,
tracing_configuration: &ExportingConfiguration,
) -> Option<Arc<TracingGuard>> {
if !logging_configuration.is_enabled() && !tracing_configuration.is_enabled() {
return None;
};
let app_name = if cmd.is_background_node() {
"local node"
} else {
"cli"
};
let tracing_guard = LoggingTracing::setup(
logging_configuration,
tracing_configuration,
app_name,
cmd.node_name(),
);
Some(Arc::new(tracing_guard))
}
fn make_logging_configuration(
global_args: &GlobalArgs,
cmd: &OckamSubcommand,
is_tty: bool,
) -> miette::Result<LoggingConfiguration> {
let log_path = cmd.log_path();
if cmd.is_background_node() {
Ok(LoggingConfiguration::background(log_path).into_diagnostic()?)
} else {
let level_and_crates =
LogLevelWithCratesFilter::from_verbose(global_args.verbose).into_diagnostic()?;
let log_path = if level_and_crates.explicit_verbose_flag || cmd.is_foreground_node() {
None
} else {
Some(CliState::command_log_path(cmd.name().as_str())?)
};
let colored = if !global_args.no_color && is_tty && log_path.is_none() {
Colored::On
} else {
Colored::Off
};
Ok(logging_configuration(level_and_crates, log_path, colored).into_diagnostic()?)
}
}
fn make_tracing_configuration(
state: &CliState,
cmd: &OckamSubcommand,
) -> miette::Result<ExportingConfiguration> {
Ok(if cmd.is_background_node() {
ExportingConfiguration::background(state).into_diagnostic()?
} else {
ExportingConfiguration::foreground(state).into_diagnostic()?
})
}
fn log_inputs(
arguments: &[String],
global_args: &GlobalArgs,
cmd: &OckamSubcommand,
logging_configuration: &LoggingConfiguration,
tracing_configuration: &ExportingConfiguration,
) {
debug!("Arguments: {}", arguments.join(" "));
debug!("Global arguments: {:#?}", &global_args);
debug!("Command: {:#?}", &cmd);
debug!("Version: {}", Version::new().no_color());
info!("Tracing initialized");
debug!("{:#?}", logging_configuration);
debug!("{:#?}", tracing_configuration);
}
pub fn set_quiet(&self) -> Self {
let mut clone = self.clone();
clone.global_args = clone.global_args.set_quiet();
clone.terminal = clone.terminal.set_quiet();
clone
}
pub fn force_flush(&self) {
if let Some(tracing_guard) = self.tracing_guard.clone() {
tracing_guard.force_flush();
};
}
pub fn shutdown(&self) {
if let Some(tracing_guard) = self.tracing_guard.clone() {
tracing_guard.force_flush();
tracing_guard.shutdown();
};
}
}