diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 944b95ad..6608edab 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1,9 +1,6 @@ -use std::path::PathBuf; - +use crate::config::config::ConfigOptionCli; use clap::{Args, Parser, Subcommand, ValueEnum}; -use crate::config::config::ConfigOption; - #[derive(Parser)] #[command(version, about, long_about = None)] pub struct Cli { @@ -17,9 +14,13 @@ pub struct Cli { #[derive(Subcommand)] pub enum Commands { - /// Configures a new Drop server - #[command(subcommand)] - Configure(ConfigOption), + /// Configures downpour endpoints + Configure { + #[arg(short, long)] + name: String, + #[command(subcommand)] + option: ConfigOptionCli + }, /// Uploads new game version to depot Upload(UploadInfo), } @@ -29,8 +30,8 @@ pub struct UploadInfo { /// Identifies the specific upload style that will be used for the set depot pub upload_style: UploadStyle, /// Relative path to new version files - #[arg(short, long)] - pub path: PathBuf, + #[arg(short, long, default_value_t = String::from("."))] + pub path: String, /// ID of game to attach to #[arg(short, long)] pub game_id: String, diff --git a/cli/src/config/config.rs b/cli/src/config/config.rs index 27ff92a2..440e2ed3 100644 --- a/cli/src/config/config.rs +++ b/cli/src/config/config.rs @@ -1,20 +1,17 @@ -use std::{fs, str::FromStr}; - -use clap::Subcommand; -use dialoguer::{Input, theme::ColorfulTheme}; -use log::warn; -use serde::{Deserialize, Serialize}; - use crate::config::{ s3::{S3Config, S3ConfigCli}, server::ServerConfig, }; +use clap::Subcommand; +use log::warn; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, fs}; const CONFIG_DIR: &str = "downpour/config.json"; #[derive(Serialize, Deserialize)] pub struct Config { - items: Vec, + items: HashMap, active_s3: Option, } impl Config { @@ -23,6 +20,7 @@ impl Config { let save_path = dirs::config_dir() .expect("Apparently your home directory doesn't exist") // Should probably formalise that error .join(CONFIG_DIR); + fs::create_dir_all(save_path.parent().unwrap())?; fs::write(save_path, json)?; Ok(()) } @@ -36,29 +34,30 @@ impl Config { Config::new() } } - pub fn add_item(&mut self, item: ConfigItem) { - if matches!(item.config_option, ConfigOption::S3(..)) { - self.active_s3 = Some(item.name.clone()) + pub fn add_item(&mut self, name: String, object: ConfigOption) { + if matches!(object, ConfigOption::S3(..)) { + self.active_s3 = Some(name.clone()) } - self.items.push(item); + self.items.insert(name, object); + self.save().expect("Failed to save config"); } } -#[derive(Serialize, Deserialize)] -pub struct ConfigItem { - name: String, - config_option: ConfigOption, -} -#[derive(Subcommand, Serialize, Deserialize)] -pub enum ConfigOption { +#[derive(Subcommand, Clone)] +pub enum ConfigOptionCli { Server(ServerConfig), S3(S3ConfigCli), } +#[derive(Serialize, Deserialize, Clone)] +pub enum ConfigOption { + Server(ServerConfig), + S3(S3Config), +} impl Config { pub fn new() -> Self { Self { - items: Vec::new(), + items: HashMap::new(), active_s3: None, } } @@ -66,12 +65,12 @@ impl Config { if let Some(active_s3) = &self.active_s3 { self.items .iter() - .filter_map(|item| { - if item.name == *active_s3 { - match &item.config_option { + .filter_map(|(name, option)| { + if *name == *active_s3 { + match option { ConfigOption::S3(s3_config) => Some(s3_config), _ => { - warn!("Name {} is not of type 'S3'", item.name); + warn!("Name {} is not of type 'S3'", name); None } } @@ -87,4 +86,3 @@ impl Config { } } } - diff --git a/cli/src/config/configurable.rs b/cli/src/config/configurable.rs deleted file mode 100644 index 8a81aca2..00000000 --- a/cli/src/config/configurable.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::config::config::Config; - -/// Trait which represents data which may be stored in `config_dir/downpour/config.json` -pub trait Configurable { - fn configure(&self, config: &mut Config); -} - diff --git a/cli/src/config/configure.rs b/cli/src/config/configure.rs new file mode 100644 index 00000000..5c684cfe --- /dev/null +++ b/cli/src/config/configure.rs @@ -0,0 +1,4 @@ +pub trait Configurable { + type Out; + fn configure(self) -> Self::Out; +} \ No newline at end of file diff --git a/cli/src/interactive.rs b/cli/src/config/interactive.rs similarity index 88% rename from cli/src/interactive.rs rename to cli/src/config/interactive.rs index bc87ee4b..0c72ca03 100644 --- a/cli/src/interactive.rs +++ b/cli/src/config/interactive.rs @@ -8,7 +8,7 @@ macro_rules! interactive_variable { let $var = if let Some($var) = $value.$var { $var } else { - crate::interactive::query_variable($prompt).unwrap() + crate::config::interactive::query_variable($prompt).unwrap() }; }; } @@ -18,7 +18,7 @@ macro_rules! interactive_optional_variable { let $var = if let Some($var) = $value.$var { Some($var) } else { - crate::interactive::query_optional_variable($prompt).unwrap() + crate::config::interactive::query_optional_variable($prompt).unwrap() }; }; } diff --git a/cli/src/config/mod.rs b/cli/src/config/mod.rs index 8fd2538b..00b59511 100644 --- a/cli/src/config/mod.rs +++ b/cli/src/config/mod.rs @@ -1,4 +1,6 @@ pub mod config; -pub mod configurable; pub mod s3; pub mod server; +pub mod configure; +#[macro_use] +pub mod interactive; diff --git a/cli/src/config/s3.rs b/cli/src/config/s3.rs index 798f8eee..d2c11f78 100644 --- a/cli/src/config/s3.rs +++ b/cli/src/config/s3.rs @@ -1,11 +1,10 @@ use std::str::FromStr; use clap::Args; -use dialoguer::{Input, theme::ColorfulTheme}; use s3::{Bucket, Region, creds::Credentials}; use serde::{Deserialize, Serialize}; -use crate::{config::configurable::Configurable, interactive_optional_variable, interactive_variable}; +use crate::{config::configure::Configurable, interactive_optional_variable, interactive_variable}; #[derive(Serialize, Deserialize, Args, Clone)] @@ -17,14 +16,16 @@ pub struct S3ConfigCli { endpoint: Option, } -impl From for S3Config { - fn from(value: S3ConfigCli) -> Self { - interactive_variable!(value, secret_key, "S3 Secret Key"); - interactive_variable!(value, key_id, "S3 Key ID"); - interactive_variable!(value, region, "S3 Region"); - interactive_variable!(value, bucket_name, "S3 Bucket Name"); - interactive_optional_variable!(value, endpoint, "S3 Endpoint (leave blank for none"); - Self { +impl Configurable for S3ConfigCli { + type Out = S3Config; + + fn configure(self) -> Self::Out { + interactive_variable!(self, secret_key, "S3 Secret Key"); + interactive_variable!(self, key_id, "S3 Key ID"); + interactive_variable!(self, region, "S3 Region"); + interactive_variable!(self, bucket_name, "S3 Bucket Name"); + interactive_optional_variable!(self, endpoint, "S3 Endpoint (leave blank for none"); + Self::Out { secret_key, key_id, region, @@ -34,9 +35,7 @@ impl From for S3Config { } } - - -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct S3Config { secret_key: String, key_id: String, @@ -63,10 +62,4 @@ impl S3Config { Ok(*bucket) } -} - -impl Configurable for S3Config { - fn configure(&self, config: &mut super::config::Config) { - println!("Configuring S3Config with {:?}", self); - } -} +} \ No newline at end of file diff --git a/cli/src/config/server.rs b/cli/src/config/server.rs index a2939826..5ecf6757 100644 --- a/cli/src/config/server.rs +++ b/cli/src/config/server.rs @@ -1,18 +1,10 @@ use clap::Args; use serde::{Deserialize, Serialize}; -use crate::config::configurable::Configurable; - #[derive(Serialize, Deserialize, Args, Clone)] pub struct ServerConfig { /// Endpoint of the Drop server url: String, #[arg(short, long)] token: String, -} - -impl Configurable for ServerConfig { - fn configure(&self, config: &mut super::config::Config) { - println!("Configured ServerConfig") - } } \ No newline at end of file diff --git a/cli/src/logging.rs b/cli/src/logging.rs new file mode 100644 index 00000000..8f64982f --- /dev/null +++ b/cli/src/logging.rs @@ -0,0 +1,54 @@ +use fern::colors::{Color, ColoredLevelConfig}; +use log::LevelFilter; +use std::env; +use std::fs; +use std::io; + + +pub fn configure_logging() -> anyhow::Result<()> { + let log_level = env::var("RUST_LOG") + .unwrap_or_else(|_| "info".to_string()) + .parse::()?; + + let log_dir = env::var("LOG_FILE_DIR").unwrap_or_else(|_| "logs".to_string()); + + fs::create_dir_all(&log_dir)?; + + let colors = ColoredLevelConfig::new() + .error(Color::Red) + .warn(Color::Yellow) + .info(Color::Blue) + .debug(Color::Green) + .trace(Color::Magenta); + + fern::Dispatch::new() + .chain( + fern::Dispatch::new() + .format(move |out, message, record| { + out.finish(format_args!( + "[{}] {}: {}", + chrono::Local::now().format("%H:%M:%S%.3f"), + colors.color(record.level()), + message + )) + }) + .chain(io::stdout()), + ) + .chain( + fern::Dispatch::new() + .format(|out, message, record| { + out.finish(format_args!( + "[{}] {} {} - {}", + chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), + record.level(), + record.target(), + message + )) + }) + .chain(fern::log_file(format!("{}/app.log", log_dir))?), + ) + .level(log_level) + .apply()?; + + Ok(()) +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 4782ad8b..5d0d9774 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,36 +1,31 @@ +use crate::config::configure::Configurable; use crate::{ cli::{Cli, Commands}, - commands::{configure::interactive_configure, upload}, - config::{ - config::{Config, ConfigOption}, - configurable::Configurable, - s3::S3Config, - }, + commands::upload, + config::config::Config, }; use clap::Parser; -use fern::colors::{Color, ColoredLevelConfig}; -use log::LevelFilter; -use std::env; -use std::fs; -use std::io; mod cli; mod commands; mod config; +mod logging; mod manifest; -#[macro_use] -pub mod interactive; - - #[tokio::main] async fn main() -> anyhow::Result<()> { - configure_logging()?; + crate::logging::configure_logging()?; + let cli = Cli::parse(); + let mut config = Config::read(); match &cli.command { - Commands::Configure(options) => { - configure_command(&mut config, options).await?; - } + Commands::Configure { name, option } => match option { + config::config::ConfigOptionCli::Server(server_config) => todo!(), + config::config::ConfigOptionCli::S3(s3_config_cli) => config.add_item( + name.clone(), + config::config::ConfigOption::S3(s3_config_cli.clone().configure()), + ), + }, Commands::Upload(info) => { upload::interface::upload(info, config).await?; } @@ -38,60 +33,3 @@ async fn main() -> anyhow::Result<()> { Ok(()) } - -async fn configure_command(config: &mut Config, options: &ConfigOption) -> anyhow::Result<()> { - let configuration: Box = match options { - ConfigOption::Server(options) => Box::new(options.clone()), - ConfigOption::S3(options) => Box::new(S3Config::from(options.clone())), - }; - configuration.configure(config); - Ok(()) -} - -pub fn configure_logging() -> anyhow::Result<()> { - let log_level = env::var("RUST_LOG") - .unwrap_or_else(|_| "info".to_string()) - .parse::()?; - - let log_dir = env::var("LOG_FILE_DIR").unwrap_or_else(|_| "logs".to_string()); - - fs::create_dir_all(&log_dir)?; - - let colors = ColoredLevelConfig::new() - .error(Color::Red) - .warn(Color::Yellow) - .info(Color::Blue) - .debug(Color::Green) - .trace(Color::Magenta); - - fern::Dispatch::new() - .chain( - fern::Dispatch::new() - .format(move |out, message, record| { - out.finish(format_args!( - "[{}] {}: {}", - chrono::Local::now().format("%H:%M:%S%.3f"), - colors.color(record.level()), - message - )) - }) - .chain(io::stdout()), - ) - .chain( - fern::Dispatch::new() - .format(|out, message, record| { - out.finish(format_args!( - "[{}] {} {} - {}", - chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), - record.level(), - record.target(), - message - )) - }) - .chain(fern::log_file(format!("{}/app.log", log_dir))?), - ) - .level(log_level) - .apply()?; - - Ok(()) -}