Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge default language data with section language data #2039

Merged
merged 6 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions components/config/src/config/languages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,91 @@ pub struct LanguageOptions {
pub translations: HashMap<String, String>,
}

impl LanguageOptions {
/// Merges self with another LanguageOptions, panicking if 2 equivalent fields are not None,
/// empty or of default value.
pub fn merge(&mut self, other: &LanguageOptions) -> Result<()> {
match &self.title {
None => self.title = other.title.clone(),
Some(self_title) => match &other.title {
Some(other_title) => bail!(
"`title` for default language is specified twice, as {:?} and {:?}.",
self_title,
other_title
),
None => (),
},
};

match &self.description {
None => self.description = other.description.clone(),
Some(self_description) => match &other.description {
Some(other_description) => bail!(
"`description` for default language is specified twice, as {:?} and {:?}.",
self_description,
other_description
),
None => (),
},
};

self.generate_feed = self.generate_feed || other.generate_feed;

match &self.feed_filename == "atom.xml" {
// "atom.xml" is default value.
true => self.feed_filename = other.feed_filename.clone(),
false => match &other.feed_filename.is_empty() {
false => bail!(
"`feed filename` for default language is specifiec twice, as {:?} and {:?}.",
self.feed_filename,
other.feed_filename
),
true => (),
},
};

match &self.taxonomies.is_empty() {
true => self.taxonomies = other.taxonomies.clone(),
false => match &other.taxonomies.is_empty() {
false => bail!(
"`taxonomies` for default language is specifiec twice, as {:?} and {:?}.",
self.taxonomies,
other.taxonomies
),
true => (),
},
};

self.build_search_index = self.build_search_index || other.build_search_index;

match self.search == search::Search::default() {
true => self.search = other.search.clone(),
false => match self.search == other.search {
false => bail!(
"`search` for default language is specified twice, as {:?} and {:?}.",
self.search,
other.search
),
true => (),
},
};

match &self.translations.is_empty() {
true => self.translations = other.translations.clone(),
false => match &other.translations.is_empty() {
false => bail!(
"`translations` for default language is specified twice, as {:?} and {:?}.",
self.translations,
other.translations
),
true => (),
},
};

Ok(())
}
}

/// We want to ensure the language codes are valid ones
pub fn validate_code(code: &str) -> Result<()> {
if LanguageIdentifier::from_bytes(code.as_bytes()).is_err() {
Expand All @@ -38,3 +123,65 @@ pub fn validate_code(code: &str) -> Result<()> {

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn merge_without_conflict() {
let mut base_default_language_options = LanguageOptions {
title: Some("Site's title".to_string()),
description: None,
generate_feed: true,
feed_filename: "atom.xml".to_string(),
taxonomies: vec![],
build_search_index: true,
search: search::Search::default(),
translations: HashMap::new(),
};

let section_default_language_options = LanguageOptions {
title: None,
description: Some("Site's description".to_string()),
generate_feed: false,
feed_filename: "rss.xml".to_string(),
taxonomies: vec![],
build_search_index: true,
search: search::Search::default(),
translations: HashMap::new(),
};

base_default_language_options.merge(&section_default_language_options).unwrap();
}

#[test]
#[should_panic]
fn merge_with_conflict() {
let mut base_default_language_options = LanguageOptions {
title: Some("Site's title".to_string()),
description: Some("Duplicate site description".to_string()),
generate_feed: true,
feed_filename: "".to_string(),
taxonomies: vec![],
build_search_index: true,
search: search::Search::default(),
translations: HashMap::new(),
};

let section_default_language_options = LanguageOptions {
title: None,
description: Some("Site's description".to_string()),
generate_feed: false,
feed_filename: "Some feed_filename".to_string(),
taxonomies: vec![],
build_search_index: true,
search: search::Search::default(),
translations: HashMap::new(),
};

base_default_language_options
.merge(&section_default_language_options)
.expect("This should lead to panic");
}
}
14 changes: 10 additions & 4 deletions components/config/src/config/markup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,11 @@ impl Markdown {
)
}
} else {
bail!("Highlight theme {} not available.\n\
You can load custom themes by configuring `extra_syntaxes_and_themes` to include a list of folders containing '.tmTheme' files", self.highlight_theme)
bail!(
"Highlight theme {} not available.\n\
You can load custom themes by configuring `extra_syntaxes_and_themes` to include a list of folders containing '.tmTheme' files",
self.highlight_theme
)
}
}

Expand All @@ -142,8 +145,11 @@ impl Markdown {
// Check extra themes
if let Some(extra) = &*self.extra_theme_set {
if !extra.themes.contains_key(theme_name) {
bail!("Can't export highlight theme {}, as it does not exist.\n\
Make sure it's spelled correctly, or your custom .tmTheme' is defined properly.", theme_name)
bail!(
"Can't export highlight theme {}, as it does not exist.\n\
Make sure it's spelled correctly, or your custom .tmTheme' is defined properly.",
theme_name
)
}
}
}
Expand Down
115 changes: 97 additions & 18 deletions components/config/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl Config {
languages::validate_code(code)?;
}

config.add_default_language();
config.add_default_language()?;
config.slugify_taxonomies();

if !config.ignored_content.is_empty() {
Expand All @@ -153,7 +153,7 @@ impl Config {

pub fn default_for_test() -> Self {
let mut config = Config::default();
config.add_default_language();
config.add_default_language().unwrap();
config.slugify_taxonomies();
config
}
Expand Down Expand Up @@ -206,26 +206,37 @@ impl Config {
}
}

/// Adds the default language to the list of languages if not present
pub fn add_default_language(&mut self) {
/// Adds the default language to the list of languages if options for it are specified at base level of config.toml.
/// If section for the same language also exists, the options at this section and base are merged and then adds it
/// to list.
pub fn add_default_language(&mut self) -> Result<()> {
// We automatically insert a language option for the default language *if* it isn't present
// TODO: what to do if there is like an empty dict for the lang? merge it or use the language
// TODO: as source of truth?
if !self.languages.contains_key(&self.default_language) {
self.languages.insert(
self.default_language.clone(),
languages::LanguageOptions {
title: self.title.clone(),
description: self.description.clone(),
generate_feed: self.generate_feed,
feed_filename: self.feed_filename.clone(),
build_search_index: self.build_search_index,
taxonomies: self.taxonomies.clone(),
search: self.search.clone(),
translations: self.translations.clone(),
},
);

let mut base_default_language_options = languages::LanguageOptions {
title: self.title.clone(),
description: self.description.clone(),
generate_feed: self.generate_feed,
feed_filename: self.feed_filename.clone(),
build_search_index: self.build_search_index,
taxonomies: self.taxonomies.clone(),
search: self.search.clone(),
translations: self.translations.clone(),
};

if let Some(section_default_language_options) = self.languages.get(&self.default_language) {
if base_default_language_options != languages::LanguageOptions::default() {
println!("Warning: config.toml contains both default language specific information at base and under section `[languages.{}]`, \
which may cause merge conflicts. Please use only one to specify language specific information", self.default_language);
base_default_language_options.merge(section_default_language_options)?;
} else {
return Ok(());
}
}
self.languages.insert(self.default_language.clone(), base_default_language_options);

Ok(())
}

/// Merges the extra data from the theme with the config extra data
Expand Down Expand Up @@ -390,6 +401,74 @@ mod tests {
use super::*;
use utils::slugs::SlugifyStrategy;

#[test]
fn can_add_default_language_with_data_only_at_base_section() {
let title_base = Some("Base section title".to_string());
let description_base = Some("Base section description".to_string());

let mut config = Config::default();
config.title = title_base.clone();
config.description = description_base.clone();
config.add_default_language().unwrap();

let default_language_options =
config.languages.get(&config.default_language).unwrap().clone();
assert_eq!(default_language_options.title, title_base);
assert_eq!(default_language_options.description, description_base);
}

#[test]
fn can_add_default_language_with_data_at_base_and_language_section() {
let title_base = Some("Base section title".to_string());
let description_lang_section = Some("Language section description".to_string());

let mut config = Config::default();
config.title = title_base.clone();
config.languages.insert(
config.default_language.clone(),
languages::LanguageOptions {
title: None,
description: description_lang_section.clone(),
generate_feed: true,
feed_filename: config.feed_filename.clone(),
taxonomies: config.taxonomies.clone(),
build_search_index: false,
search: search::Search::default(),
translations: config.translations.clone(),
},
);
config.add_default_language().unwrap();

let default_language_options =
config.languages.get(&config.default_language).unwrap().clone();
assert_eq!(default_language_options.title, title_base);
assert_eq!(default_language_options.description, description_lang_section);
}

#[test]
fn errors_when_same_field_present_at_base_and_language_section() {
let title_base = Some("Base section title".to_string());
let title_lang_section = Some("Language section title".to_string());

let mut config = Config::default();
config.title = title_base.clone();
config.languages.insert(
config.default_language.clone(),
languages::LanguageOptions {
title: title_lang_section.clone(),
description: None,
generate_feed: true,
feed_filename: config.feed_filename.clone(),
taxonomies: config.taxonomies.clone(),
build_search_index: false,
search: search::Search::default(),
translations: config.translations.clone(),
},
);
let result = config.add_default_language();
assert!(result.is_err());
}

#[test]
fn can_import_valid_config() {
let config = r#"
Expand Down