Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions lofty/src/tag/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,18 +828,36 @@ impl TagItem {

/// Set a language for the [`TagItem`]
///
/// The default language is empty.
///
/// NOTE: This will not be reflected in most tag formats.
pub fn set_lang(&mut self, lang: Lang) {
self.lang = lang;
}

/// Returns a reference to the language of the [`TagItem`]
///
/// NOTE: This will not be reflected in most tag formats.
pub fn lang(&self) -> &Lang {
&self.lang
}

/// Set a description for the [`TagItem`]
///
/// The default description is empty.
///
/// NOTE: This will not be reflected in most tag formats.
pub fn set_description(&mut self, description: String) {
self.description = description;
}

/// Returns a reference to the description of the [`TagItem`]
///
/// NOTE: This will not be reflected in most tag formats.
pub fn description(&self) -> &str {
&self.description
}

/// Returns a reference to the [`ItemKey`]
pub fn key(&self) -> &ItemKey {
&self.item_key
Expand Down
45 changes: 43 additions & 2 deletions lofty/src/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,52 @@ impl Tag {

/// Removes all items with the specified [`ItemKey`], and returns them
pub fn take(&mut self, key: &ItemKey) -> impl Iterator<Item = TagItem> + '_ {
self.take_filter(key, |_| true)
}

/// Removes selected items with the specified [`ItemKey`], and returns them
///
/// Only takes items for which `filter()` returns `true`. All other items are retained.
///
/// # Examples
///
/// ```
/// use lofty::tag::{ItemKey, ItemValue, Tag, TagItem, TagType};
///
/// let mut tag = Tag::new(TagType::Id3v2);
/// tag.push(TagItem::new(
/// ItemKey::Comment,
/// ItemValue::Text("comment without description".to_owned()),
/// ));
/// let mut item = TagItem::new(
/// ItemKey::Comment,
/// ItemValue::Text("comment with description".to_owned()),
/// );
/// item.set_description("description".to_owned());
/// tag.push(item);
/// assert_eq!(tag.get_strings(&ItemKey::Comment).count(), 2);
///
/// // Extract all comment items with an empty description.
/// let comments = tag
/// .take_filter(&ItemKey::Comment, |item| item.description().is_empty())
/// .filter_map(|item| item.into_value().into_string())
/// .collect::<Vec<_>>();
/// assert_eq!(comments, vec!["comment without description".to_owned()]);
///
/// // The comments that didn't match the filter are still present.
/// assert_eq!(tag.get_strings(&ItemKey::Comment).count(), 1);
/// ```
pub fn take_filter(
&mut self,
key: &ItemKey,
mut filter: impl FnMut(&TagItem) -> bool,
) -> impl Iterator<Item = TagItem> + '_ {
// TODO: drain_filter
let mut split_idx = 0_usize;
let mut split_idx = 0;

for read_idx in 0..self.items.len() {
if self.items[read_idx].key() == key {
let item = &self.items[read_idx];
if item.key() == key && filter(item) {
self.items.swap(split_idx, read_idx);
split_idx += 1;
}
Expand Down