wthrr 0.7.0

Weather companion for the terminal
use colored::{Color::Yellow, Colorize};
use directories::ProjectDirs;
use optional_struct::Applyable;
use ron::{
	extensions::Extensions,
	ser::{to_string_pretty, PrettyConfig},
	Options,
};
use std::{
	fs::{self, File},
	io::Write,
	path::PathBuf,
};

use anyhow::{Context, Result};
use dialoguer::{theme::ColorfulTheme, Confirm, Select};

use crate::modules::{
	args::Cli,
	params::{ConfigFile, Params},
	translation::translate,
};

const CONFIG_DIR_NAME: &str = "weathercrab";
const CONFIG_FILE_NAME: &str = "wthrr.ron";

impl Params {
	fn get_path() -> PathBuf {
		ProjectDirs::from("", "", CONFIG_DIR_NAME)
			.unwrap()
			.config_dir()
			.join(CONFIG_FILE_NAME)
	}

	pub fn get_config_file() -> Params {
		let mut config = Params::default();
		let path = Self::get_path();

		if let Ok(file) = fs::read_to_string(&path) {
			let cfg_form_file: ConfigFile = match Options::default()
				.with_default_extension(Extensions::IMPLICIT_SOME)
				.from_str(&file)
			{
				Ok(contents) => contents,
				Err(error) => {
					let warning_sign = " Warning:".color(Yellow);
					println!(
						"{warning_sign} {}\n{: >4}At: {error}.\n{: >4}Falling back to default values.\n",
						path.display(),
						"",
						"",
					);
					return config;
				}
			};

			cfg_form_file.apply_to(&mut config);
		}

		config
	}

	fn store(&self) {
		let path = Self::get_path();

		let cfg_dir = path.parent().unwrap();
		if !cfg_dir.is_dir() {
			fs::create_dir(cfg_dir).unwrap();
		};

		let mut file = File::create(path).unwrap();
		let output = to_string_pretty(self, PrettyConfig::default()).unwrap();
		file.write_all(output.as_bytes()).unwrap();
	}

	pub async fn handle_next(self, args: Cli, mut config: Self) -> Result<()> {
		if !args.save && !config.address.is_empty() {
			return Ok(());
		}

		if config.address.is_empty() {
			self.apply_to(&mut config);
			config
				.save_prompt(args.address.as_deref().unwrap_or_default().to_string())
				.await?;
		} else {
			self.apply_to(&mut config);
			Self::store(&config);
		}

		Ok(())
	}

	async fn save_prompt(mut self, arg_address: String) -> Result<()> {
		let mut items = vec![
			translate(&self.language, "Yes please").await?,
			translate(&self.language, "No, ask me next time").await?,
			translate(&self.language, "No, dont ask me again").await?,
		];

		if arg_address.is_empty() || arg_address == "auto" {
			items.push(translate(&self.language, "Always check for a weather station").await?)
		}

		let selection = Select::with_theme(&ColorfulTheme::default())
			.with_prompt(translate(&self.language, "Would you like to use this as your default?").await?)
			.items(&items)
			.default(0)
			.interact()?;

		match selection {
			0 => {}
			1 => return Ok(()),
			2 => self.address = "arg_input".to_string(),
			3 => self.address = "auto".to_string(),
			_ => println!("User did not select anything or exited using Esc or q"),
		}

		Self::store(&self);

		Ok(())
	}

	pub async fn reset(lang: &str) -> Result<()> {
		let confirmation = Confirm::with_theme(&ColorfulTheme::default())
			.with_prompt(translate(lang, "This will wipe wthrr's configuration. Continue?").await?)
			.interact()?;

		if confirmation {
			let path = Self::get_path();

			std::fs::remove_dir_all(path.parent().unwrap()).with_context(|| "Error resetting config file.")?;
		}

		Ok(())
	}
}