mod capture;
mod config;
mod snapshot;
mod ui;
use std::path::{Path, PathBuf};
use clap::Parser;
use crate::snapshot::SnapshotSettings;
use crate::ui::CamshooterApp;
#[derive(Parser)]
#[command(name = "camshooter", version, about)]
struct Cli {
#[arg(short, long, value_name = "DIR")]
output: Option<PathBuf>,
}
fn main() -> eframe::Result {
let cli = Cli::parse();
let config = match config::load() {
Ok(c) => c,
Err(msg) => {
eprintln!("camshooter: {msg}");
std::process::exit(2);
}
};
let out_dir = cli
.output
.or_else(|| config.output_dir_path())
.unwrap_or_else(|| PathBuf::from("."));
if let Err(msg) = validate_output_dir(&out_dir) {
eprintln!("camshooter: {msg}");
std::process::exit(2);
}
let settings = SnapshotSettings {
dir: out_dir,
prefix: config.prefix,
on_collision: config.on_collision,
};
let native_options = eframe::NativeOptions {
viewport: eframe::egui::ViewportBuilder::default()
.with_title("camshooter")
.with_inner_size([960.0, 720.0]),
..Default::default()
};
eframe::run_native(
"camshooter",
native_options,
Box::new(move |cc| Ok(Box::new(CamshooterApp::new(cc, settings)))),
)
}
fn validate_output_dir(dir: &Path) -> Result<(), String> {
if dir.exists() {
if !dir.is_dir() {
return Err(format!("output path {} is not a directory", dir.display()));
}
let probe = dir.join(".camshooter-write-probe");
std::fs::File::create(&probe)
.and_then(|_| std::fs::remove_file(&probe))
.map_err(|e| format!("output directory {} is not writable: {e}", dir.display()))?;
} else {
std::fs::create_dir_all(dir)
.map_err(|e| format!("cannot create output directory {}: {e}", dir.display()))?;
}
Ok(())
}