//! `config validate` subcommand use crate::{ command::config::{current_revision, RevisionCheckError}, config::{client, server, CLIENT_CONFIG_PATH, SERVER_CONFIG_PATH}, }; use color_print::cprintln; use enum_iterator::Sequence; use std::{fs, io::Read}; use validator::Validate; /// Errors that can happen in `config validate` subcommand #[derive(thiserror::Error, Debug)] pub(crate) enum ValidationError { #[error(transparent)] ReadFile(#[from] std::io::Error), #[error(transparent)] UnknownRevision(#[from] RevisionCheckError), #[error("config files are not the latest revision")] OutOfDate, #[error("invalid config file")] InvalidConfig, } #[derive(thiserror::Error, Debug)] enum ReadError { #[error(transparent)] ReadFile(#[from] std::io::Error), #[error("the config file is not written in the correct format")] InvalidFormat(#[from] toml::de::Error), } pub(super) fn run() -> Result<(), ValidationError> { if current_revision()?.next().is_some() { cprintln!("Please first run `<bold>fishctl config update</>` to update your config files."); return Err(ValidationError::OutOfDate); } let server_validation_result = match read_server_toml() { Ok(config) => config.validate().map_err(|err| { cprintln!("<r!><bold>config/server.toml is invalid.</></>\n{}", err); ValidationError::InvalidConfig }), Err(ReadError::InvalidFormat(err)) => { cprintln!("<r!><bold>config/server.toml is invalid.</></>\n{}", err); Err(ValidationError::InvalidConfig) } Err(ReadError::ReadFile(err)) => Err(ValidationError::ReadFile(err)), }; let client_validation_result = match read_client_toml() { Ok(config) => config.validate().map_err(|err| { cprintln!( "<r!><bold>{} is invalid.</></>\n{}", CLIENT_CONFIG_PATH, err ); ValidationError::InvalidConfig }), Err(ReadError::InvalidFormat(err)) => { cprintln!( "<r!><bold>{} is invalid.</></>\n{}", CLIENT_CONFIG_PATH, err ); Err(ValidationError::InvalidConfig) } Err(ReadError::ReadFile(err)) => Err(ValidationError::ReadFile(err)), }; if server_validation_result.is_ok() && client_validation_result.is_ok() { cprintln!( "<g!><bold>{} and {} are valid!</></>", SERVER_CONFIG_PATH, CLIENT_CONFIG_PATH ); cprintln!("<bold>Note:</> This command only checks the format of the config files, and its result does not guarantee the correctness of the value."); } server_validation_result.and(client_validation_result) } fn read_server_toml() -> Result<server::Config, ReadError> { let mut file = fs::File::open(SERVER_CONFIG_PATH)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; toml::from_str(&buffer).map_err(ReadError::InvalidFormat) } fn read_client_toml() -> Result<client::Config, ReadError> { let mut file = fs::File::open(CLIENT_CONFIG_PATH)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; toml::from_str(&buffer).map_err(ReadError::InvalidFormat) }