mod config_ops;
mod context;
mod profiles;
use anyhow::{Context, Result};
use clap::Subcommand;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use crate::output::OutputFormat;
#[derive(Debug, Subcommand)]
pub enum ConfigCommands {
#[command(subcommand)]
Profile(ProfileCommands),
Get {
key: String,
},
Set {
key: String,
value: String,
},
#[command(subcommand)]
Context(ContextCommands),
MigrateTokens,
}
#[derive(Debug, Subcommand)]
pub enum ContextCommands {
Show,
Set {
key: String,
value: String,
},
Clear,
}
#[derive(Debug, Subcommand)]
pub enum ProfileCommands {
Create {
name: String,
},
List,
Use {
name: String,
},
Delete {
name: String,
},
Current,
Export {
#[arg(long = "out-file", default_value = "profiles-export.json")]
out_file: std::path::PathBuf,
#[arg(long)]
include_secrets: bool,
#[arg(short, long)]
name: Option<String>,
},
Import {
file: std::path::PathBuf,
#[arg(long)]
overwrite: bool,
},
Diff {
profile1: String,
profile2: String,
},
}
impl ConfigCommands {
pub async fn execute(self, output_format: OutputFormat) -> Result<()> {
match self {
ConfigCommands::Profile(cmd) => cmd.execute(output_format).await,
ConfigCommands::Get { key } => config_ops::get_config(&key, output_format).await,
ConfigCommands::Set { key, value } => {
config_ops::set_config(&key, &value, output_format).await
}
ConfigCommands::Context(cmd) => cmd.execute(output_format).await,
ConfigCommands::MigrateTokens => {
raps_kernel::storage::TokenStorage::migrate_to_keychain()
}
}
}
}
impl ContextCommands {
pub async fn execute(self, output_format: OutputFormat) -> Result<()> {
match self {
ContextCommands::Show => context::show_context(output_format).await,
ContextCommands::Set { key, value } => {
context::set_context(&key, &value, output_format).await
}
ContextCommands::Clear => context::clear_context(output_format).await,
}
}
}
impl ProfileCommands {
pub async fn execute(self, output_format: OutputFormat) -> Result<()> {
match self {
ProfileCommands::Create { name } => {
profiles::create_profile(&name, output_format).await
}
ProfileCommands::List => profiles::list_profiles(output_format).await,
ProfileCommands::Use { name } => profiles::use_profile(&name, output_format).await,
ProfileCommands::Delete { name } => {
profiles::delete_profile(&name, output_format).await
}
ProfileCommands::Current => profiles::show_current_profile(output_format).await,
ProfileCommands::Export {
out_file,
include_secrets,
name,
} => profiles::export_profiles(&out_file, include_secrets, name, output_format).await,
ProfileCommands::Import { file, overwrite } => {
profiles::import_profiles(&file, overwrite, output_format).await
}
ProfileCommands::Diff { profile1, profile2 } => {
profiles::diff_profiles(&profile1, &profile2, output_format).await
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileConfig {
pub client_id: Option<String>,
pub client_secret: Option<String>,
pub base_url: Option<String>,
pub callback_url: Option<String>,
pub da_nickname: Option<String>,
pub use_keychain: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_hub_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_project_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_account_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ProfilesData {
pub active_profile: Option<String>,
pub profiles: HashMap<String, ProfileConfig>,
}
fn profiles_path() -> Result<PathBuf> {
let proj_dirs = directories::ProjectDirs::from("com", "autodesk", "raps")
.context("Failed to get project directories")?;
let config_dir = proj_dirs.config_dir();
std::fs::create_dir_all(config_dir)?;
Ok(config_dir.join("profiles.json"))
}
pub(crate) fn load_profiles() -> Result<ProfilesData> {
let path = profiles_path()?;
if !path.exists() {
return Ok(ProfilesData {
active_profile: None,
profiles: HashMap::new(),
});
}
let content = std::fs::read_to_string(&path)?;
let data: ProfilesData =
serde_json::from_str(&content).context("Failed to parse profiles.json")?;
Ok(data)
}
fn save_profiles(data: &ProfilesData) -> Result<()> {
let path = profiles_path()?;
let content = serde_json::to_string_pretty(data)?;
std::fs::write(&path, content)?;
Ok(())
}