awatcher/src/config.rs
2024-06-24 01:16:40 -04:00

157 lines
5.7 KiB
Rust

use std::path::Path;
use std::path::PathBuf;
use clap::parser::ValueSource;
use clap::{arg, value_parser, Arg, ArgAction, ArgMatches, Command};
use fern::colors::{Color, ColoredLevelConfig};
use log::LevelFilter;
use watchers::config::defaults;
use watchers::config::Config;
use watchers::config::FileConfig;
pub struct RunnerConfig {
pub watchers_config: Config,
#[cfg(feature = "bundle")]
pub config_file: PathBuf,
#[cfg(feature = "bundle")]
pub no_tray: bool,
}
pub fn setup_logger(verbosity: LevelFilter) -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
let colors = ColoredLevelConfig::new()
.info(Color::Green)
.debug(Color::Blue)
.trace(Color::Cyan);
out.finish(format_args!(
"[{} {} {}] {}",
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.6f"),
colors.color(record.level()),
record.target(),
message
));
})
.level(log::LevelFilter::Warn)
.level_for("watchers", verbosity)
.level_for("awatcher", verbosity)
.chain(std::io::stdout())
.apply()?;
Ok(())
}
pub fn from_cli() -> anyhow::Result<RunnerConfig> {
let matches = Command::new("Activity Watcher")
.version(env!("CARGO_PKG_VERSION"))
.about(
#[cfg(not(feature = "bundle"))]
"X11 and Wayland active window and idle watcher for ActivityWatch server",
#[cfg(feature = "bundle")]
"X11 and Wayland active window and idle watcher with a bundled ActivityWatch server",
)
.args([
arg!(-c --config <FILE> "Custom config file").value_parser(value_parser!(PathBuf)),
arg!(--port <PORT> "Custom server port")
.value_parser(value_parser!(u32))
.default_value(defaults::port().to_string()),
#[cfg(not(feature = "bundle"))]
arg!(--host <HOST> "Custom server host")
.value_parser(value_parser!(String))
.default_value(defaults::host()),
arg!(--"idle-timeout" <SECONDS> "Time of inactivity to consider the user idle")
.value_parser(value_parser!(u32))
.default_value(defaults::idle_timeout_seconds().to_string()),
arg!(--"poll-time-idle" <SECONDS> "Period between sending heartbeats to the server for idle activity")
.value_parser(value_parser!(u32))
.default_value(defaults::poll_time_idle_seconds().to_string()),
arg!(--"poll-time-window" <SECONDS> "Period between sending heartbeats to the server for window activity")
.value_parser(value_parser!(u32))
.default_value(defaults::poll_time_window_seconds().to_string()),
arg!(--"no-server" "Don't send data to the ActivityWatch server")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
#[cfg(feature = "bundle")]
arg!(--"no-tray" "Don't use the bundled tray, run only server and watchers in the background")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
Arg::new("verbosity")
.short('v')
.help("Verbosity level: -v for warnings, -vv for info, -vvv for debug, -vvvv for trace")
.action(ArgAction::Count),
])
.get_matches();
let config = new_with_cli(&matches)?;
let verbosity = match matches.get_count("verbosity") {
0 => LevelFilter::Error,
1 => LevelFilter::Warn,
2 => LevelFilter::Info,
3 => LevelFilter::Debug,
_ => LevelFilter::Trace,
};
setup_logger(verbosity)?;
Ok(RunnerConfig {
watchers_config: Config {
port: config.server.port,
host: config.server.host,
idle_timeout: config.client.get_idle_timeout(),
poll_time_idle: config.client.get_poll_time_idle(),
poll_time_window: config.client.get_poll_time_window(),
filters: config.client.filters,
no_server: *matches.get_one("no-server").unwrap(),
},
#[cfg(feature = "bundle")]
config_file: config.config_file,
#[cfg(feature = "bundle")]
no_tray: *matches.get_one("no-tray").unwrap(),
})
}
pub fn new_with_cli(matches: &ArgMatches) -> anyhow::Result<FileConfig> {
let mut config_path = None;
if matches.contains_id("config") {
let config_file = matches.get_one::<PathBuf>("config");
if let Some(path) = config_file {
config_path = Some(Path::new(path).to_path_buf());
}
}
let mut config = FileConfig::new(config_path)?;
merge_cli(&mut config, matches);
Ok(config)
}
fn merge_cli(config: &mut FileConfig, matches: &ArgMatches) {
get_arg_value(
"poll-time-idle",
matches,
&mut config.client.poll_time_idle_seconds,
);
get_arg_value(
"poll-time-window",
matches,
&mut config.client.poll_time_window_seconds,
);
get_arg_value(
"idle-timeout",
matches,
&mut config.client.idle_timeout_seconds,
);
get_arg_value("port", matches, &mut config.server.port);
#[cfg(not(feature = "bundle"))]
get_arg_value("host", matches, &mut config.server.host);
}
fn get_arg_value<T>(id: &str, matches: &ArgMatches, config_value: &mut T)
where
T: Clone + Send + Sync + 'static,
{
if let Some(ValueSource::CommandLine) = matches.value_source(id) {
let value = &mut matches.get_one::<T>(id).unwrap().clone();
std::mem::swap(config_value, value);
}
}