fancier error handling for config

main
xenofem 2023-07-30 02:47:55 -04:00
parent e4be6d9588
commit 9a90b18caf
2 changed files with 67 additions and 39 deletions

View File

@ -1,11 +1,14 @@
use std::{
env::{self, VarError},
error::Error,
fmt::Debug,
path::PathBuf,
str::FromStr,
time::Duration,
};
use anyhow::{anyhow, Context};
pub struct Config {
pub capture_images: bool,
pub capture_audio_duration: Option<f64>,
@ -22,37 +25,62 @@ pub struct Config {
const VAR_PREFIX: &str = "SCREENCAP_BOT_";
fn get_var(name: &str) -> Result<String, VarError> {
env::var(VAR_PREFIX.to_string() + name)
}
fn parse_var<E: Debug, T: FromStr<Err = E>>(name: &str) -> Result<T, VarError> {
get_var(name).map(|s| {
s.parse()
.unwrap_or_else(|e| panic!("Failed to parse {}{}: {:?}", VAR_PREFIX, name, e))
})
}
fn expect_var(name: &str) -> String {
get_var(name).unwrap_or_else(|_| panic!("{}{} must be set", VAR_PREFIX, name))
}
pub fn load() -> Config {
let capture_images = parse_var("CAPTURE_IMAGES").unwrap_or(true);
Config {
capture_images,
capture_audio_duration: parse_var("CAPTURE_AUDIO_DURATION").ok(),
shows_file: parse_var("SHOWS_FILE").unwrap_or(PathBuf::from("./shows.yaml")),
global_tags: get_var("GLOBAL_TAGS")
.map(|s| s.split(',').map(String::from).collect())
.unwrap_or_default(),
post_interval: Duration::from_secs(parse_var("POST_INTERVAL").unwrap_or_default()),
cohost_email: expect_var("COHOST_EMAIL"),
cohost_password: expect_var("COHOST_PASSWORD"),
cohost_page: expect_var("COHOST_PAGE"),
cohost_draft: parse_var("COHOST_DRAFT").unwrap_or(false),
cohost_cw: parse_var("COHOST_CW").unwrap_or(capture_images),
eighteen_plus: parse_var("18PLUS").unwrap_or(false),
fn get_var(name: &str) -> anyhow::Result<Option<String>> {
let var_name = VAR_PREFIX.to_string() + name;
match env::var(&var_name) {
Ok(v) => Ok(Some(v)),
Err(VarError::NotPresent) => Ok(None),
Err(e) => Err(e).with_context(|| format!("Failed to read variable {}", var_name)),
}
}
fn parse_var<E: Debug + Send + Sync + Error + 'static, T: FromStr<Err = E>>(
name: &str,
) -> anyhow::Result<Option<T>> {
get_var(name)?
.map(|s| {
s.parse()
.with_context(|| format!("Failed to parse variable {}{}", VAR_PREFIX, name))
})
.transpose()
}
fn require_var(name: &str) -> anyhow::Result<String> {
get_var(name)?.ok_or_else(|| anyhow!("{}{} must be set", VAR_PREFIX, name))
}
pub fn load() -> anyhow::Result<Config> {
let capture_images = parse_var("CAPTURE_IMAGES")?.unwrap_or(true);
let capture_audio_duration = parse_var("CAPTURE_AUDIO_DURATION")?;
if let Some(d) = capture_audio_duration {
if d <= 0.0 {
return Err(anyhow!(
"{}CAPTURE_AUDIO_DURATION cannot be <= 0",
VAR_PREFIX
));
}
}
if let (false, None) = (capture_images, capture_audio_duration) {
return Err(anyhow!(
"At least one of image capture and audio capture must be enabled!"
));
}
Ok(Config {
capture_images,
capture_audio_duration,
shows_file: parse_var("SHOWS_FILE")?.unwrap_or_else(|| PathBuf::from("./shows.yaml")),
global_tags: get_var("GLOBAL_TAGS")?
.map(|s| s.split(',').map(String::from).collect())
.unwrap_or_default(),
post_interval: Duration::from_secs(parse_var("POST_INTERVAL")?.unwrap_or_default()),
cohost_email: require_var("COHOST_EMAIL")?,
cohost_password: require_var("COHOST_PASSWORD")?,
cohost_page: require_var("COHOST_PAGE")?,
cohost_draft: parse_var("COHOST_DRAFT")?.unwrap_or(false),
cohost_cw: parse_var("COHOST_CW")?.unwrap_or(capture_images),
eighteen_plus: parse_var("18PLUS")?.unwrap_or(false),
})
}

View File

@ -27,13 +27,7 @@ async fn main() -> anyhow::Result<()> {
let mut rng = rand::thread_rng();
let conf = config::load();
if let (false, None) = (conf.capture_images, conf.capture_audio_duration) {
return Err(anyhow!(
"At least one of image capture and audio capture must be enabled!"
));
}
let conf = config::load()?;
info!("Loading shows from {}", conf.shows_file.display());
let shows = shows::load(&conf.shows_file).with_context(|| {
@ -110,6 +104,12 @@ async fn post_random_capture<R: Rng>(
Some(d) => media_info.duration_secs - d,
None => media_info.duration_secs,
};
if max_timestamp < 0.0 {
return Err(anyhow!(
"Media file {} is too short to take audio clip!",
file.display()
));
}
let timestamp = max_timestamp * rng.sample::<f64, _>(Standard);
let formatted_timestamp = format_timestamp(timestamp, Some(media_info.duration_secs));
info!("Taking capture at {}", formatted_timestamp);