Setup base eframe application + serialization + arg parsing and configuration

This commit is contained in:
2026-09-16 20:13:36 -04:00
commit a33a40c27f
12 changed files with 6855 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
use std::time::Duration;
use eframe::{egui, Frame, Storage, CreationContext};
use eframe::egui::{Context, Ui};
use serde::{Deserialize, Serialize};
#[derive(Default)]
pub(crate) struct Application {
settings: AppSettings,
}
#[derive(Serialize, Deserialize, Default)]
struct AppSettings {
serialization_test_string: String,
}
impl Application {
pub fn new(cc: &CreationContext) -> Self {
let mut settings = AppSettings::default();
if let Some(storage) = cc.storage {
if let Some(stored_str) = storage.get_string("app_settings") {
if let Ok(parsed) = serde_json::from_str(&stored_str) {
settings = parsed;
}
}
}
Self { settings }
}
}
impl eframe::App for Application {
fn logic(&mut self, ctx: &Context, frame: &mut Frame) {
}
fn ui(&mut self, ui: &mut Ui, frame: &mut Frame) {
egui::CentralPanel::default().show(ui, |ui| {
ui.heading("Hello!");
ui.text_edit_singleline(&mut self.settings.serialization_test_string);
});
}
fn save(&mut self, _storage: &mut dyn Storage) {
if let Ok(serialized) = serde_json::to_string(&self.settings) {
_storage.set_string("app_settings", serialized);
}
}
fn on_exit(&mut self) {
}
fn auto_save_interval(&self) -> Duration {
Duration::from_secs(5)
}
}
+43
View File
@@ -0,0 +1,43 @@
use clap::Parser;
use crate::{args};
#[derive(Parser, Debug)]
pub(crate) struct Args {
/// Override the state file location
pub(crate) state: Option<String>,
/// Override the default data dir
#[clap(short, long)]
pub(crate) data_dir: Option<String>,
/// Enable verbose logging
#[clap(short, long)]
pub(crate) verbose: Option<bool>,
}
#[derive(Debug)]
pub(crate) struct ArgsConfig {
pub(crate) state: String,
pub(crate) data_dir: String,
pub(crate) verbose: bool,
}
pub(crate) fn parse_args() -> ArgsConfig {
let args = args::Args::parse();
let mut args_config = args::ArgsConfig {
state: "".to_string(),
data_dir: "".to_string(),
verbose: false,
};
if args.data_dir.is_none() {
let data_dir = dirs::data_dir().unwrap_or_else(|| "./".into());
args_config.data_dir = format!("{}/yt-offline", data_dir.into_string().expect("Failed to convert data directory into string"));
}
// TODO: add a check in case the passed in data dir as a / at the end
if args.state.is_some() {
args_config.state = format!("{}/{}", args_config.data_dir, args.state.expect("Failed to unwrap config arg"));
} else {
args_config.state = format!("{}/state.json", args_config.data_dir);
}
args_config
}
+31
View File
@@ -0,0 +1,31 @@
use tracing::log::LevelFilter;
use tracing_subscriber::EnvFilter;
use tracing::debug;
#[allow(unused_variables)]
pub fn init(is_verbose: bool) {
let lvl_filter;
let directive;
#[cfg(debug_assertions)]
{
lvl_filter = LevelFilter::Info;
directive = "yt_offline=DEBUG";
}
#[cfg(not(debug_assertions))]
{
if is_verbose{
lvl_filter = LevelFilter::Info;
directive = "yt_offline=INFO";
} else {
lvl_filter = LevelFilter::Warn;
directive = "yt_offline=WARN";
}
}
let filter = EnvFilter::builder()
.with_default_directive(lvl_filter.as_str().parse().expect("Failed to parse lvl_filter into directive"))
.from_env().expect("Failed to parse env filter")
.add_directive(directive.parse().expect("Failed to parse directive"));
tracing_subscriber::fmt().with_env_filter(filter).compact().init();
debug!("Successfully setup tracing subscriber.");
}
+34
View File
@@ -0,0 +1,34 @@
use std::fs;
use std::path::PathBuf;
use std::process::exit;
use tracing::{debug, error};
mod logging;
mod args;
mod app;
fn main() {
let args = args::parse_args();
logging::init(args.verbose);
debug!("Using args: \n{:#?}\n", args);
let data_dir_path = PathBuf::from(&args.data_dir);
if data_dir_path.is_file() {
error!("data_dir is a file... If you have set the data_dir manually, please pass in a directory. If you didn't pass in a path at all, please report this.");
error!("{:#?}", args);
exit(1);
}
if !data_dir_path.exists() {
if let Err(e) = fs::create_dir(data_dir_path.as_path()) {
error!("Failed to create data_dir path with fs error: {}", e);
exit(1);
}
}
let mut options = eframe::NativeOptions::default();
options.persistence_path = Some(args.state.parse().expect("Failed to parse state config file path into path"));
eframe::run_native(
"yt-offline",
options,
Box::new(|cc| {
Ok(Box::new(app::Application::new(cc)))
})).expect("TODO: panic message");
}