Skip to content

Commit

Permalink
EBML: Make SimpleTag creation easier
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA committed Sep 16, 2024
1 parent 85cbea3 commit 81fc15d
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 27 deletions.
12 changes: 7 additions & 5 deletions lofty/src/ebml/read/segment_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ where
Ok(())
}

fn read_tag<R>(children_reader: &mut ElementChildIterator<'_, R>) -> Result<Tag>
fn read_tag<R>(children_reader: &mut ElementChildIterator<'_, R>) -> Result<Tag<'static>>
where
R: Read + Seek,
{
Expand Down Expand Up @@ -132,7 +132,9 @@ where
Ok(target)
}

fn read_simple_tag<R>(children_reader: &mut ElementChildIterator<'_, R>) -> Result<SimpleTag>
fn read_simple_tag<R>(
children_reader: &mut ElementChildIterator<'_, R>,
) -> Result<SimpleTag<'static>>
where
R: Read + Seek,
{
Expand Down Expand Up @@ -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() {
Expand All @@ -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!(
Expand All @@ -212,7 +214,7 @@ where
};

Ok(SimpleTag {
name,
name: name.into(),
language,
default,
value,
Expand Down
8 changes: 2 additions & 6 deletions lofty/src/ebml/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Tag>,
pub(crate) tags: Vec<Tag<'static>>,
pub(crate) attached_files: Vec<AttachedFile>,
}

Expand All @@ -40,11 +40,7 @@ impl TagExt for EbmlTag {
}

fn len(&self) -> usize {
self.tags
.iter()
.map(|tag| tag.simple_tags.len())
.sum::<usize>()
+ self.attached_files.len()
self.tags.iter().map(Tag::len).sum::<usize>() + self.attached_files.len()
}

fn contains<'a>(&'a self, _key: Self::RefKey<'a>) -> bool {
Expand Down
108 changes: 94 additions & 14 deletions lofty/src/ebml/tag/simple_tag.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use crate::tag::ItemValue;

/// The language of a [`SimpleTag`] or chapter
Expand Down Expand Up @@ -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<u8>),
Binary(Cow<'a, [u8]>),
}

impl From<TagValue> for ItemValue {
fn from(value: TagValue) -> Self {
impl From<TagValue<'_>> 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<ItemValue> for TagValue {
impl From<ItemValue> 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<String> for TagValue<'_> {
fn from(value: String) -> Self {
TagValue::String(value.into())
}
}

impl<'a> From<Cow<'a, str>> 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<Vec<u8>> for TagValue<'_> {
fn from(value: Vec<u8>) -> Self {
TagValue::Binary(value.into())
}
}

impl<'a> From<Cow<'a, [u8]>> 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())),
}
}
}
Expand All @@ -69,7 +116,7 @@ impl From<ItemValue> 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:
Expand All @@ -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<Language>,
/// 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`.
Expand All @@ -93,5 +140,38 @@ pub struct SimpleTag {
/// The actual tag value
///
/// For more information, see [`TagValue`]
pub value: Option<TagValue>,
pub value: Option<TagValue<'a>>,
}

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<N, V>(name: N, value: V) -> Self
where
N: Into<Cow<'a, str>>,
V: Into<TagValue<'a>>,
{
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),
}
}
}
45 changes: 43 additions & 2 deletions lofty/src/ebml/tag/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SimpleTag>,
pub simple_tags: Vec<SimpleTag<'a>>,
}

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()
}
}

0 comments on commit 81fc15d

Please sign in to comment.