use std::path::PathBuf;
use anyhow::{Context, Result};
use clap::Args;
use serde_json::{json, Value};
use crate::commands::apps::{app_context, print_detected, print_status, project_config_app_id};
use crate::output;
use crate::setup::{self, detect};
#[derive(Args)]
pub struct ApplyArgs {
#[arg(long)]
pub file: Option<PathBuf>,
#[arg(long, default_value = ".")]
pub dir: PathBuf,
#[arg(long)]
pub detect: bool,
#[arg(long)]
pub debug_cert: bool,
#[arg(long)]
pub app_id: Option<String>,
#[arg(long)]
pub dry_run: bool,
#[arg(long)]
pub json: bool,
}
pub async fn run(args: ApplyArgs) -> Result<()> {
let file = args
.file
.clone()
.unwrap_or_else(|| args.dir.join("offline.config.json"));
let config: Value = if file.exists() {
let text = std::fs::read_to_string(&file)
.with_context(|| format!("reading {}", file.display()))?;
serde_json::from_str(&text)
.with_context(|| format!("{} is not valid JSON", file.display()))?
} else if args.detect {
json!({})
} else {
anyhow::bail!(
"{} not found: run `offline init` to create it, add an \"app\" block, or pass --detect",
file.display()
);
};
let mut desired = setup::desired_from_config(&config);
let detected = args
.detect
.then(|| detect::detect(&args.dir, &detect::DetectOptions::default()));
if let Some(found) = &detected {
setup::merge_detected(&mut desired, found, args.debug_cert);
}
let app_flag = args
.app_id
.clone()
.or_else(|| {
config
.get("appId")
.and_then(Value::as_str)
.map(str::to_string)
})
.or_else(|| project_config_app_id(&args.dir));
let ctx = app_context(app_flag.as_deref(), "apply").await?;
let changes = setup::apply(&ctx, &desired, args.dry_run).await?;
let report = ctx.status(detected.as_ref()).await?;
if args.json {
return output::json(&json!({
"appId": ctx.app_id,
"dryRun": args.dry_run,
"changes": changes,
"detected": detected,
"status": report,
}));
}
if let Some(found) = &detected {
print_detected(found);
println!();
}
if changes.is_empty() {
output::success(&format!(
"{} already matches {}",
ctx.app_id,
file.display()
));
} else {
let verb = if args.dry_run {
"would change"
} else {
"changed"
};
output::heading(&format!("{verb} {}", ctx.app_id));
for c in &changes {
println!(" {c}");
}
}
println!();
print_status(&report);
Ok(())
}