From 53e7059613863e05dbcf71327dad75ffa1a95513 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 5 Jan 2024 11:59:05 +0400 Subject: Ignore null values in `alacritty migrate` This should help with broken YAML configurations by throwing nulls away, which are not representable in toml. --- CHANGELOG.md | 4 +++ alacritty/src/config/mod.rs | 75 +++++++++++++++++++++++++++++++++++++++++++-- alacritty/src/migrate.rs | 2 +- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b1fa37a..2cb10ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Support for pasting in Vi + Search mode +### Changed + +- `alacritty migrate` will ignore null values in yaml instead of erroring out + ### Fixed - `alacritty migrate` failing with nonexistent imports diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs index 95a1ff00..a77ed770 100644 --- a/alacritty/src/config/mod.rs +++ b/alacritty/src/config/mod.rs @@ -207,7 +207,7 @@ fn parse_config( config_paths.push(path.to_owned()); // Deserialize the configuration file. - let config = deserialize_config(path)?; + let config = deserialize_config(path, false)?; // Merge config with imports. let imports = load_imports(&config, config_paths, recursion_limit); @@ -215,7 +215,7 @@ fn parse_config( } /// Deserialize a configuration file. -pub fn deserialize_config(path: &Path) -> Result { +pub fn deserialize_config(path: &Path, warn_pruned: bool) -> Result { let mut contents = fs::read_to_string(path)?; // Remove UTF-8 BOM. @@ -230,7 +230,8 @@ pub fn deserialize_config(path: &Path) -> Result { "YAML config {path:?} is deprecated, please migrate to TOML using `alacritty migrate`" ); - let value: serde_yaml::Value = serde_yaml::from_str(&contents)?; + let mut value: serde_yaml::Value = serde_yaml::from_str(&contents)?; + prune_yaml_nulls(&mut value, warn_pruned); contents = toml::to_string(&value)?; } @@ -318,6 +319,35 @@ pub fn imports( Ok(import_paths) } +/// Prune the nulls from the YAML to ensure TOML compatibility. +fn prune_yaml_nulls(value: &mut serde_yaml::Value, warn_pruned: bool) { + fn walk(value: &mut serde_yaml::Value, warn_pruned: bool) -> bool { + match value { + serde_yaml::Value::Sequence(sequence) => { + sequence.retain_mut(|value| !walk(value, warn_pruned)); + sequence.is_empty() + }, + serde_yaml::Value::Mapping(mapping) => { + mapping.retain(|key, value| { + let retain = !walk(value, warn_pruned); + if let Some(key_name) = key.as_str().filter(|_| !retain && warn_pruned) { + eprintln!("Removing null key \"{key_name}\" from the end config"); + } + retain + }); + mapping.is_empty() + }, + serde_yaml::Value::Null => true, + _ => false, + } + } + + if walk(value, warn_pruned) { + // When the value itself is null return the mapping. + *value = serde_yaml::Value::Mapping(Default::default()); + } +} + /// Get the location of the first found default config file paths /// according to the following order: /// @@ -370,4 +400,43 @@ mod tests { fn empty_config() { toml::from_str::("").unwrap(); } + + fn yaml_to_toml(contents: &str) -> String { + let mut value: serde_yaml::Value = serde_yaml::from_str(contents).unwrap(); + prune_yaml_nulls(&mut value, false); + toml::to_string(&value).unwrap() + } + + #[test] + fn yaml_with_nulls() { + let contents = r#" + window: + blinking: Always + cursor: + not_blinking: Always + some_array: + - { window: } + - { window: "Hello" } + + "#; + let toml = yaml_to_toml(contents); + assert_eq!( + toml.trim(), + r#"[window] +blinking = "Always" +not_blinking = "Always" + +[[window.some_array]] +window = "Hello""# + ); + } + + #[test] + fn empty_yaml_to_toml() { + let contents = r#" + + "#; + let toml = yaml_to_toml(contents); + assert!(toml.is_empty()); + } } diff --git a/alacritty/src/migrate.rs b/alacritty/src/migrate.rs index 5f47b083..dbcfb2ae 100644 --- a/alacritty/src/migrate.rs +++ b/alacritty/src/migrate.rs @@ -74,7 +74,7 @@ fn migrate_config( } // Try to parse the configuration file. - let mut config = match config::deserialize_config(path) { + let mut config = match config::deserialize_config(path, !options.dry_run) { Ok(config) => config, Err(err) => return Err(format!("parsing error: {err}")), }; -- cgit v1.2.3-54-g00ecf