feat: versioning of configuration files
This commit is contained in:
parent
ce4706c4b1
commit
8c12dfc132
64
src/main.rs
64
src/main.rs
|
@ -145,6 +145,12 @@ mod conf {
|
||||||
/// example). See the definition of each of the member structs for more
|
/// example). See the definition of each of the member structs for more
|
||||||
/// information.
|
/// information.
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// The "version" of the configuration, corresponding to the MAJOR in
|
||||||
|
/// semantic versioning. Should be increased every time the
|
||||||
|
/// configuration structure suffers breaking changes.
|
||||||
|
/// This value is optional because sufficiently old configuration files
|
||||||
|
/// may not have a version field.
|
||||||
|
pub version: Option<usize>,
|
||||||
/// Configuration regarding the Redis database.
|
/// Configuration regarding the Redis database.
|
||||||
pub db: DbConfig,
|
pub db: DbConfig,
|
||||||
/// Configuration regarding the types of (URL shorten) slugs produced.
|
/// Configuration regarding the types of (URL shorten) slugs produced.
|
||||||
|
@ -153,6 +159,32 @@ mod conf {
|
||||||
pub serve_rules: ServeRules,
|
pub serve_rules: ServeRules,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the configuration version field that this version of lonk expects.
|
||||||
|
pub fn config_version() -> usize {
|
||||||
|
usize::from_str(env!("CARGO_PKG_VERSION_MAJOR")).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ConfigParseError {
|
||||||
|
SerdeError(serde_json::error::Error),
|
||||||
|
OldVersion(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn from_sync_buffer<R: std::io::Read>(
|
||||||
|
buffer: std::io::BufReader<R>,
|
||||||
|
) -> Result<Self, ConfigParseError> {
|
||||||
|
let parsed: Config =
|
||||||
|
serde_json::from_reader(buffer).map_err(|err| ConfigParseError::SerdeError(err))?;
|
||||||
|
|
||||||
|
let parsed_version = parsed.version.unwrap_or(0);
|
||||||
|
if parsed_version != config_version() {
|
||||||
|
return Err(ConfigParseError::OldVersion(parsed_version));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default implementations
|
// Default implementations
|
||||||
|
|
||||||
impl Default for DbConfig {
|
impl Default for DbConfig {
|
||||||
|
@ -210,6 +242,7 @@ mod conf {
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
version: Some(config_version()),
|
||||||
db: Default::default(),
|
db: Default::default(),
|
||||||
slug_rules: Default::default(),
|
slug_rules: Default::default(),
|
||||||
serve_rules: Default::default(),
|
serve_rules: Default::default(),
|
||||||
|
@ -824,9 +857,17 @@ async fn serve() {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
let config_buf = std::io::BufReader::new(config_file);
|
let parse_result = tokio::task::spawn_blocking(move || {
|
||||||
serde_json::from_reader(config_buf).unwrap_or_else(|err| match err.classify() {
|
conf::Config::from_sync_buffer(std::io::BufReader::new(config_file))
|
||||||
serde_json::error::Category::Io => panic!("IO error when reading configuration file."),
|
})
|
||||||
|
.await
|
||||||
|
.expect("Tokio error from blocking task.");
|
||||||
|
|
||||||
|
match parse_result {
|
||||||
|
Err(conf::ConfigParseError::SerdeError(err)) => match err.classify() {
|
||||||
|
serde_json::error::Category::Io => {
|
||||||
|
panic!("IO error when reading configuration file.")
|
||||||
|
}
|
||||||
serde_json::error::Category::Syntax => panic!(
|
serde_json::error::Category::Syntax => panic!(
|
||||||
"Configuration file is syntactically incorrect.
|
"Configuration file is syntactically incorrect.
|
||||||
See {}:line {}, column {}.",
|
See {}:line {}, column {}.",
|
||||||
|
@ -844,7 +885,17 @@ async fn serve() {
|
||||||
serde_json::error::Category::Eof => {
|
serde_json::error::Category::Eof => {
|
||||||
panic!("Unexpected end of file when reading configuration file.")
|
panic!("Unexpected end of file when reading configuration file.")
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
Err(conf::ConfigParseError::OldVersion(old_version)) => {
|
||||||
|
panic!(
|
||||||
|
"Configuration file has outdated version.
|
||||||
|
Expected version field to be {} but got {}.",
|
||||||
|
old_version,
|
||||||
|
conf::config_version()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(config) => config,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create slug factory
|
// Create slug factory
|
||||||
|
@ -934,7 +985,8 @@ fn main() {
|
||||||
serde_json::to_string_pretty(&conf::Config::default())
|
serde_json::to_string_pretty(&conf::Config::default())
|
||||||
.expect("Default configuration should always be JSON serializable")
|
.expect("Default configuration should always be JSON serializable")
|
||||||
);
|
);
|
||||||
} else {
|
std::process::exit(0);
|
||||||
serve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serve();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue