Skip to content

Commit 81fc15d

Browse files
committed
EBML: Make SimpleTag creation easier
1 parent 85cbea3 commit 81fc15d

File tree

4 files changed

+146
-27
lines changed

4 files changed

+146
-27
lines changed

lofty/src/ebml/read/segment_tags.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ where
2828
Ok(())
2929
}
3030

31-
fn read_tag<R>(children_reader: &mut ElementChildIterator<'_, R>) -> Result<Tag>
31+
fn read_tag<R>(children_reader: &mut ElementChildIterator<'_, R>) -> Result<Tag<'static>>
3232
where
3333
R: Read + Seek,
3434
{
@@ -132,7 +132,9 @@ where
132132
Ok(target)
133133
}
134134

135-
fn read_simple_tag<R>(children_reader: &mut ElementChildIterator<'_, R>) -> Result<SimpleTag>
135+
fn read_simple_tag<R>(
136+
children_reader: &mut ElementChildIterator<'_, R>,
137+
) -> Result<SimpleTag<'static>>
136138
where
137139
R: Read + Seek,
138140
{
@@ -185,7 +187,7 @@ where
185187
continue;
186188
}
187189

188-
value = Some(TagValue::String(children_reader.read_string(size.value())?));
190+
value = Some(TagValue::from(children_reader.read_string(size.value())?));
189191
},
190192
ElementIdent::TagBinary => {
191193
if value.is_some() {
@@ -194,7 +196,7 @@ where
194196
continue;
195197
}
196198

197-
value = Some(TagValue::Binary(children_reader.read_binary(size.value())?));
199+
value = Some(TagValue::from(children_reader.read_binary(size.value())?));
198200
},
199201
_ => {
200202
unreachable!(
@@ -212,7 +214,7 @@ where
212214
};
213215

214216
Ok(SimpleTag {
215-
name,
217+
name: name.into(),
216218
language,
217219
default,
218220
value,

lofty/src/ebml/tag/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use lofty_attr::tag;
2424
#[derive(Default, Debug, PartialEq, Eq, Clone)]
2525
#[tag(description = "An `EBML` tag", supported_formats(Ebml))]
2626
pub struct EbmlTag {
27-
pub(crate) tags: Vec<Tag>,
27+
pub(crate) tags: Vec<Tag<'static>>,
2828
pub(crate) attached_files: Vec<AttachedFile>,
2929
}
3030

@@ -40,11 +40,7 @@ impl TagExt for EbmlTag {
4040
}
4141

4242
fn len(&self) -> usize {
43-
self.tags
44-
.iter()
45-
.map(|tag| tag.simple_tags.len())
46-
.sum::<usize>()
47-
+ self.attached_files.len()
43+
self.tags.iter().map(Tag::len).sum::<usize>() + self.attached_files.len()
4844
}
4945

5046
fn contains<'a>(&'a self, _key: Self::RefKey<'a>) -> bool {

lofty/src/ebml/tag/simple_tag.rs

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::borrow::Cow;
2+
13
use crate::tag::ItemValue;
24

35
/// The language of a [`SimpleTag`] or chapter
@@ -35,27 +37,72 @@ pub enum Language {
3537
/// - [`ItemValue::Text`] | [`ItemValue::Locator`] -> [`TagValue::String`]
3638
/// - [`ItemValue::Binary`] -> [`TagValue::Binary`]
3739
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38-
pub enum TagValue {
40+
pub enum TagValue<'a> {
3941
/// A UTF-8 string tag value
40-
String(String),
42+
String(Cow<'a, str>),
4143
/// A binary tag value
42-
Binary(Vec<u8>),
44+
Binary(Cow<'a, [u8]>),
4345
}
4446

45-
impl From<TagValue> for ItemValue {
46-
fn from(value: TagValue) -> Self {
47+
impl From<TagValue<'_>> for ItemValue {
48+
fn from(value: TagValue<'_>) -> Self {
4749
match value {
48-
TagValue::String(s) => ItemValue::Text(s),
49-
TagValue::Binary(b) => ItemValue::Binary(b),
50+
TagValue::String(s) => ItemValue::Text(s.into_owned()),
51+
TagValue::Binary(b) => ItemValue::Binary(b.into_owned()),
5052
}
5153
}
5254
}
5355

54-
impl From<ItemValue> for TagValue {
56+
impl From<ItemValue> for TagValue<'_> {
5557
fn from(value: ItemValue) -> Self {
5658
match value {
57-
ItemValue::Text(s) | ItemValue::Locator(s) => TagValue::String(s),
58-
ItemValue::Binary(b) => TagValue::Binary(b),
59+
ItemValue::Text(s) | ItemValue::Locator(s) => TagValue::String(Cow::Owned(s)),
60+
ItemValue::Binary(b) => TagValue::Binary(Cow::Owned(b)),
61+
}
62+
}
63+
}
64+
65+
impl From<String> for TagValue<'_> {
66+
fn from(value: String) -> Self {
67+
TagValue::String(value.into())
68+
}
69+
}
70+
71+
impl<'a> From<Cow<'a, str>> for TagValue<'a> {
72+
fn from(value: Cow<'a, str>) -> Self {
73+
TagValue::String(value)
74+
}
75+
}
76+
77+
impl<'a> From<&'a str> for TagValue<'a> {
78+
fn from(value: &'a str) -> Self {
79+
TagValue::String(Cow::Borrowed(value))
80+
}
81+
}
82+
83+
impl From<Vec<u8>> for TagValue<'_> {
84+
fn from(value: Vec<u8>) -> Self {
85+
TagValue::Binary(value.into())
86+
}
87+
}
88+
89+
impl<'a> From<Cow<'a, [u8]>> for TagValue<'a> {
90+
fn from(value: Cow<'a, [u8]>) -> Self {
91+
TagValue::Binary(value)
92+
}
93+
}
94+
95+
impl<'a> From<&'a [u8]> for TagValue<'a> {
96+
fn from(value: &'a [u8]) -> Self {
97+
TagValue::Binary(Cow::Borrowed(value))
98+
}
99+
}
100+
101+
impl TagValue<'_> {
102+
fn into_owned(self) -> TagValue<'static> {
103+
match self {
104+
TagValue::String(s) => TagValue::String(Cow::Owned(s.into_owned())),
105+
TagValue::Binary(b) => TagValue::Binary(Cow::Owned(b.into_owned())),
59106
}
60107
}
61108
}
@@ -69,7 +116,7 @@ impl From<ItemValue> for TagValue {
69116
/// - This also means that multiple tags can describe the same target.
70117
/// - They **do not** need to have a value.
71118
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
72-
pub struct SimpleTag {
119+
pub struct SimpleTag<'a> {
73120
/// The name of the tag as it is stored
74121
///
75122
/// This field can essentially contain anything, but the following conditions are recommended:
@@ -78,12 +125,12 @@ pub struct SimpleTag {
78125
/// - It **SHOULD NOT** contain any space.
79126
///
80127
/// When in doubt, the [`TagName`] enum can be used, which covers all specified tags.
81-
pub name: String,
128+
pub name: Cow<'a, str>,
82129
/// The language of the tag
83130
///
84131
/// See [`Language`] for more information.
85132
pub language: Option<Language>,
86-
/// Whether [`language`] is the default/original langauge to use
133+
/// Whether [`language`] is the default/original language to use
87134
///
88135
/// This is used when multiple languages are present in a file. This field will be ignored
89136
/// if [`language`] is `None`.
@@ -93,5 +140,38 @@ pub struct SimpleTag {
93140
/// The actual tag value
94141
///
95142
/// For more information, see [`TagValue`]
96-
pub value: Option<TagValue>,
143+
pub value: Option<TagValue<'a>>,
144+
}
145+
146+
impl<'a> SimpleTag<'a> {
147+
/// Create a new `SimpleTag` with the given name and value
148+
///
149+
/// # Example
150+
///
151+
/// ```
152+
/// use lofty::ebml::{SimpleTag, TagValue};
153+
///
154+
/// let tag = SimpleTag::new("TITLE", "My Title");
155+
/// ```
156+
pub fn new<N, V>(name: N, value: V) -> Self
157+
where
158+
N: Into<Cow<'a, str>>,
159+
V: Into<TagValue<'a>>,
160+
{
161+
Self {
162+
name: name.into(),
163+
language: None,
164+
default: false,
165+
value: Some(value.into()),
166+
}
167+
}
168+
169+
pub(crate) fn into_owned(self) -> SimpleTag<'static> {
170+
SimpleTag {
171+
name: Cow::Owned(self.name.into_owned()),
172+
language: self.language,
173+
default: self.default,
174+
value: self.value.map(TagValue::into_owned),
175+
}
176+
}
97177
}

lofty/src/ebml/tag/tag.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,50 @@ use super::{SimpleTag, Target};
88
/// This structure is very different from other formats. See [`Target`] and [`SimpleTag`] for more
99
/// information on how these work.
1010
#[derive(Default, Debug, PartialEq, Eq, Clone)]
11-
pub struct Tag {
11+
pub struct Tag<'a> {
1212
/// The target for which the tags are applied.
1313
pub target: Target,
1414
/// General information about the target
15-
pub simple_tags: Vec<SimpleTag>,
15+
pub simple_tags: Vec<SimpleTag<'a>>,
16+
}
17+
18+
impl Tag<'_> {
19+
/// Get the number of simple tags in this tag.
20+
///
21+
/// # Example
22+
///
23+
/// ```
24+
/// use lofty::ebml::{SimpleTag, Tag, Target};
25+
///
26+
/// let tag = Tag {
27+
/// target: Target::default(),
28+
/// simple_tags: vec![
29+
/// SimpleTag::new("TITLE", "My Title"),
30+
/// SimpleTag::new("ARTIST", "My Artist"),
31+
/// ],
32+
/// };
33+
///
34+
/// assert_eq!(tag.len(), 2);
35+
/// ```
36+
pub fn len(&self) -> usize {
37+
self.simple_tags.len()
38+
}
39+
40+
/// Check if there are no simple tags in this tag.
41+
///
42+
/// # Example
43+
///
44+
/// ```
45+
/// use lofty::ebml::{SimpleTag, Tag, Target};
46+
///
47+
/// let tag = Tag {
48+
/// target: Target::default(),
49+
/// simple_tags: vec![],
50+
/// };
51+
///
52+
/// assert!(tag.is_empty());
53+
/// ```
54+
pub fn is_empty(&self) -> bool {
55+
self.simple_tags.is_empty()
56+
}
1657
}

0 commit comments

Comments
 (0)