use crate::config::app::AppConfig;
use crate::config::turnkey::{Config, StoredQosOperatorKey};
use crate::outcome::Outcome;
use crate::output::StdCtx;
use crate::prompts::{bail_interactive_conflicts_with_non_interactive, ensure_stdin_is_tty};
use anyhow::{Context, Result, bail};
use clap::Args as ClapArgs;
use serde::Serialize;
use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
#[derive(Debug, ClapArgs)]
#[command(about, long_about = None)]
pub struct Args {
#[arg(
short,
long,
value_name = "PATH",
default_value = "app.json",
env = "TVC_APP_CONFIG_OUT"
)]
pub output: PathBuf,
#[arg(long)]
pub interactive: bool,
}
pub async fn run(ctx: &mut StdCtx, args: Args) -> Result<Outcome> {
if args.interactive {
if ctx.is_non_interactive() {
bail_interactive_conflicts_with_non_interactive()?;
} else {
ensure_stdin_is_tty()?;
}
}
execute(args).await
}
async fn execute(args: Args) -> Result<Outcome> {
if args.output.exists() {
bail!("File already exists: {}", args.output.display());
}
let operator_public_key = load_operator_public_key().await;
let mut config = AppConfig::template(operator_public_key.as_deref());
if args.interactive {
config.fill_interactively(operator_public_key.as_deref())?;
}
let json = serde_json::to_string_pretty(&config).context("failed to serialize config")?;
std::fs::write(&args.output, json)
.with_context(|| format!("failed to write file: {}", args.output.display()))?;
Ok(Outcome::AppInit(AppConfigCreated {
command: "app init",
path: args.output.display().to_string(),
template: !args.interactive,
interactive: args.interactive,
}))
}
#[derive(Default, Serialize)]
pub struct AppConfigCreated {
command: &'static str,
path: String,
template: bool,
interactive: bool,
}
impl Display for AppConfigCreated {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.interactive {
write!(
f,
r#"Created app config: {}
Run: tvc app create --config-file {}"#,
self.path, self.path
)
} else {
write!(
f,
r#"Created app config template: {}
Edit the file to fill in your values, then run:
tvc app create --config-file {}"#,
self.path, self.path
)
}
}
}
async fn load_operator_public_key() -> Option<String> {
let config = Config::load().await.ok()?;
let (alias, org_config) = config.active_org_config()?;
let local = org_config.select_local_record(alias).ok()?;
let operator_key = StoredQosOperatorKey::load(&local.key_path).await.ok()??;
Some(operator_key.public_key)
}