diff --git a/Cargo.lock b/Cargo.lock index cfe07ae..b5fc15b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,26 @@ dependencies = [ "serde", ] +[[package]] +name = "enum-iterator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -466,7 +486,9 @@ dependencies = [ "chrono", "clap", "color-print", + "enum-iterator", "serde", + "serde_repr", "sqlx", "thiserror", "tokio", @@ -1218,6 +1240,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "serde_spanned" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 58d5f9f..87e922b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,10 @@ path = "src/main.rs" chrono = "0.4" clap = { version = "4.5", features = ["derive"] } color-print = "0.3" +enum-iterator = "2.1" thiserror = "1.0" serde = { version = "1.0", features = ["derive"] } +serde_repr = "0.1" sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] } tokio = { version = "1.38", features = ["full"] } toml = "0.8" diff --git a/src/command/config.rs b/src/command/config.rs index 0c9974a..36f9088 100644 --- a/src/command/config.rs +++ b/src/command/config.rs @@ -35,7 +35,7 @@ pub(crate) enum RevisionCheckError { ReadFile(#[from] std::io::Error), } -fn next_revision() -> Result, RevisionCheckError> { +fn current_revision() -> Result { 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(); @@ -57,7 +57,7 @@ fn next_revision() -> Result, RevisionCheckError> { } if old_config_exists && !server_config_exists && !client_config_exists { - return Ok(Some(Revision::V1)); + return Ok(Revision::V0); } #[derive(Deserialize)] @@ -83,11 +83,7 @@ fn next_revision() -> Result, RevisionCheckError> { )); } - let next_revision = match server_config_revision { - Revision::V1 => None, - }; - - Ok(next_revision) + Ok(server_config_revision) } pub(crate) async fn run(command: Commands) -> Result<(), ConfigError> { diff --git a/src/command/config/update.rs b/src/command/config/update.rs index 802d65e..2a08f3c 100644 --- a/src/command/config/update.rs +++ b/src/command/config/update.rs @@ -3,11 +3,12 @@ mod v1; use crate::{ - command::config::{next_revision, RevisionCheckError}, + 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 @@ -41,34 +42,36 @@ fn take_backup() -> Result<(), UpdateError> { Ok(()) } -async fn update_to_latest() -> Result<(), UpdateError> { - while let Some(next_revision) = next_revision()? { - run_impl(next_revision).await?; +async fn update_to_latest(mut revision: Revision) -> Result<(), UpdateError> { + while let Some(next_revision) = revision.next() { + run_impl(&next_revision).await?; + revision = next_revision.clone(); } Ok(()) } /// Updates config files to the specified revision. -async fn run_impl(revision: Revision) -> Result<(), UpdateError> { +async fn run_impl(revision: &Revision) -> Result<(), UpdateError> { match revision { + Revision::V0 => unreachable!(), Revision::V1 => v1::run().await?, } Ok(()) } pub(super) async fn run(revision: Option) -> Result<(), UpdateError> { - let next_revision = next_revision()?; + let current_revision = current_revision()?; - if next_revision.is_none() { + if current_revision == Revision::last().unwrap() { println!("Your config files are already up-to-date! (as of this fishctl release)"); return Ok(()); } - if next_revision.unwrap() != Revision::V1 { + if current_revision != Revision::V0 { take_backup()?; } match revision { - Some(revision) => run_impl(revision).await, - None => update_to_latest().await, + Some(revision) => run_impl(&revision).await, + None => update_to_latest(current_revision).await, } } diff --git a/src/command/config/validate.rs b/src/command/config/validate.rs index 5336bec..e6dd20a 100644 --- a/src/command/config/validate.rs +++ b/src/command/config/validate.rs @@ -1,10 +1,11 @@ //! `config validate` subcommand use crate::{ - command::config::{next_revision, RevisionCheckError}, - config::{client, server, CLIENT_CONFIG_PATH, SERVER_CONFIG_PATH}, + command::config::{current_revision, RevisionCheckError}, + config::{client, server, Revision, CLIENT_CONFIG_PATH, SERVER_CONFIG_PATH}, }; use color_print::cprintln; +use enum_iterator::Sequence; use std::{fs, io::Read}; use validator::Validate; @@ -30,7 +31,7 @@ enum ReadError { } pub(super) fn run() -> Result<(), ValidationError> { - if next_revision()?.is_some() { + if current_revision()? != Revision::last().unwrap() { cprintln!("Please first run `fishctl config update` to update your config files."); return Err(ValidationError::OutOfDate); } diff --git a/src/config/mod.rs b/src/config/mod.rs index 5fd590c..6debd2d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,15 +2,20 @@ pub mod client; pub mod server; use clap::ValueEnum; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, PartialEq, Clone, ValueEnum, Debug)] -#[clap(rename_all = "lowercase")] -pub enum Revision { - #[serde(rename = "1")] - V1, -} +use enum_iterator::Sequence; +use serde_repr::{Deserialize_repr, Serialize_repr}; pub const SERVER_CONFIG_PATH: &str = "config/server.toml"; pub const CLIENT_CONFIG_PATH: &str = "config/client.toml"; pub const OLD_CONFIG_PATH: &str = ".config/default.yml"; + +#[derive(Deserialize_repr, Serialize_repr, PartialEq, Clone, ValueEnum, Sequence, Debug)] +#[clap(rename_all = "lowercase")] +#[repr(u8)] +pub enum Revision { + #[clap(skip)] + /// Misskey-style config (`.config/default.yml`) + V0 = 0, + /// The first revision number for `config/{server,client}.toml` + V1, +}