From 422ed8cce327d3cab99354d4e0f2dc51aeb83674 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 30 Mar 2025 08:38:43 +0100 Subject: [PATCH] Hide MarkedYaml from the public API This gives the freedom to changing the YAML parsing implementation without changing the public API. --- src/metadata/file.rs | 29 ++++++------- src/metadata/group.rs | 28 ++++++------- src/metadata/location.rs | 30 ++++++------- src/metadata/message.rs | 63 +++++++++++++--------------- src/metadata/metadata_document.rs | 8 ++-- src/metadata/plugin_cleaning_data.rs | 42 +++++++++---------- src/metadata/plugin_metadata.rs | 28 ++++++------- src/metadata/tag.rs | 34 +++++++-------- src/metadata/yaml/mod.rs | 5 ++- src/metadata/yaml/parse.rs | 6 +++ 10 files changed, 133 insertions(+), 140 deletions(-) diff --git a/src/metadata/file.rs b/src/metadata/file.rs index 593f98ba..25fa7c93 100644 --- a/src/metadata/file.rs +++ b/src/metadata/file.rs @@ -7,10 +7,9 @@ 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, + EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, as_string_node, + get_required_string_value, get_string_value, parse_condition, }, }; @@ -139,10 +138,8 @@ impl std::fmt::Display for Filename { } } -impl TryFrom<&MarkedYaml> for File { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for File { + fn try_from_yaml(value: &MarkedYaml) -> Result { match &value.data { YamlData::String(s) => Ok(File { name: Filename::new(s.clone()), @@ -249,7 +246,7 @@ mod tests { fn should_only_set_name_if_decoding_from_scalar() { let yaml = parse("name1"); - let file = File::try_from(&yaml).unwrap(); + let file = File::try_from_yaml(&yaml).unwrap(); assert_eq!("name1", file.name().as_str()); assert!(file.display_name().is_none()); @@ -261,21 +258,21 @@ mod tests { fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(File::try_from(&yaml).is_err()); + assert!(File::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_name_is_missing() { let yaml = parse("{display: display1}"); - assert!(File::try_from(&yaml).is_err()); + assert!(File::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_an_invalid_condition() { let yaml = parse("{name: name1, condition: invalid}"); - assert!(File::try_from(&yaml).is_err()); + assert!(File::try_from_yaml(&yaml).is_err()); } #[test] @@ -284,7 +281,7 @@ mod tests { "{name: name1, display: display1, condition: 'file(\"Foo.esp\")', detail: 'details'}", ); - let file = File::try_from(&yaml).unwrap(); + let file = File::try_from_yaml(&yaml).unwrap(); assert_eq!("name1", file.name().as_str()); assert_eq!("display1", file.display_name().unwrap()); @@ -296,7 +293,7 @@ mod tests { fn should_leave_optional_fields_empty_if_not_present() { let yaml = parse("{name: name1}"); - let file = File::try_from(&yaml).unwrap(); + let file = File::try_from_yaml(&yaml).unwrap(); assert_eq!("name1", file.name().as_str()); assert!(file.display_name().is_none()); @@ -310,7 +307,7 @@ mod tests { "{name: name1, detail: [{text: english, lang: en}, {text: french, lang: fr}]}", ); - let file = File::try_from(&yaml).unwrap(); + let file = File::try_from_yaml(&yaml).unwrap(); assert_eq!( &[ @@ -325,7 +322,7 @@ mod tests { fn should_not_error_if_one_detail_is_given_and_it_is_not_english() { let yaml = parse("name: name1\ndetail:\n - lang: fr\n text: content1"); - let file = File::try_from(&yaml).unwrap(); + let file = File::try_from_yaml(&yaml).unwrap(); assert_eq!( &[MessageContent::new("content1".into()).with_language("fr".into())], @@ -339,7 +336,7 @@ mod tests { "name: name1\ndetail:\n - lang: de\n text: content1\n - lang: fr\n text: content2", ); - assert!(File::try_from(&yaml).is_err()); + assert!(File::try_from_yaml(&yaml).is_err()); } } diff --git a/src/metadata/group.rs b/src/metadata/group.rs index da5177cd..00c20a0c 100644 --- a/src/metadata/group.rs +++ b/src/metadata/group.rs @@ -1,9 +1,11 @@ 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::{ + error::ParseMetadataError, + yaml::{ + EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, get_as_hash, get_required_string_value, + get_string_value, get_strings_vec_value, + }, }; /// Represents a group to which plugin metadata objects can belong. @@ -82,10 +84,8 @@ impl std::default::Default for Group { } } -impl TryFrom<&MarkedYaml> for Group { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for Group { + fn try_from_yaml(value: &MarkedYaml) -> Result { let hash = get_as_hash(value, YamlObjectType::Group)?; let name = @@ -143,32 +143,32 @@ mod tests { fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(Group::try_from(&yaml).is_err()); + assert!(Group::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_name_is_missing() { let yaml = parse("{description: text}"); - assert!(Group::try_from(&yaml).is_err()); + assert!(Group::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_after_is_not_an_array_of_strings() { let yaml = parse("{name: group1, after: other_group}"); - assert!(Group::try_from(&yaml).is_err()); + assert!(Group::try_from_yaml(&yaml).is_err()); let yaml = parse("{name: group1, after: [0, 1]}"); - assert!(Group::try_from(&yaml).is_err()); + assert!(Group::try_from_yaml(&yaml).is_err()); } #[test] fn should_set_all_given_fields() { let yaml = parse("{name: group1, description: text, after: [ other_group ]}"); - let group = Group::try_from(&yaml).unwrap(); + let group = Group::try_from_yaml(&yaml).unwrap(); assert_eq!("group1", group.name()); assert_eq!("text", group.description().unwrap()); @@ -179,7 +179,7 @@ mod tests { fn should_leave_optional_fields_empty_if_not_present() { let yaml = parse("{name: group1}"); - let group = Group::try_from(&yaml).unwrap(); + let group = Group::try_from_yaml(&yaml).unwrap(); assert_eq!("group1", group.name()); assert!(group.description().is_none()); diff --git a/src/metadata/location.rs b/src/metadata/location.rs index 4e062e78..dcbb88d4 100644 --- a/src/metadata/location.rs +++ b/src/metadata/location.rs @@ -1,11 +1,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::{ + error::{ExpectedType, ParseMetadataError}, + yaml::{EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, get_required_string_value}, +}; /// Represents a URL at which the parent plugin can be found. #[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -48,10 +46,8 @@ impl Location { } } -impl TryFrom<&MarkedYaml> for Location { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for Location { + fn try_from_yaml(value: &MarkedYaml) -> Result { match &value.data { YamlData::String(s) => Ok(Location { url: s.clone(), @@ -120,7 +116,7 @@ mod tests { fn should_only_set_name_if_decoding_from_scalar() { let yaml = parse("https://www.example.com"); - let location = Location::try_from(&yaml).unwrap(); + let location = Location::try_from_yaml(&yaml).unwrap(); assert_eq!("https://www.example.com", location.url()); assert!(location.name().is_none()); @@ -130,42 +126,42 @@ mod tests { fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(Location::try_from(&yaml).is_err()); + assert!(Location::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_link_is_missing() { let yaml = parse("{name: example}"); - assert!(Location::try_from(&yaml).is_err()); + assert!(Location::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_link_is_not_a_string() { let yaml = parse("{link: [https://www.example.com], name: example}"); - assert!(Location::try_from(&yaml).is_err()); + assert!(Location::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_name_is_not_a_string() { let yaml = parse("{link: https://www.example.com, name: [example]}"); - assert!(Location::try_from(&yaml).is_err()); + assert!(Location::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_name_is_missing() { let yaml = parse("{link: https://www.example.com}"); - assert!(Location::try_from(&yaml).is_err()); + assert!(Location::try_from_yaml(&yaml).is_err()); } #[test] fn should_set_all_fields() { let yaml = parse("{link: https://www.example.com, name: example}"); - let location = Location::try_from(&yaml).unwrap(); + let location = Location::try_from_yaml(&yaml).unwrap(); assert_eq!("https://www.example.com", location.url()); assert_eq!("example", location.name().unwrap()); diff --git a/src/metadata/message.rs b/src/metadata/message.rs index ea176358..ddf65138 100644 --- a/src/metadata/message.rs +++ b/src/metadata/message.rs @@ -10,10 +10,9 @@ 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, + EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, as_string_node, get_as_hash, + get_required_string_value, get_strings_vec_value, parse_condition, }, }; @@ -239,10 +238,8 @@ pub(crate) fn validate_message_contents( Ok(()) } -impl TryFrom<&MarkedYaml> for MessageContent { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for MessageContent { + fn try_from_yaml(value: &MarkedYaml) -> Result { let hash = get_as_hash(value, YamlObjectType::MessageContent)?; let text = @@ -269,7 +266,7 @@ pub(crate) fn parse_message_contents_yaml( } YamlData::Array(a) => a .iter() - .map(MessageContent::try_from) + .map(MessageContent::try_from_yaml) .collect::, _>>()?, _ => { return Err(ParseMetadataError::unexpected_value_type( @@ -293,10 +290,8 @@ pub(crate) fn parse_message_contents_yaml( } } -impl TryFrom<&MarkedYaml> for Message { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for Message { + fn try_from_yaml(value: &MarkedYaml) -> Result { let hash = get_as_hash(value, YamlObjectType::Message)?; let message_type = @@ -554,21 +549,21 @@ mod tests { fn should_error_if_given_a_scalar() { let yaml = parse("content"); - assert!(MessageContent::try_from(&yaml).is_err()); + assert!(MessageContent::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(MessageContent::try_from(&yaml).is_err()); + assert!(MessageContent::try_from_yaml(&yaml).is_err()); } #[test] fn should_set_all_given_fields() { let yaml = parse("{text: content, lang: fr}"); - let content = MessageContent::try_from(&yaml).unwrap(); + let content = MessageContent::try_from_yaml(&yaml).unwrap(); assert_eq!("content", content.text()); assert_eq!("fr", content.language()); @@ -603,35 +598,35 @@ mod tests { fn should_error_if_given_a_scalar() { let yaml = parse("content"); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_content_is_missing() { let yaml = parse("{type: say}"); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_an_invalid_condition() { let yaml = parse("{type: say, content: text, condition: invalid}"); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_set_all_given_fields() { let yaml = parse("{type: say, content: text, condition: 'file(\"Foo.esp\")'}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!(MessageType::Say, message.message_type()); assert_eq!(&[MessageContent::new("text".into())], message.content()); @@ -642,7 +637,7 @@ mod tests { fn should_leave_optional_fields_empty_if_not_present() { let yaml = parse("{type: say, content: text}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!(MessageType::Say, message.message_type()); assert_eq!(&[MessageContent::new("text".into())], message.content()); @@ -653,17 +648,17 @@ mod tests { fn should_set_say_warn_and_error_message_types() { let yaml = parse("{type: say, content: text}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!(MessageType::Say, message.message_type()); let yaml = parse("{type: warn, content: text}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!(MessageType::Warn, message.message_type()); let yaml = parse("{type: error, content: text}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!(MessageType::Error, message.message_type()); } @@ -671,7 +666,7 @@ mod tests { fn should_use_say_if_message_type_is_unrecognised() { let yaml = parse("{type: info, content: text}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!(MessageType::Say, message.message_type()); } @@ -681,7 +676,7 @@ mod tests { "{type: say, content: [{text: english, lang: en}, {text: french, lang: fr}]}", ); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!( &[ @@ -696,7 +691,7 @@ mod tests { fn should_not_error_if_one_content_object_is_given_and_it_is_not_english() { let yaml = parse("type: say\ncontent:\n - lang: fr\n text: content1"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!( &[MessageContent::new("content1".into()).with_language("fr".into())], @@ -710,14 +705,14 @@ mod tests { "type: say\ncontent:\n - lang: de\n text: content1\n - lang: fr\n text: content2", ); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_apply_substitutions_when_there_is_only_one_content_string() { let yaml = parse("type: say\ncontent: con{0}tent1\nsubs:\n - sub1"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!("consub1tent1", message.content()[0].text()); } @@ -728,7 +723,7 @@ mod tests { "type: say\ncontent:\n - lang: en\n text: content1 {0}\n - lang: fr\n text: content2 {0}\nsubs:\n - sub", ); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!("content1 sub", message.content()[0].text()); assert_eq!("content2 sub", message.content()[1].text()); @@ -738,21 +733,21 @@ mod tests { fn should_error_if_the_message_has_more_substitutions_than_expected() { let yaml = parse("{type: say, content: 'content1', subs: [sub1]}"); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_the_content_string_expects_more_substitutions_than_exist() { let yaml = parse("{type: say, content: '{0} {1}', subs: [sub1]}"); - assert!(Message::try_from(&yaml).is_err()); + assert!(Message::try_from_yaml(&yaml).is_err()); } #[test] fn should_ignore_substution_syntax_if_no_substitutions_exist() { let yaml = parse("{type: say, content: 'content {0}'}"); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!("content {0}", message.content()[0].text()); } @@ -763,7 +758,7 @@ mod tests { "{type: say, content: 'content %1% %2% %3% %4% %5% %6% %7% %8% %9% %10% %11%', subs: [a, b, c, d, e, f, g, h, i, j, k]}", ); - let message = Message::try_from(&yaml).unwrap(); + let message = Message::try_from_yaml(&yaml).unwrap(); assert_eq!("content a b c d e f g h i j k", message.content()[0].text()); } diff --git a/src/metadata/metadata_document.rs b/src/metadata/metadata_document.rs index 8619e0d5..c8da2450 100644 --- a/src/metadata/metadata_document.rs +++ b/src/metadata/metadata_document.rs @@ -16,7 +16,7 @@ use super::{ group::Group, message::Message, plugin_metadata::PluginMetadata, - yaml::{EmitYaml, YamlEmitter, YamlObjectType, get_as_slice, process_merge_keys}, + yaml::{EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, get_as_slice, process_merge_keys}, }; #[derive(Clone, Debug, Eq, PartialEq)] @@ -119,7 +119,7 @@ impl MetadataDocument { let mut plugins: HashMap = HashMap::new(); let mut regex_plugins: Vec = Vec::new(); for plugin_yaml in get_as_slice(&doc, "plugins", YamlObjectType::MetadataDocument)? { - let plugin = PluginMetadata::try_from(plugin_yaml)?; + let plugin = PluginMetadata::try_from_yaml(plugin_yaml)?; if plugin.is_regex_plugin() { regex_plugins.push(plugin); } else { @@ -137,7 +137,7 @@ impl MetadataDocument { let messages = get_as_slice(&doc, "globals", YamlObjectType::MetadataDocument)? .iter() - .map(Message::try_from) + .map(Message::try_from_yaml) .collect::, _>>()?; let mut bash_tags = Vec::new(); @@ -171,7 +171,7 @@ impl MetadataDocument { let mut group_names = HashSet::new(); let mut groups = Vec::new(); for group_yaml in get_as_slice(&doc, "groups", YamlObjectType::MetadataDocument)? { - let group = Group::try_from(group_yaml)?; + let group = Group::try_from_yaml(group_yaml)?; let name = group.name().to_string(); if group_names.contains(&name) { diff --git a/src/metadata/plugin_cleaning_data.rs b/src/metadata/plugin_cleaning_data.rs index a6ec33dd..ef09b09c 100644 --- a/src/metadata/plugin_cleaning_data.rs +++ b/src/metadata/plugin_cleaning_data.rs @@ -6,8 +6,10 @@ 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::{ + EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, as_string_node, get_as_hash, + get_required_string_value, get_u32_value, + }, }; /// Represents data identifying the plugin under which it is stored as dirty or @@ -134,10 +136,8 @@ impl PluginCleaningData { } } -impl TryFrom<&MarkedYaml> for PluginCleaningData { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for PluginCleaningData { + fn try_from_yaml(value: &MarkedYaml) -> Result { let hash = get_as_hash(value, YamlObjectType::PluginCleaningData)?; let crc = match get_u32_value(hash, "crc", YamlObjectType::PluginCleaningData)? { @@ -224,58 +224,58 @@ mod tests { fn should_error_if_given_a_scalar() { let yaml = parse("0x12345678"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_crc_is_missing() { let yaml = parse("{util: cleaner}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_util_is_missing() { let yaml = parse("{crc: 0x12345678}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_a_count_is_not_a_number() { let yaml = parse("{crc: 0x12345678, util: cleaner, itm: true}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); let yaml = parse("{crc: 0x12345678, util: cleaner, udr: true}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); let yaml = parse("{crc: 0x12345678, util: cleaner, nav: true}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_a_count_does_not_fit_in_a_u32() { let yaml = parse("{crc: 0x12345678, util: cleaner, itm: -1}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); let yaml = parse("{crc: 0x12345678, util: cleaner, udr: -2}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); let yaml = parse("{crc: 0x12345678, util: cleaner, nav: -3}"); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } #[test] @@ -283,7 +283,7 @@ mod tests { let yaml = parse("{crc: 0x12345678, util: cleaner, detail: info, itm: 2, udr: 10, nav: 30}"); - let data = PluginCleaningData::try_from(&yaml).unwrap(); + let data = PluginCleaningData::try_from_yaml(&yaml).unwrap(); assert_eq!(0x12345678, data.crc()); assert_eq!("cleaner", data.cleaning_utility()); @@ -297,7 +297,7 @@ mod tests { fn should_leave_optional_fields_at_defaults_if_not_present() { let yaml = parse("{crc: 0x12345678, util: cleaner}"); - let data = PluginCleaningData::try_from(&yaml).unwrap(); + let data = PluginCleaningData::try_from_yaml(&yaml).unwrap(); assert_eq!(0x12345678, data.crc()); assert_eq!("cleaner", data.cleaning_utility()); @@ -313,7 +313,7 @@ mod tests { "{crc: 0x12345678, util: cleaner, detail: [{text: english, lang: en}, {text: french, lang: fr}]}", ); - let data = PluginCleaningData::try_from(&yaml).unwrap(); + let data = PluginCleaningData::try_from_yaml(&yaml).unwrap(); assert_eq!( &[ @@ -329,7 +329,7 @@ mod tests { let yaml = parse("crc: 0x12345678\nutil: cleaner\ndetail:\n - lang: fr\n text: content1"); - let data = PluginCleaningData::try_from(&yaml).unwrap(); + let data = PluginCleaningData::try_from_yaml(&yaml).unwrap(); assert_eq!( &[MessageContent::new("content1".into()).with_language("fr".into())], @@ -343,7 +343,7 @@ mod tests { "crc: 0x12345678\nutil: cleaner\ndetail:\n - lang: de\n text: content1\n - lang: fr\n text: content2", ); - assert!(PluginCleaningData::try_from(&yaml).is_err()); + assert!(PluginCleaningData::try_from_yaml(&yaml).is_err()); } } diff --git a/src/metadata/plugin_metadata.rs b/src/metadata/plugin_metadata.rs index bd126f0e..33ee8e67 100644 --- a/src/metadata/plugin_metadata.rs +++ b/src/metadata/plugin_metadata.rs @@ -12,9 +12,9 @@ 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, + EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, get_as_hash, get_as_slice, + get_required_string_value, get_string_value, }, }; @@ -349,10 +349,8 @@ fn replace_capturing_groups(regex_string: &str) -> Cow<'_, str> { } } -impl TryFrom<&MarkedYaml> for PluginMetadata { - type Error = ParseMetadataError; - - fn try_from(value: &MarkedYaml) -> Result { +impl TryFromYaml for PluginMetadata { + fn try_from_yaml(value: &MarkedYaml) -> Result { let hash = get_as_hash(value, YamlObjectType::PluginMetadata)?; let name = get_required_string_value( @@ -397,13 +395,13 @@ impl TryFrom<&MarkedYaml> for PluginMetadata { } } -fn get_vec<'a, T: TryFrom<&'a MarkedYaml, Error = impl Into>>( - hash: &'a saphyr::AnnotatedHash, +fn get_vec( + hash: &saphyr::AnnotatedHash, key: &'static str, ) -> Result, ParseMetadataError> { let mut vec = get_as_slice(hash, key, YamlObjectType::PluginMetadata)? .iter() - .map(|e| T::try_from(e).map_err(Into::into)) + .map(|e| T::try_from_yaml(e)) .collect::, _>>()?; vec.shrink_to_fit(); @@ -872,14 +870,14 @@ mod tests { fn should_error_if_given_a_scalar() { let yaml = parse("name1"); - assert!(PluginMetadata::try_from(&yaml).is_err()); + assert!(PluginMetadata::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(PluginMetadata::try_from(&yaml).is_err()); + assert!(PluginMetadata::try_from_yaml(&yaml).is_err()); } #[test] @@ -908,7 +906,7 @@ mod tests { - 'https://www.example.com'", ); - let plugin = PluginMetadata::try_from(&yaml).unwrap(); + let plugin = PluginMetadata::try_from_yaml(&yaml).unwrap(); assert_eq!(BLANK_ESP, plugin.name()); assert_eq!(&[File::new(BLANK_ESM.into())], plugin.load_after_files()); @@ -955,7 +953,7 @@ mod tests { util: 'utility'", ); - let plugin = PluginMetadata::try_from(&yaml).unwrap(); + let plugin = PluginMetadata::try_from_yaml(&yaml).unwrap(); assert_eq!("Blank\\.esp", plugin.name()); assert_eq!( @@ -972,14 +970,14 @@ mod tests { fn should_error_if_regex_name_is_invalid() { let yaml = parse("{name: 'RagnvaldBook(Farengar(+Ragnvald)?)?\\.esp'}"); - assert!(PluginMetadata::try_from(&yaml).is_err()); + assert!(PluginMetadata::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_a_field_that_should_be_an_array_is_not() { let yaml = parse("{name: 'Blank.esp', after: Blank.esm}"); - assert!(PluginMetadata::try_from(&yaml).is_err()); + assert!(PluginMetadata::try_from_yaml(&yaml).is_err()); } } diff --git a/src/metadata/tag.rs b/src/metadata/tag.rs index 70543281..682ab75b 100644 --- a/src/metadata/tag.rs +++ b/src/metadata/tag.rs @@ -1,10 +1,12 @@ -use saphyr::YamlData; +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, parse_condition}; +use super::{ + error::{ExpectedType, ParseMetadataError}, + yaml::{ + EmitYaml, TryFromYaml, YamlEmitter, YamlObjectType, get_required_string_value, + parse_condition, + }, +}; /// Represents whether a Bash Tag suggestion is for addition or removal. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -62,10 +64,8 @@ impl Tag { } } -impl TryFrom<&saphyr::MarkedYaml> for Tag { - type Error = ParseMetadataError; - - fn try_from(value: &saphyr::MarkedYaml) -> Result { +impl TryFromYaml for Tag { + fn try_from_yaml(value: &MarkedYaml) -> Result { match &value.data { YamlData::String(s) => { let (name, suggestion) = name_and_suggestion(s); @@ -146,7 +146,7 @@ mod tests { fn should_only_set_name_and_suggestion_if_decoding_from_scalar() { let yaml = parse("Relev"); - let tag = Tag::try_from(&yaml).unwrap(); + let tag = Tag::try_from_yaml(&yaml).unwrap(); assert_eq!("Relev", tag.name()); assert!(tag.is_addition()); @@ -157,7 +157,7 @@ mod tests { fn should_only_set_name_and_suggestion_if_decoding_from_scalar_with_leading_hyphen() { let yaml = parse("-Relev"); - let tag = Tag::try_from(&yaml).unwrap(); + let tag = Tag::try_from_yaml(&yaml).unwrap(); assert_eq!("Relev", tag.name()); assert!(!tag.is_addition()); @@ -168,28 +168,28 @@ mod tests { fn should_error_if_given_a_list() { let yaml = parse("[0, 1, 2]"); - assert!(Tag::try_from(&yaml).is_err()); + assert!(Tag::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_name_is_missing() { let yaml = parse("{condition: 'file(\"Foo.esp\")'}"); - assert!(Tag::try_from(&yaml).is_err()); + assert!(Tag::try_from_yaml(&yaml).is_err()); } #[test] fn should_error_if_given_an_invalid_condition() { let yaml = parse("{name: Relev, condition: invalid}"); - assert!(Tag::try_from(&yaml).is_err()); + assert!(Tag::try_from_yaml(&yaml).is_err()); } #[test] fn should_set_all_fields() { let yaml = parse("{name: Relev, condition: 'file(\"Foo.esp\")'}"); - let tag = Tag::try_from(&yaml).unwrap(); + let tag = Tag::try_from_yaml(&yaml).unwrap(); assert_eq!("Relev", tag.name()); assert!(tag.is_addition()); @@ -200,7 +200,7 @@ mod tests { fn should_leave_optional_fields_empty_if_not_present() { let yaml = parse("{name: Relev}"); - let tag = Tag::try_from(&yaml).unwrap(); + let tag = Tag::try_from_yaml(&yaml).unwrap(); assert_eq!("Relev", tag.name()); assert!(tag.is_addition()); diff --git a/src/metadata/yaml/mod.rs b/src/metadata/yaml/mod.rs index 8eab521a..90d1040e 100644 --- a/src/metadata/yaml/mod.rs +++ b/src/metadata/yaml/mod.rs @@ -5,6 +5,7 @@ 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, + TryFromYaml, 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/parse.rs b/src/metadata/yaml/parse.rs index 1364ea5e..8b426887 100644 --- a/src/metadata/yaml/parse.rs +++ b/src/metadata/yaml/parse.rs @@ -192,3 +192,9 @@ pub fn parse_condition( None => Ok(None), } } + +/// This is effectively TryFrom<&MarkedYaml>, but implementing it doesn't make +/// MarkedYaml part of the crate's public API. +pub trait TryFromYaml: Sized { + fn try_from_yaml(value: &MarkedYaml) -> Result; +}