2024-06-20 08:17:13 +09:00
|
|
|
//! `config migrate` subcommand
|
|
|
|
|
2024-06-20 02:42:33 +09:00
|
|
|
mod v20240701;
|
2024-06-20 02:07:34 +09:00
|
|
|
|
2024-06-21 04:27:03 +09:00
|
|
|
use crate::config::{Revision, CLIENT_CONFIG_PATH, OLD_CONFIG_PATH, SERVER_CONFIG_PATH};
|
|
|
|
use serde::Deserialize;
|
|
|
|
use std::{fs, io::Read, path::Path};
|
2024-06-20 02:07:34 +09:00
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
2024-06-21 03:13:58 +09:00
|
|
|
pub(crate) enum MigrationError {
|
2024-06-21 04:27:03 +09:00
|
|
|
#[error("failed to determine the current config revision ({0})")]
|
|
|
|
UnknownRevision(&'static str),
|
|
|
|
#[error(transparent)]
|
|
|
|
ReadFile(#[from] std::io::Error),
|
|
|
|
#[error(transparent)]
|
|
|
|
InvalidConfig(#[from] toml::de::Error),
|
2024-06-20 03:19:20 +09:00
|
|
|
#[error(transparent)]
|
|
|
|
V20240701(#[from] v20240701::Error),
|
2024-06-20 02:07:34 +09:00
|
|
|
}
|
|
|
|
|
2024-06-21 04:27:03 +09:00
|
|
|
fn next_revision() -> Result<Option<Revision>, MigrationError> {
|
|
|
|
let old_config_exists = Path::new(OLD_CONFIG_PATH).is_file();
|
|
|
|
let server_config_exists = Path::new(SERVER_CONFIG_PATH).is_file();
|
|
|
|
let client_config_exists = Path::new(CLIENT_CONFIG_PATH).is_file();
|
|
|
|
|
|
|
|
if server_config_exists && !client_config_exists {
|
|
|
|
return Err(MigrationError::UnknownRevision(
|
|
|
|
"client config file does not exist",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
if !server_config_exists && client_config_exists {
|
|
|
|
return Err(MigrationError::UnknownRevision(
|
|
|
|
"server config file does not exist",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
if !old_config_exists && !server_config_exists && !client_config_exists {
|
|
|
|
return Err(MigrationError::UnknownRevision(
|
|
|
|
"config file does not exist",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
if old_config_exists && !server_config_exists && !client_config_exists {
|
|
|
|
return Ok(Some(Revision::V20240701));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct Config {
|
|
|
|
config_revision: Revision,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut buffer = String::new();
|
|
|
|
|
|
|
|
let mut server_toml = fs::File::open(SERVER_CONFIG_PATH)?;
|
|
|
|
server_toml.read_to_string(&mut buffer)?;
|
|
|
|
|
|
|
|
let server_config_revision = toml::from_str::<Config>(&buffer)?.config_revision;
|
|
|
|
|
|
|
|
let mut client_toml = fs::File::open(CLIENT_CONFIG_PATH)?;
|
|
|
|
client_toml.read_to_string(&mut buffer)?;
|
|
|
|
|
|
|
|
let client_config_revision = toml::from_str::<Config>(&buffer)?.config_revision;
|
|
|
|
|
|
|
|
if server_config_revision != client_config_revision {
|
|
|
|
return Err(MigrationError::UnknownRevision(
|
|
|
|
"server config revision and client config revision are different",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let next_revision = match server_config_revision {
|
|
|
|
Revision::V20240701 => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(next_revision)
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn update_to_latest() -> Result<(), MigrationError> {
|
|
|
|
if next_revision()?.is_none() {
|
|
|
|
println!("Your config files are already up-to-date! (as of this fishctl release)");
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
while let Some(next_revision) = next_revision()? {
|
|
|
|
run_impl(next_revision).await?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn run_impl(revision: Revision) -> Result<(), MigrationError> {
|
2024-06-21 03:31:13 +09:00
|
|
|
match revision {
|
2024-06-21 03:41:48 +09:00
|
|
|
Revision::V20240701 => v20240701::run().await?,
|
2024-06-20 02:07:34 +09:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-06-21 04:27:03 +09:00
|
|
|
|
|
|
|
pub(super) async fn run(revision: Option<Revision>) -> Result<(), MigrationError> {
|
|
|
|
match revision {
|
|
|
|
Some(revision) => run_impl(revision).await,
|
|
|
|
None => update_to_latest().await,
|
|
|
|
}
|
|
|
|
}
|