use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use serde_json::{Map, Value, json};
use time::OffsetDateTime;
use time::macros::format_description;
#[derive(Clone, Debug)]
pub struct UpdateOptions {
pub version: String,
pub channel: String,
pub output: PathBuf,
pub existing: Option<PathBuf>,
}
pub fn update(options: &UpdateOptions) -> Result<()> {
let mut data = load_existing(options.existing.as_deref())?;
let display_version = options.version.trim_start_matches('v');
data.insert(
options.channel.clone(),
json!({
"version": display_version,
"date": OffsetDateTime::now_utc()
.format(format_description!(
"[year]-[month]-[day]T[hour]:[minute]:[second]Z"
))
.context("format latest.json timestamp")?,
}),
);
let content = serde_json::to_string_pretty(&Value::Object(data))? + "\n";
crate::fs::write_string(&options.output, content)
}
pub fn load_existing(existing: Option<&Path>) -> Result<Map<String, Value>> {
let Some(existing) = existing.filter(|path| path.is_file()) else {
return Ok(Map::new());
};
let content = std::fs::read_to_string(existing)
.with_context(|| format!("read existing latest.json {}", existing.display()))?;
let value: Value = serde_json::from_str(&content)
.with_context(|| format!("parse existing latest.json {}", existing.display()))?;
Ok(value.as_object().cloned().unwrap_or_default())
}