diff --git a/src/metadata/file.rs b/src/metadata/file.rs index 13693736..96fdc121 100644 --- a/src/metadata/file.rs +++ b/src/metadata/file.rs @@ -7,11 +7,11 @@ use super::{ MessageContent, emit_message_contents, parse_message_contents_yaml, validate_message_contents, }, + yaml::{EmitYaml, YamlEmitter}, yaml::{ YamlObjectType, as_string_node, get_required_string_value, get_string_value, parse_condition, }, - yaml_emit::{EmitYaml, YamlEmitter}, }; /// Represents a file in a game's Data folder, including files in diff --git a/src/metadata/group.rs b/src/metadata/group.rs index f976e71d..4779fa37 100644 --- a/src/metadata/group.rs +++ b/src/metadata/group.rs @@ -1,10 +1,10 @@ use saphyr::MarkedYaml; use super::error::ParseMetadataError; +use super::yaml::{EmitYaml, YamlEmitter}; use super::yaml::{ YamlObjectType, get_as_hash, get_required_string_value, get_string_value, get_strings_vec_value, }; -use super::yaml_emit::{EmitYaml, YamlEmitter}; /// Represents a group to which plugin metadata objects can belong. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -104,10 +104,6 @@ impl TryFrom<&MarkedYaml> for Group { } impl EmitYaml for Group { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { emitter.begin_map(); diff --git a/src/metadata/location.rs b/src/metadata/location.rs index ae7eb639..33d56886 100644 --- a/src/metadata/location.rs +++ b/src/metadata/location.rs @@ -3,9 +3,9 @@ use saphyr::{MarkedYaml, YamlData}; use super::error::ExpectedType; use super::error::ParseMetadataError; +use super::yaml::EmitYaml; +use super::yaml::YamlEmitter; use super::yaml::{YamlObjectType, get_required_string_value}; -use super::yaml_emit::EmitYaml; -use super::yaml_emit::YamlEmitter; /// Represents a URL at which the parent plugin can be found. #[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] diff --git a/src/metadata/message.rs b/src/metadata/message.rs index b146107d..1b463495 100644 --- a/src/metadata/message.rs +++ b/src/metadata/message.rs @@ -10,11 +10,11 @@ use super::{ ExpectedType, MetadataParsingErrorReason, MultilingualMessageContentsError, ParseMetadataError, }, + yaml::{EmitYaml, YamlEmitter}, yaml::{ YamlObjectType, as_string_node, get_as_hash, get_required_string_value, get_strings_vec_value, parse_condition, }, - yaml_emit::{EmitYaml, YamlEmitter}, }; /// Codes used to indicate the type of a message. @@ -388,10 +388,6 @@ impl TryFrom<&MarkedYaml> for Message { } impl EmitYaml for MessageContent { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { emitter.begin_map(); @@ -425,10 +421,6 @@ pub(super) fn emit_message_contents( } impl EmitYaml for Message { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { emitter.begin_map(); diff --git a/src/metadata/metadata_document.rs b/src/metadata/metadata_document.rs index dde340f0..861cb653 100644 --- a/src/metadata/metadata_document.rs +++ b/src/metadata/metadata_document.rs @@ -1,7 +1,6 @@ use std::{ collections::{HashMap, HashSet}, path::Path, - sync::LazyLock, }; use saphyr::{MarkedYaml, YamlData}; @@ -11,18 +10,15 @@ use crate::logging; use super::{ error::{ ExpectedType, LoadMetadataError, MetadataDocumentParsingError, ParseMetadataError, - RegexError, WriteMetadataError, YamlMergeKeyError, + RegexError, WriteMetadataError, }, file::Filename, group::Group, message::Message, plugin_metadata::PluginMetadata, - yaml::{YamlObjectType, as_string_node, get_as_slice}, - yaml_emit::{EmitYaml, YamlEmitter}, + yaml::{EmitYaml, YamlEmitter, YamlObjectType, get_as_slice, process_merge_keys}, }; -static MERGE_KEY: LazyLock = LazyLock::new(|| as_string_node("<<")); - #[derive(Clone, Debug, Eq, PartialEq)] pub struct MetadataDocument { bash_tags: Vec, @@ -342,71 +338,6 @@ impl std::default::Default for MetadataDocument { } } -fn process_merge_keys(mut yaml: MarkedYaml) -> Result { - match yaml.data { - YamlData::Array(a) => { - yaml.data = merge_array_elements(a).map(YamlData::Array)?; - Ok(yaml) - } - YamlData::Hash(h) => { - yaml.data = merge_hash_keys(h).map(YamlData::Hash)?; - Ok(yaml) - } - _ => Ok(yaml), - } -} - -fn merge_array_elements( - array: saphyr::AnnotatedArray, -) -> Result, YamlMergeKeyError> { - array.into_iter().map(process_merge_keys).collect() -} - -fn merge_hash_keys( - hash: saphyr::AnnotatedHash, -) -> Result, YamlMergeKeyError> { - let mut hash: saphyr::AnnotatedHash = hash - .into_iter() - .map(|(key, value)| { - process_merge_keys(key) - .and_then(|key| process_merge_keys(value).map(|value| (key, value))) - }) - .collect::>()?; - - if let Some(value) = hash.remove(&MERGE_KEY) { - merge_into_hash(hash, value) - } else { - Ok(hash) - } -} - -fn merge_into_hash( - hash: saphyr::AnnotatedHash, - value: MarkedYaml, -) -> Result, YamlMergeKeyError> { - match value.data { - YamlData::::Array(a) => a.into_iter().try_fold(hash, |acc, e| { - if let YamlData::Hash(h) = e.data { - Ok(merge_hashes(acc, h)) - } else { - Err(YamlMergeKeyError::new(e)) - } - }), - YamlData::::Hash(h) => Ok(merge_hashes(hash, h)), - _ => Err(YamlMergeKeyError::new(value)), - } -} - -fn merge_hashes( - mut hash1: saphyr::AnnotatedHash, - hash2: saphyr::AnnotatedHash, -) -> saphyr::AnnotatedHash { - for (key, value) in hash2 { - hash1.entry(key).or_insert(value); - } - hash1 -} - fn replace_prelude(masterlist: String, prelude: String) -> String { let line_ending = detect_line_ending(&masterlist); if let Some((start, end)) = find_prelude_bounds(&masterlist) { diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index d430da2c..0bf39d25 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -9,7 +9,6 @@ mod plugin_cleaning_data; pub(crate) mod plugin_metadata; mod tag; mod yaml; -mod yaml_emit; pub use file::{File, Filename}; pub use group::Group; @@ -20,8 +19,8 @@ pub use plugin_metadata::PluginMetadata; pub use tag::{Tag, TagSuggestion}; #[cfg(test)] -fn emit(metadata: &T) -> String { - let mut emitter = yaml_emit::YamlEmitter::new(); +fn emit(metadata: &T) -> String { + let mut emitter = yaml::YamlEmitter::new(); metadata.emit_yaml(&mut emitter); emitter.into_string() diff --git a/src/metadata/plugin_cleaning_data.rs b/src/metadata/plugin_cleaning_data.rs index 550a077e..2365e913 100644 --- a/src/metadata/plugin_cleaning_data.rs +++ b/src/metadata/plugin_cleaning_data.rs @@ -6,8 +6,8 @@ use super::{ MessageContent, emit_message_contents, parse_message_contents_yaml, validate_message_contents, }, + yaml::{EmitYaml, YamlEmitter}, yaml::{YamlObjectType, as_string_node, get_as_hash, get_required_string_value, get_u32_value}, - yaml_emit::{EmitYaml, YamlEmitter}, }; /// Represents data identifying the plugin under which it is stored as dirty or @@ -181,10 +181,6 @@ impl TryFrom<&MarkedYaml> for PluginCleaningData { } impl EmitYaml for PluginCleaningData { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { emitter.begin_map(); diff --git a/src/metadata/plugin_metadata.rs b/src/metadata/plugin_metadata.rs index 7eecd345..30c8cd99 100644 --- a/src/metadata/plugin_metadata.rs +++ b/src/metadata/plugin_metadata.rs @@ -12,10 +12,10 @@ use super::{ message::Message, plugin_cleaning_data::PluginCleaningData, tag::Tag, + yaml::{EmitYaml, YamlEmitter}, yaml::{ YamlObjectType, get_as_hash, get_as_slice, get_required_string_value, get_string_value, }, - yaml_emit::{EmitYaml, YamlEmitter}, }; pub(crate) const GHOST_FILE_EXTENSION: &str = ".ghost"; @@ -376,10 +376,6 @@ fn get_vec<'a, T: TryFrom<&'a MarkedYaml, Error = impl Into> } impl EmitYaml for PluginMetadata { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { emitter.begin_map(); diff --git a/src/metadata/tag.rs b/src/metadata/tag.rs index d63a6b84..70543281 100644 --- a/src/metadata/tag.rs +++ b/src/metadata/tag.rs @@ -2,9 +2,9 @@ use saphyr::YamlData; use super::error::ExpectedType; use super::error::ParseMetadataError; +use super::yaml::EmitYaml; +use super::yaml::YamlEmitter; use super::yaml::{YamlObjectType, get_required_string_value, parse_condition}; -use super::yaml_emit::EmitYaml; -use super::yaml_emit::YamlEmitter; /// Represents whether a Bash Tag suggestion is for addition or removal. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] diff --git a/src/metadata/yaml_emit.rs b/src/metadata/yaml/emit.rs similarity index 98% rename from src/metadata/yaml_emit.rs rename to src/metadata/yaml/emit.rs index 8f8b2f41..e0c0fb18 100644 --- a/src/metadata/yaml_emit.rs +++ b/src/metadata/yaml/emit.rs @@ -1,5 +1,7 @@ pub trait EmitYaml { - fn is_scalar(&self) -> bool; + fn is_scalar(&self) -> bool { + false + } fn emit_yaml(&self, emitter: &mut YamlEmitter); } @@ -277,10 +279,6 @@ fn double_quote(value: &str) -> String { } impl EmitYaml for &[T] { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { match self { [] => {} @@ -305,10 +303,6 @@ impl EmitYaml for &[T] { } impl EmitYaml for Vec { - fn is_scalar(&self) -> bool { - false - } - fn emit_yaml(&self, emitter: &mut YamlEmitter) { self.as_slice().emit_yaml(emitter); } diff --git a/src/metadata/yaml/merge.rs b/src/metadata/yaml/merge.rs new file mode 100644 index 00000000..89994125 --- /dev/null +++ b/src/metadata/yaml/merge.rs @@ -0,0 +1,74 @@ +use std::sync::LazyLock; + +use saphyr::{MarkedYaml, YamlData}; + +use crate::metadata::error::YamlMergeKeyError; + +use super::as_string_node; + +static MERGE_KEY: LazyLock = LazyLock::new(|| as_string_node("<<")); + +pub fn process_merge_keys(mut yaml: MarkedYaml) -> Result { + match yaml.data { + YamlData::Array(a) => { + yaml.data = merge_array_elements(a).map(YamlData::Array)?; + Ok(yaml) + } + YamlData::Hash(h) => { + yaml.data = merge_hash_keys(h).map(YamlData::Hash)?; + Ok(yaml) + } + _ => Ok(yaml), + } +} + +fn merge_array_elements( + array: saphyr::AnnotatedArray, +) -> Result, YamlMergeKeyError> { + array.into_iter().map(process_merge_keys).collect() +} + +fn merge_hash_keys( + hash: saphyr::AnnotatedHash, +) -> Result, YamlMergeKeyError> { + let mut hash: saphyr::AnnotatedHash = hash + .into_iter() + .map(|(key, value)| { + process_merge_keys(key) + .and_then(|key| process_merge_keys(value).map(|value| (key, value))) + }) + .collect::>()?; + + if let Some(value) = hash.remove(&MERGE_KEY) { + merge_into_hash(hash, value) + } else { + Ok(hash) + } +} + +fn merge_into_hash( + hash: saphyr::AnnotatedHash, + value: MarkedYaml, +) -> Result, YamlMergeKeyError> { + match value.data { + YamlData::::Array(a) => a.into_iter().try_fold(hash, |acc, e| { + if let YamlData::Hash(h) = e.data { + Ok(merge_hashes(acc, h)) + } else { + Err(YamlMergeKeyError::new(e)) + } + }), + YamlData::::Hash(h) => Ok(merge_hashes(hash, h)), + _ => Err(YamlMergeKeyError::new(value)), + } +} + +fn merge_hashes( + mut hash1: saphyr::AnnotatedHash, + hash2: saphyr::AnnotatedHash, +) -> saphyr::AnnotatedHash { + for (key, value) in hash2 { + hash1.entry(key).or_insert(value); + } + hash1 +} diff --git a/src/metadata/yaml/mod.rs b/src/metadata/yaml/mod.rs new file mode 100644 index 00000000..8eab521a --- /dev/null +++ b/src/metadata/yaml/mod.rs @@ -0,0 +1,10 @@ +mod emit; +mod merge; +mod parse; + +pub use emit::{EmitYaml, YamlEmitter}; +pub use merge::process_merge_keys; +pub use parse::{ + YamlObjectType, as_string_node, get_as_hash, get_as_slice, get_required_string_value, + get_string_value, get_strings_vec_value, get_u32_value, parse_condition, to_yaml, +}; diff --git a/src/metadata/yaml.rs b/src/metadata/yaml/parse.rs similarity index 91% rename from src/metadata/yaml.rs rename to src/metadata/yaml/parse.rs index 7f3ea765..1364ea5e 100644 --- a/src/metadata/yaml.rs +++ b/src/metadata/yaml/parse.rs @@ -1,9 +1,9 @@ use std::str::FromStr; use loot_condition_interpreter::Expression; -use saphyr::{AnnotatedArray, AnnotatedHash, MarkedYaml, Marker, Yaml, YamlData}; +use saphyr::{AnnotatedHash, MarkedYaml, Marker, Yaml, YamlData}; -use super::error::{ExpectedType, MetadataParsingErrorReason, ParseMetadataError}; +use super::super::error::{ExpectedType, MetadataParsingErrorReason, ParseMetadataError}; #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum YamlObjectType { @@ -42,24 +42,18 @@ pub fn to_yaml(yaml: &MarkedYaml) -> Yaml { saphyr::YamlData::Integer(v) => Yaml::Integer(*v), saphyr::YamlData::String(v) => Yaml::String(v.clone()), saphyr::YamlData::Boolean(v) => Yaml::Boolean(*v), - saphyr::YamlData::Array(v) => Yaml::Array(to_array(v)), - saphyr::YamlData::Hash(v) => Yaml::Hash(to_hash(v)), + saphyr::YamlData::Array(v) => Yaml::Array(v.iter().map(to_yaml).collect()), + saphyr::YamlData::Hash(v) => Yaml::Hash( + v.iter() + .map(|(key, value)| (to_yaml(key), to_yaml(value))) + .collect(), + ), saphyr::YamlData::Alias(v) => Yaml::Alias(*v), saphyr::YamlData::Null => Yaml::Null, saphyr::YamlData::BadValue => Yaml::BadValue, } } -fn to_array(array: &AnnotatedArray) -> saphyr::Array { - array.iter().map(to_yaml).collect() -} - -fn to_hash(hash: &AnnotatedHash) -> saphyr::Hash { - hash.iter() - .map(|(key, value)| (to_yaml(key), to_yaml(value))) - .collect() -} - pub fn as_string_node(value: &str) -> MarkedYaml { MarkedYaml { span: saphyr_parser::Span::default(),