124 lines
3.5 KiB
Rust
124 lines
3.5 KiB
Rust
//! `config` subcommand
|
|
|
|
mod update;
|
|
mod validate;
|
|
|
|
use crate::config::{Revision, CLIENT_CONFIG_PATH, OLD_CONFIG_PATH, SERVER_CONFIG_PATH};
|
|
use clap::Subcommand;
|
|
use serde::Deserialize;
|
|
use std::{
|
|
fs,
|
|
io::{self, Read},
|
|
path::Path,
|
|
};
|
|
|
|
#[derive(Subcommand)]
|
|
pub(crate) enum Commands {
|
|
/// Convert old config files to the new format
|
|
Update { revision: Option<Revision> },
|
|
/// Validate the config files
|
|
Validate,
|
|
}
|
|
|
|
/// Errors that can happen in `config` subcommand
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub(crate) enum ConfigError {
|
|
#[error(transparent)]
|
|
Update(#[from] update::UpdateError),
|
|
#[error(transparent)]
|
|
Validate(#[from] validate::ValidationError),
|
|
}
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub(crate) enum RevisionCheckError {
|
|
#[error("failed to determine the current config revision ({0})")]
|
|
UnknownRevision(&'static str),
|
|
#[error(transparent)]
|
|
ReadFile(#[from] ReadError),
|
|
}
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub(crate) enum ReadError {
|
|
#[error(transparent)]
|
|
ReadFile(#[from] io::Error),
|
|
#[error("the config file is not written in the correct format")]
|
|
InvalidFormat(#[from] toml::de::Error),
|
|
}
|
|
|
|
fn read_file_string(path: &str) -> Result<String, ReadError> {
|
|
let mut file = fs::File::open(path)?;
|
|
let mut result = String::new();
|
|
file.read_to_string(&mut result)?;
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
fn read_server_config<T>() -> Result<T, ReadError>
|
|
where
|
|
T: serde::de::DeserializeOwned,
|
|
{
|
|
toml::from_str(&read_file_string(SERVER_CONFIG_PATH)?).map_err(ReadError::InvalidFormat)
|
|
}
|
|
|
|
fn read_client_config<T>() -> Result<T, ReadError>
|
|
where
|
|
T: serde::de::DeserializeOwned,
|
|
{
|
|
toml::from_str(&read_file_string(CLIENT_CONFIG_PATH)?).map_err(ReadError::InvalidFormat)
|
|
}
|
|
|
|
fn current_revision() -> Result<Revision, RevisionCheckError> {
|
|
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(RevisionCheckError::UnknownRevision(
|
|
"client config file does not exist",
|
|
));
|
|
}
|
|
if !server_config_exists && client_config_exists {
|
|
return Err(RevisionCheckError::UnknownRevision(
|
|
"server config file does not exist",
|
|
));
|
|
}
|
|
if !old_config_exists && !server_config_exists && !client_config_exists {
|
|
return Err(RevisionCheckError::UnknownRevision(
|
|
"config file does not exist",
|
|
));
|
|
}
|
|
|
|
if old_config_exists && !server_config_exists && !client_config_exists {
|
|
return Ok(Revision::V0);
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct Config {
|
|
config_revision: Revision,
|
|
}
|
|
|
|
let server_config_revision = read_server_config::<Config>()?.config_revision;
|
|
let client_config_revision = read_server_config::<Config>()?.config_revision;
|
|
|
|
if server_config_revision != client_config_revision {
|
|
return Err(RevisionCheckError::UnknownRevision(
|
|
"server config revision and client config revision are different",
|
|
));
|
|
}
|
|
|
|
match server_config_revision {
|
|
Revision::V0 => Err(RevisionCheckError::UnknownRevision(
|
|
"revision 0 does not exist",
|
|
)),
|
|
_ => Ok(server_config_revision),
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn run(command: Commands) -> Result<(), ConfigError> {
|
|
match command {
|
|
Commands::Update { revision } => update::run(revision).await?,
|
|
Commands::Validate => validate::run()?,
|
|
}
|
|
|
|
Ok(())
|
|
}
|