From fae17a5abd95f6cb22a400298b0327efae122202 Mon Sep 17 00:00:00 2001 From: LunarEclipse Date: Thu, 30 May 2024 19:05:01 +0200 Subject: [PATCH] Made the multiple feeds config changes "loudly" backwards-incompatible --- CHANGELOG.md | 1 + components/config/src/config/languages.rs | 5 +- components/config/src/config/mod.rs | 56 ++----------------- components/config/src/lib.rs | 28 +++++++++- .../content/src/front_matter/section.rs | 14 ++++- 5 files changed, 47 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d29cacc4e0..8fd817fd50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Added support for generating multiple kinds of feeds at once - Changed config options named `generate_feed` to `generate_feeds` (both in config.toml and in section front-matter) - Changed config option `feed_filename: String` to `feed_filenames: Vec` +- The config file no longer allows arbitrary fields outside the `[extra]` section (front-matter is unaffected) ## 0.18.0 (2023-12-18) diff --git a/components/config/src/config/languages.rs b/components/config/src/config/languages.rs index 1d7f3816da..eca7bc9514 100644 --- a/components/config/src/config/languages.rs +++ b/components/config/src/config/languages.rs @@ -5,22 +5,19 @@ use libs::unic_langid::LanguageIdentifier; use serde::{Deserialize, Serialize}; use crate::config::search; -use crate::config::single_or_vec; use crate::config::taxonomies; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default)] +#[serde(default, deny_unknown_fields)] pub struct LanguageOptions { /// Title of the site. Defaults to None pub title: Option, /// Description of the site. Defaults to None pub description: Option, /// Whether to generate feeds for that language, defaults to `false` - #[serde(alias = "generate_feed")] pub generate_feeds: bool, /// The filenames to use for feeds. Used to find the templates, too. /// Defaults to ["atom.xml"], with "rss.xml" also having a template provided out of the box. - #[serde(alias = "feed_filename", deserialize_with = "single_or_vec")] pub feed_filenames: Vec, pub taxonomies: Vec, /// Whether to generate search index for that language, defaults to `false` diff --git a/components/config/src/config/mod.rs b/components/config/src/config/mod.rs index bb9bb5bb71..186d884b36 100644 --- a/components/config/src/config/mod.rs +++ b/components/config/src/config/mod.rs @@ -10,8 +10,7 @@ use std::path::{Path, PathBuf}; use libs::globset::GlobSet; use libs::toml::Value as Toml; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use crate::theme::Theme; use errors::{anyhow, bail, Result}; @@ -31,7 +30,7 @@ pub enum Mode { } #[derive(Clone, Debug, Deserialize)] -#[serde(default)] +#[serde(default, deny_unknown_fields)] pub struct Config { /// Base URL of the site, the only required config argument pub base_url: String, @@ -51,13 +50,11 @@ pub struct Config { translations: HashMap, /// Whether to generate feeds. Defaults to false. - #[serde(alias = "generate_feed")] pub generate_feeds: bool, /// The number of articles to include in the feed. Defaults to including all items. pub feed_limit: Option, /// The filenames to use for feeds. Used to find the templates, too. /// Defaults to ["atom.xml"], with "rss.xml" also having a template provided out of the box. - #[serde(alias = "feed_filename", deserialize_with = "single_or_vec")] pub feed_filenames: Vec, /// If set, files from static/ will be hardlinked instead of copied to the output dir. pub hard_link_static: bool, @@ -400,48 +397,6 @@ impl Default for Config { } } -/// Used for deserializing values that can be either a single value or a vec of values -pub(crate) fn single_or_vec<'de, T, D>(deserializer: D) -> Result, D::Error> -where - T: DeserializeOwned, - D: Deserializer<'de>, -{ - let v = SingleOrVec::deserialize(deserializer)?; - Ok(v.into()) -} - -#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] -#[serde(untagged)] -pub(crate) enum SingleOrVec { - Multiple(Vec), - One(T), - None, -} - -impl From> for Vec { - fn from(x: SingleOrVec) -> Vec { - use SingleOrVec::*; - - match x { - Multiple(v) => v, - One(v) => vec![v], - None => vec![], - } - } -} - -impl From> for SingleOrVec { - fn from(value: Vec) -> Self { - Self::Multiple(value) - } -} - -impl Default for SingleOrVec { - fn default() -> Self { - Self::None - } -} - #[cfg(test)] mod tests { use super::*; @@ -1025,15 +980,14 @@ author = "person@example.com (Some Person)" } #[test] - fn test_backwards_compatibility_for_feeds() { + #[should_panic] + fn test_backwards_incompatibility_for_feeds() { let config = r#" base_url = "example.com" generate_feed = true feed_filename = "test.xml" "#; - let config = Config::parse(config).unwrap(); - assert_eq!(config.generate_feeds, true); - assert_eq!(config.feed_filenames, vec!["test.xml".to_owned()]); + Config::parse(config).unwrap(); } } diff --git a/components/config/src/lib.rs b/components/config/src/lib.rs index 05d621615c..bbc4b93255 100644 --- a/components/config/src/lib.rs +++ b/components/config/src/lib.rs @@ -2,7 +2,7 @@ mod config; pub mod highlighting; mod theme; -use std::path::Path; +use std::{marker::PhantomData, path::Path}; pub use crate::config::{ languages::LanguageOptions, @@ -14,9 +14,35 @@ pub use crate::config::{ Config, }; use errors::Result; +use serde::Deserialize; /// Get and parse the config. /// If it doesn't succeed, exit pub fn get_config(filename: &Path) -> Result { Config::from_file(filename) } + +/// This is used to print an error message for deprecated fields +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Deprecated { + _type: PhantomData, +} + +impl Deprecated { + pub fn new() -> Self { + Self { _type: PhantomData } + } +} + +pub trait DeprecationReason { + const REASON: &'static str; +} + +impl<'de, T: DeprecationReason> Deserialize<'de> for Deprecated { + fn deserialize(_deserializer: D) -> std::prelude::v1::Result + where + D: serde::Deserializer<'de>, + { + Err(serde::de::Error::custom(format!("Failed to parse a deprecated option: {}", T::REASON))) + } +} diff --git a/components/content/src/front_matter/section.rs b/components/content/src/front_matter/section.rs index 43b579d511..d8456fad67 100644 --- a/components/content/src/front_matter/section.rs +++ b/components/content/src/front_matter/section.rs @@ -1,6 +1,7 @@ use libs::tera::{Map, Value}; use serde::{Deserialize, Serialize}; +use config::{Deprecated, DeprecationReason}; use errors::Result; use utils::de::fix_toml_dates; use utils::types::InsertAnchor; @@ -67,13 +68,23 @@ pub struct SectionFrontMatter { /// redirect to this #[serde(skip_serializing)] pub aliases: Vec, + /// Deprecated + #[serde(skip_serializing)] + pub generate_feed: Deprecated, /// Whether to generate a feed for the current section - #[serde(skip_serializing, alias = "generate_feed")] + #[serde(skip_serializing)] pub generate_feeds: bool, /// Any extra parameter present in the front matter pub extra: Map, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct DeprecatedGenerateFeed {} + +impl DeprecationReason for DeprecatedGenerateFeed { + const REASON: &'static str = "generate_feed is deprecated, please use generate_feeds instead"; +} + impl SectionFrontMatter { pub fn parse(raw: &RawFrontMatter) -> Result { let mut f: SectionFrontMatter = raw.deserialize()?; @@ -113,6 +124,7 @@ impl Default for SectionFrontMatter { transparent: false, page_template: None, aliases: Vec::new(), + generate_feed: Deprecated::new(), generate_feeds: false, extra: Map::new(), draft: false,