//! `config update` subcommand mod v1; use crate::{ command::config::{current_revision, RevisionCheckError}, config::{Revision, CLIENT_CONFIG_PATH, SERVER_CONFIG_PATH}, }; use chrono::Local; use color_print::cprintln; use enum_iterator::Sequence; use std::{fs, io}; /// Errors that happen in `config update` subcommand #[derive(thiserror::Error, Debug)] pub(crate) enum UpdateError { #[error(transparent)] RevisionCheck(#[from] RevisionCheckError), #[error(transparent)] FileOperation(#[from] io::Error), #[error("downgrading is not supported")] Downgrade, #[error("failed to update the config to revision 1")] V1(#[from] v1::Error), } pub(super) async fn run(revision: Option<Revision>) -> Result<(), UpdateError> { let current = current_revision()?; if current.next().is_none() { println!("Your config files are already up-to-date! (as of this fishctl release)"); return Ok(()); } if current != Revision::V0 { take_backup()?; } match revision { Some(target) => update(current, target).await, None => update(current, Revision::last().unwrap()).await, } } fn take_backup() -> Result<(), UpdateError> { let current_time = Local::now().format("%Y%m%d%H%M%S").to_string(); let server_backup_filename = format!("{}-backup-{}", SERVER_CONFIG_PATH, current_time); let client_backup_filename = format!("{}-backup-{}", CLIENT_CONFIG_PATH, current_time); cprintln!( "Saving server config backup as <bold>{}</>...", server_backup_filename ); fs::copy(SERVER_CONFIG_PATH, server_backup_filename)?; cprintln!( "Saving client config backup as <bold>{}</>...", client_backup_filename ); fs::copy(CLIENT_CONFIG_PATH, client_backup_filename)?; Ok(()) } /// Updates config files to the specified revision. async fn update(mut current: Revision, target: Revision) -> Result<(), UpdateError> { if current > target { return Err(UpdateError::Downgrade); } while current < target { let next = current.next().unwrap(); update_one_revision(next.clone()).await?; current = next; } Ok(()) } /// Updates config file revision from `target - 1` to `target`. async fn update_one_revision(target: Revision) -> Result<(), UpdateError> { println!( "Updating the config to revision {}...", target.clone() as u8 ); match target { Revision::V0 => unreachable!(), Revision::V1 => v1::run().await?, } Ok(()) }