diff --git a/lofty/src/ebml/read/segment_tags.rs b/lofty/src/ebml/read/segment_tags.rs index d0c8b5d2..94efcf3a 100644 --- a/lofty/src/ebml/read/segment_tags.rs +++ b/lofty/src/ebml/read/segment_tags.rs @@ -28,7 +28,7 @@ where Ok(()) } -fn read_tag(children_reader: &mut ElementChildIterator<'_, R>) -> Result +fn read_tag(children_reader: &mut ElementChildIterator<'_, R>) -> Result> where R: Read + Seek, { @@ -132,7 +132,9 @@ where Ok(target) } -fn read_simple_tag(children_reader: &mut ElementChildIterator<'_, R>) -> Result +fn read_simple_tag( + children_reader: &mut ElementChildIterator<'_, R>, +) -> Result> where R: Read + Seek, { @@ -185,7 +187,7 @@ where continue; } - value = Some(TagValue::String(children_reader.read_string(size.value())?)); + value = Some(TagValue::from(children_reader.read_string(size.value())?)); }, ElementIdent::TagBinary => { if value.is_some() { @@ -194,7 +196,7 @@ where continue; } - value = Some(TagValue::Binary(children_reader.read_binary(size.value())?)); + value = Some(TagValue::from(children_reader.read_binary(size.value())?)); }, _ => { unreachable!( @@ -212,7 +214,7 @@ where }; Ok(SimpleTag { - name, + name: name.into(), language, default, value, diff --git a/lofty/src/ebml/tag/mod.rs b/lofty/src/ebml/tag/mod.rs index 9c216523..a2a8e498 100644 --- a/lofty/src/ebml/tag/mod.rs +++ b/lofty/src/ebml/tag/mod.rs @@ -24,7 +24,7 @@ use lofty_attr::tag; #[derive(Default, Debug, PartialEq, Eq, Clone)] #[tag(description = "An `EBML` tag", supported_formats(Ebml))] pub struct EbmlTag { - pub(crate) tags: Vec, + pub(crate) tags: Vec>, pub(crate) attached_files: Vec, } @@ -40,11 +40,7 @@ impl TagExt for EbmlTag { } fn len(&self) -> usize { - self.tags - .iter() - .map(|tag| tag.simple_tags.len()) - .sum::() - + self.attached_files.len() + self.tags.iter().map(Tag::len).sum::() + self.attached_files.len() } fn contains<'a>(&'a self, _key: Self::RefKey<'a>) -> bool { diff --git a/lofty/src/ebml/tag/simple_tag.rs b/lofty/src/ebml/tag/simple_tag.rs index 427d408f..fea1cf12 100644 --- a/lofty/src/ebml/tag/simple_tag.rs +++ b/lofty/src/ebml/tag/simple_tag.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::tag::ItemValue; /// The language of a [`SimpleTag`] or chapter @@ -35,27 +37,72 @@ pub enum Language { /// - [`ItemValue::Text`] | [`ItemValue::Locator`] -> [`TagValue::String`] /// - [`ItemValue::Binary`] -> [`TagValue::Binary`] #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum TagValue { +pub enum TagValue<'a> { /// A UTF-8 string tag value - String(String), + String(Cow<'a, str>), /// A binary tag value - Binary(Vec), + Binary(Cow<'a, [u8]>), } -impl From for ItemValue { - fn from(value: TagValue) -> Self { +impl From> for ItemValue { + fn from(value: TagValue<'_>) -> Self { match value { - TagValue::String(s) => ItemValue::Text(s), - TagValue::Binary(b) => ItemValue::Binary(b), + TagValue::String(s) => ItemValue::Text(s.into_owned()), + TagValue::Binary(b) => ItemValue::Binary(b.into_owned()), } } } -impl From for TagValue { +impl From for TagValue<'_> { fn from(value: ItemValue) -> Self { match value { - ItemValue::Text(s) | ItemValue::Locator(s) => TagValue::String(s), - ItemValue::Binary(b) => TagValue::Binary(b), + ItemValue::Text(s) | ItemValue::Locator(s) => TagValue::String(Cow::Owned(s)), + ItemValue::Binary(b) => TagValue::Binary(Cow::Owned(b)), + } + } +} + +impl From for TagValue<'_> { + fn from(value: String) -> Self { + TagValue::String(value.into()) + } +} + +impl<'a> From> for TagValue<'a> { + fn from(value: Cow<'a, str>) -> Self { + TagValue::String(value) + } +} + +impl<'a> From<&'a str> for TagValue<'a> { + fn from(value: &'a str) -> Self { + TagValue::String(Cow::Borrowed(value)) + } +} + +impl From> for TagValue<'_> { + fn from(value: Vec) -> Self { + TagValue::Binary(value.into()) + } +} + +impl<'a> From> for TagValue<'a> { + fn from(value: Cow<'a, [u8]>) -> Self { + TagValue::Binary(value) + } +} + +impl<'a> From<&'a [u8]> for TagValue<'a> { + fn from(value: &'a [u8]) -> Self { + TagValue::Binary(Cow::Borrowed(value)) + } +} + +impl TagValue<'_> { + fn into_owned(self) -> TagValue<'static> { + match self { + TagValue::String(s) => TagValue::String(Cow::Owned(s.into_owned())), + TagValue::Binary(b) => TagValue::Binary(Cow::Owned(b.into_owned())), } } } @@ -69,7 +116,7 @@ impl From for TagValue { /// - This also means that multiple tags can describe the same target. /// - They **do not** need to have a value. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SimpleTag { +pub struct SimpleTag<'a> { /// The name of the tag as it is stored /// /// This field can essentially contain anything, but the following conditions are recommended: @@ -78,12 +125,12 @@ pub struct SimpleTag { /// - It **SHOULD NOT** contain any space. /// /// When in doubt, the [`TagName`] enum can be used, which covers all specified tags. - pub name: String, + pub name: Cow<'a, str>, /// The language of the tag /// /// See [`Language`] for more information. pub language: Option, - /// Whether [`language`] is the default/original langauge to use + /// Whether [`language`] is the default/original language to use /// /// This is used when multiple languages are present in a file. This field will be ignored /// if [`language`] is `None`. @@ -93,5 +140,38 @@ pub struct SimpleTag { /// The actual tag value /// /// For more information, see [`TagValue`] - pub value: Option, + pub value: Option>, +} + +impl<'a> SimpleTag<'a> { + /// Create a new `SimpleTag` with the given name and value + /// + /// # Example + /// + /// ``` + /// use lofty::ebml::{SimpleTag, TagValue}; + /// + /// let tag = SimpleTag::new("TITLE", "My Title"); + /// ``` + pub fn new(name: N, value: V) -> Self + where + N: Into>, + V: Into>, + { + Self { + name: name.into(), + language: None, + default: false, + value: Some(value.into()), + } + } + + pub(crate) fn into_owned(self) -> SimpleTag<'static> { + SimpleTag { + name: Cow::Owned(self.name.into_owned()), + language: self.language, + default: self.default, + value: self.value.map(TagValue::into_owned), + } + } } diff --git a/lofty/src/ebml/tag/tag.rs b/lofty/src/ebml/tag/tag.rs index ffc7386b..32a2cf6e 100644 --- a/lofty/src/ebml/tag/tag.rs +++ b/lofty/src/ebml/tag/tag.rs @@ -8,9 +8,50 @@ use super::{SimpleTag, Target}; /// This structure is very different from other formats. See [`Target`] and [`SimpleTag`] for more /// information on how these work. #[derive(Default, Debug, PartialEq, Eq, Clone)] -pub struct Tag { +pub struct Tag<'a> { /// The target for which the tags are applied. pub target: Target, /// General information about the target - pub simple_tags: Vec, + pub simple_tags: Vec>, +} + +impl Tag<'_> { + /// Get the number of simple tags in this tag. + /// + /// # Example + /// + /// ``` + /// use lofty::ebml::{SimpleTag, Tag, Target}; + /// + /// let tag = Tag { + /// target: Target::default(), + /// simple_tags: vec![ + /// SimpleTag::new("TITLE", "My Title"), + /// SimpleTag::new("ARTIST", "My Artist"), + /// ], + /// }; + /// + /// assert_eq!(tag.len(), 2); + /// ``` + pub fn len(&self) -> usize { + self.simple_tags.len() + } + + /// Check if there are no simple tags in this tag. + /// + /// # Example + /// + /// ``` + /// use lofty::ebml::{SimpleTag, Tag, Target}; + /// + /// let tag = Tag { + /// target: Target::default(), + /// simple_tags: vec![], + /// }; + /// + /// assert!(tag.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.simple_tags.is_empty() + } }