#![warn(missing_docs)]
use anyhow::{bail, Context, Result};
use std::path::PathBuf;
use std::thread::sleep;
use std::{collections::HashMap, time};
use tracing::{debug, info, warn};
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter};
pub mod config;
pub mod mattermost;
pub mod offtime;
pub mod state;
pub mod utils;
pub mod wifiscan;
pub use config::{Args, WifiStatusConfig};
pub use mattermost::MMStatus;
use offtime::Off;
pub use state::{Cache, Location, State};
pub use wifiscan::{WiFi, WifiInterface};
pub fn setup_tracing(args: &Args) -> Result<()> {
let fmt_layer = fmt::layer().with_target(false);
let filter_layer = EnvFilter::try_new(args.verbose.get_level_filter().to_string()).unwrap();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
Ok(())
}
pub fn get_cache(dir: Option<PathBuf>) -> Result<Cache> {
let mut state_file_name: PathBuf;
if let Some(ref state_dir) = dir {
state_file_name = PathBuf::from(state_dir);
} else {
bail!("Internal Error, no `state_dir` configured");
}
state_file_name.push("automattermostatus.state");
Ok(Cache::new(state_file_name))
}
pub fn prepare_status(args: &Args) -> Result<HashMap<Location, MMStatus>> {
let mut res = HashMap::new();
for s in &args.status {
let sc: WifiStatusConfig = s.parse().with_context(|| format!("Parsing {}", s))?;
debug!("Adding : {:?}", sc);
res.insert(
Location::Known(sc.wifi_string),
MMStatus::new(
sc.text,
sc.emoji,
args.mm_url
.as_ref()
.expect("Mattermost URL is not defined")
.clone(),
args.mm_token
.clone()
.expect("Mattermost private access token is not defined"),
),
);
}
Ok(res)
}
pub fn get_wifi_and_update_status_loop(
args: Args,
mut status_dict: HashMap<Location, MMStatus>,
) -> Result<()> {
let cache = get_cache(args.state_dir.to_owned()).context("Reading cached state")?;
let mut state = State::new(&cache).context("Creating cache")?;
let delay_duration = time::Duration::new(
args.delay
.expect("Internal error: args.delay shouldn't be None")
.into(),
0,
);
let wifi = WiFi::new(
&args
.interface_name
.clone()
.expect("Internal error: args.interface_name shouldn't be None"),
);
if !wifi
.is_wifi_enabled()
.context("Checking if wifi is enabled")?
{
bail!("wifi is disabled");
} else {
info!("Wifi is enabled");
}
loop {
if !&args.is_off_time() {
let ssids = wifi.visible_ssid().context("Getting visible SSIDs")?;
debug!("Visible SSIDs {:#?}", ssids);
let mut found_ssid = false;
for (l, mmstatus) in status_dict.iter_mut() {
if let Location::Known(wifi_substring) = l {
if ssids.iter().any(|x| x.contains(wifi_substring)) {
if wifi_substring.is_empty() {
debug!("We do not match against empty SSID reserved for off time");
break;
}
debug!("known wifi '{}' detected", wifi_substring);
found_ssid = true;
let mmstatus = mmstatus.clone().expires_at(&args.expires_at);
state
.update_status(l.clone(), Some(&mmstatus), &cache)
.context("Updating status")?;
break;
}
}
}
if !found_ssid {
debug!("Unknown wifi");
state
.update_status(Location::Unknown, None, &cache)
.context("Updating status")?;
}
} else {
debug!("{:?}", status_dict);
let off_location = Location::Known("".to_string());
if let Some(offstatus) = status_dict.get_mut(&off_location) {
debug!("Setting state for Offtime");
state
.update_status(off_location, Some(offstatus), &cache)
.context("Updating status")?;
}
}
if let Some(0) = args.delay {
break;
} else {
sleep(delay_duration);
}
}
Ok(())
}