tag: Remove global re-exports of tag items

This commit is contained in:
Serial 2024-04-13 14:01:48 -04:00 committed by Alex
parent 826e55be23
commit cf778c1f58
41 changed files with 302 additions and 282 deletions

View file

@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- ⚠️ Important ⚠️: Moved to `lofty::options` ([PR](https://github.com/Serial-ATA/lofty-rs/pull/374))
- **AudioFile**/**TaggedFileExt**/**TaggedFile**/**BoundTaggedFile**:
- ⚠️ Important ⚠️: Moved to `lofty::file` ([PR](https://github.com/Serial-ATA/lofty-rs/pull/374))
- **Tag**/**TagType**/**TagExt**/**TagItem**/**ItemKey**/**ItemValue**:
- ⚠️ Important ⚠️: Moved to `lofty::tag` ([PR](https://github.com/Serial-ATA/lofty-rs/pull/374))
### Fixed
- **Vorbis**: Fix panic when reading properties of zero-length files ([issue](https://github.com/Serial-ATA/lofty-rs/issues/342)) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/365))

View file

@ -5,7 +5,7 @@ use lofty::file::FileType;
use lofty::id3::v2::Id3v2Tag;
use lofty::properties::FileProperties;
use lofty::resolve::FileResolver;
use lofty::TagType;
use lofty::tag::TagType;
use lofty_attr::LoftyFile;
use std::fs::File;

View file

@ -33,8 +33,7 @@ fn main() {
// import keys from https://docs.rs/lofty/latest/lofty/enum.ItemKey.html
println!(
"Album Artist: {}",
tag.get_string(&lofty::ItemKey::AlbumArtist)
.unwrap_or("None")
tag.get_string(&ItemKey::AlbumArtist).unwrap_or("None")
);
let properties = tagged_file.properties();

View file

@ -1,6 +1,7 @@
use lofty::config::WriteOptions;
use lofty::prelude::*;
use lofty::{Probe, Tag};
use lofty::tag::Tag;
use lofty::Probe;
use structopt::StructOpt;

View file

@ -78,10 +78,10 @@ pub(crate) fn init_write_lookup(
insert!(map, AiffText, {
lofty::iff::aiff::tag::AiffTextChunksRef {
name: tag.get_string(&lofty::tag::item::ItemKey::TrackTitle),
author: tag.get_string(&lofty::tag::item::ItemKey::TrackArtist),
copyright: tag.get_string(&lofty::tag::item::ItemKey::CopyrightMessage),
annotations: Some(tag.get_strings(&lofty::tag::item::ItemKey::Comment)),
name: tag.get_string(&lofty::prelude::ItemKey::TrackTitle),
author: tag.get_string(&lofty::prelude::ItemKey::TrackArtist),
copyright: tag.get_string(&lofty::prelude::ItemKey::CopyrightMessage),
annotations: Some(tag.get_strings(&lofty::prelude::ItemKey::Comment)),
comments: None,
}
.write_to(data, write_options)
@ -96,11 +96,12 @@ pub(crate) fn write_module(
) -> proc_macro2::TokenStream {
let applicable_formats = fields.iter().map(|f| {
let tag_ty =
syn::parse_str::<syn::Path>(&format!("::lofty::TagType::{}", &f.tag_type)).unwrap();
syn::parse_str::<syn::Path>(&format!("::lofty::tag::TagType::{}", &f.tag_type))
.unwrap();
let cfg_features = f.get_cfg_features();
let block = lookup.get(&*tag_ty.segments[2].ident.to_string()).unwrap();
let block = lookup.get(&*tag_ty.segments[3].ident.to_string()).unwrap();
quote! {
#( #cfg_features )*
@ -111,7 +112,7 @@ pub(crate) fn write_module(
quote! {
pub(crate) mod write {
#[allow(unused_variables)]
pub(crate) fn write_to(data: &mut ::std::fs::File, tag: &::lofty::Tag, write_options: ::lofty::config::WriteOptions) -> ::lofty::error::Result<()> {
pub(crate) fn write_to(data: &mut ::std::fs::File, tag: &::lofty::tag::Tag, write_options: ::lofty::config::WriteOptions) -> ::lofty::error::Result<()> {
match tag.tag_type() {
#( #applicable_formats )*
_ => crate::macros::err!(UnsupportedTag),

View file

@ -446,7 +446,7 @@ fn generate_audiofile_impl(file: &LoftyFile) -> syn::Result<proc_macro2::TokenSt
}
fn save_to(&self, file: &mut ::std::fs::File, write_options: ::lofty::config::WriteOptions) -> ::lofty::error::Result<()> {
use ::lofty::prelude::TagExt as _;
use ::lofty::tag::TagExt as _;
use ::std::io::Seek as _;
#save_to_body
}
@ -461,9 +461,9 @@ fn generate_audiofile_impl(file: &LoftyFile) -> syn::Result<proc_macro2::TokenSt
}
#[allow(unreachable_code, unused_variables)]
fn contains_tag_type(&self, tag_type: ::lofty::TagType) -> bool {
fn contains_tag_type(&self, tag_type: ::lofty::tag::TagType) -> bool {
match tag_type {
#( ::lofty::TagType::#tag_type => { #tag_exists_2 } ),*
#( ::lofty::tag::TagType::#tag_type => { #tag_exists_2 } ),*
_ => false
}
}
@ -539,7 +539,7 @@ fn generate_from_taggedfile_impl(file: &LoftyFile) -> proc_macro2::TokenStream {
#file_type_variant,
::lofty::properties::FileProperties::from(input.properties),
{
let mut tags: Vec<::lofty::Tag> = Vec::new();
let mut tags: Vec<::lofty::tag::Tag> = Vec::new();
#( #conditions )*
tags

View file

@ -1,8 +1,8 @@
use crate::ape::constants::INVALID_KEYS;
use crate::error::{LoftyError, Result};
use crate::macros::decode_err;
use crate::tag::item::{ItemValue, ItemValueRef, TagItem};
use crate::tag::TagType;
use crate::tag::item::ItemValueRef;
use crate::tag::{ItemValue, TagItem, TagType};
/// Represents an `APE` tag item
///

View file

@ -6,9 +6,9 @@ use crate::ape::tag::item::{ApeItem, ApeItemRef};
use crate::config::WriteOptions;
use crate::error::{LoftyError, Result};
use crate::id3::v2::util::pairs::{format_number_pair, set_number, NUMBER_PAIR_KEYS};
use crate::tag::item::{ItemKey, ItemValue, ItemValueRef, TagItem};
use crate::tag::{try_parse_year, Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::item::ItemValueRef;
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use std::borrow::Cow;
use std::fs::File;
@ -547,7 +547,7 @@ mod tests {
use crate::config::WriteOptions;
use crate::id3::v2::util::pairs::DEFAULT_NUMBER_IN_PAIR;
use crate::prelude::*;
use crate::{ItemValue, Tag, TagItem, TagType};
use crate::tag::{ItemValue, Tag, TagItem, TagType};
use std::io::Cursor;

View file

@ -4,7 +4,7 @@ use crate::ape::constants::{APE_PREAMBLE, INVALID_KEYS};
use crate::ape::header::{self, ApeHeader};
use crate::error::Result;
use crate::macros::{decode_err, err, try_vec};
use crate::tag::item::ItemValue;
use crate::tag::ItemValue;
use crate::util::text::utf8_decode;
use std::io::{Read, Seek, SeekFrom};

View file

@ -5,7 +5,7 @@
use crate::file::FileType;
use crate::id3::v2::FrameId;
use crate::tag::item::ItemKey;
use crate::tag::ItemKey;
use std::collections::TryReserveError;
use std::fmt::{Debug, Display, Formatter};

View file

@ -3,8 +3,7 @@ use super::file_type::FileType;
use crate::config::{ParseOptions, WriteOptions};
use crate::error::Result;
use crate::properties::FileProperties;
use crate::tag::{Tag, TagType};
use crate::traits::TagExt;
use crate::tag::{Tag, TagExt, TagType};
use std::fs::File;
use std::io::{Read, Seek};

View file

@ -16,7 +16,7 @@ use crate::id3::v2::tag::Id3v2Tag;
use crate::ogg::tag::VorbisCommentsRef;
use crate::ogg::{OggPictureStorage, VorbisComments};
use crate::picture::{Picture, PictureInformation};
use crate::traits::TagExt;
use crate::tag::TagExt;
use std::fs::File;
use std::io::Seek;

View file

@ -194,7 +194,7 @@ pub const GENRES: [&str; 192] = [
"Psybient",
];
use crate::tag::item::ItemKey;
use crate::tag::ItemKey;
pub(crate) const VALID_ITEMKEYS: [ItemKey; 7] = [
ItemKey::TrackTitle,
ItemKey::TrackArtist,

View file

@ -1,9 +1,8 @@
use crate::config::WriteOptions;
use crate::error::{LoftyError, Result};
use crate::id3::v1::constants::GENRES;
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::{Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::{ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use std::borrow::Cow;
use std::fs::File;
@ -443,7 +442,7 @@ mod tests {
use crate::config::WriteOptions;
use crate::id3::v1::Id3v1Tag;
use crate::prelude::*;
use crate::{Tag, TagType};
use crate::tag::{Tag, TagType};
#[test]
fn parse_id3v1() {

View file

@ -2,8 +2,7 @@ use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use crate::error::{Id3v2Error, Id3v2ErrorKind, LoftyError, Result};
use crate::tag::item::ItemKey;
use crate::tag::TagType;
use crate::tag::{ItemKey, TagType};
/// An `ID3v2` frame ID
///

View file

@ -11,8 +11,7 @@ use super::items::{
};
use super::util::upgrade::{upgrade_v2, upgrade_v3};
use crate::error::{ErrorKind, Id3v2Error, Id3v2ErrorKind, LoftyError, Result};
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::TagType;
use crate::tag::{ItemKey, ItemValue, TagItem, TagType};
use crate::util::text::TextEncoding;
use id::FrameId;

View file

@ -18,9 +18,8 @@ use crate::id3::v2::util::pairs::{
};
use crate::id3::v2::KeyValueFrame;
use crate::picture::{Picture, PictureType, TOMBSTONE_PICTURE};
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::{try_parse_year, Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use crate::util::text::{decode_text, TextDecodeOptions, TextEncoding};
use std::borrow::Cow;

View file

@ -1,4 +1,4 @@
use crate::tag::item::ItemKey;
use crate::tag::ItemKey;
pub(crate) const TIPL_MAPPINGS: &[(ItemKey, &str)] = &[
(ItemKey::Producer, "producer"),

View file

@ -1,6 +1,6 @@
//! Contains utilities for ID3v2 style number pairs
use crate::tag::item::{ItemKey, TagItem};
use crate::tag::{ItemKey, TagItem};
use std::fmt::Display;
@ -58,7 +58,7 @@ pub(crate) fn set_number<F: FnMut(u32)>(item: &TagItem, mut setter: F) {
#[cfg(test)]
mod tests {
use crate::id3::v2::util::pairs::set_number;
use crate::{ItemKey, ItemValue, TagItem};
use crate::tag::{ItemKey, ItemValue, TagItem};
#[test]
fn whitespace_in_number() {

View file

@ -2,9 +2,8 @@ use crate::config::WriteOptions;
use crate::error::{LoftyError, Result};
use crate::iff::chunk::Chunks;
use crate::macros::err;
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::{Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::{ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use std::borrow::Cow;
use std::fs::File;
@ -486,7 +485,7 @@ mod tests {
use crate::config::{ParseOptions, WriteOptions};
use crate::iff::aiff::{AIFFTextChunks, Comment};
use crate::prelude::*;
use crate::{ItemValue, Tag, TagItem, TagType};
use crate::tag::{ItemValue, Tag, TagItem, TagType};
use std::io::Cursor;

View file

@ -3,9 +3,8 @@ mod write;
use crate::config::WriteOptions;
use crate::error::{LoftyError, Result};
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::{try_parse_year, Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use std::borrow::Cow;
use std::fs::File;
@ -352,7 +351,7 @@ mod tests {
use crate::iff::chunk::Chunks;
use crate::iff::wav::RIFFInfoList;
use crate::prelude::*;
use crate::{Tag, TagType};
use crate::tag::{Tag, TagType};
use byteorder::LittleEndian;

View file

@ -159,7 +159,7 @@ pub(crate) mod picture;
mod probe;
pub mod properties;
pub mod resolve;
pub(crate) mod tag;
pub mod tag;
mod traits;
mod util;
@ -177,8 +177,6 @@ pub mod wavpack;
pub use crate::probe::{read_from, read_from_path, Probe};
pub use crate::picture::{MimeType, Picture, PictureType};
pub use crate::tag::{Tag, TagType};
pub use tag::item::{ItemKey, ItemValue, TagItem};
pub use util::text::TextEncoding;
pub use picture::PictureInformation;
@ -188,7 +186,7 @@ pub use lofty_attr::LoftyFile;
pub mod prelude {
//! A prelude for commonly used items in the library.
//!
//! This module is intended to be glob imported.
//! This module is intended to be wildcard imported.
//!
//! ```rust
//! use lofty::prelude::*;
@ -196,6 +194,6 @@ pub mod prelude {
pub use crate::error::LoftyError;
pub use crate::file::{AudioFile, TaggedFileExt};
pub use crate::tag::item::ItemKey;
pub use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
pub use crate::tag::{ItemKey, TagExt};
pub use crate::traits::{Accessor, MergeTag, SplitTag};
}

View file

@ -1,8 +1,7 @@
use crate::config::ParsingMode;
use crate::error::{ErrorKind, LoftyError, Result};
use crate::macros::{err, try_vec};
use crate::tag::item::ItemKey;
use crate::tag::TagType;
use crate::tag::{ItemKey, TagType};
use crate::util::text::utf8_decode;
use std::borrow::Cow;

View file

@ -9,9 +9,8 @@ use crate::config::WriteOptions;
use crate::error::LoftyError;
use crate::mp4::ilst::atom::AtomDataStorage;
use crate::picture::{Picture, PictureType, TOMBSTONE_PICTURE};
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::{try_parse_year, Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use atom::{AdvisoryRating, Atom, AtomData};
use std::borrow::Cow;
@ -767,7 +766,7 @@ mod tests {
use crate::prelude::*;
use crate::tag::utils::test_utils;
use crate::tag::utils::test_utils::read_path;
use crate::{ItemValue, Tag, TagItem, TagType};
use crate::tag::{ItemValue, Tag, TagItem, TagType};
use std::io::{Cursor, Read as _, Seek as _, Write as _};

View file

@ -6,9 +6,8 @@ use crate::ogg::picture_storage::OggPictureStorage;
use crate::ogg::write::OGGFormat;
use crate::picture::{Picture, PictureInformation};
use crate::probe::Probe;
use crate::tag::item::{ItemKey, ItemValue, TagItem};
use crate::tag::{try_parse_year, Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag};
use std::borrow::Cow;
use std::fs::File;
@ -696,7 +695,7 @@ mod tests {
use crate::config::{ParsingMode, WriteOptions};
use crate::ogg::{OggPictureStorage, VorbisComments};
use crate::prelude::*;
use crate::{ItemValue, Tag, TagItem, TagType};
use crate::tag::{ItemValue, Tag, TagItem, TagType};
fn read_tag(tag: &[u8]) -> VorbisComments {
let mut reader = std::io::Cursor::new(tag);

View file

@ -1,20 +1,27 @@
//! Utilities for generic tag handling
pub(crate) mod item;
mod tag_type;
mod tagext;
pub(crate) mod utils;
use crate::config::WriteOptions;
use crate::error::{LoftyError, Result};
use crate::file::FileType;
use crate::macros::err;
use crate::picture::{Picture, PictureType};
use crate::probe::Probe;
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use item::{ItemKey, ItemValue, TagItem};
use crate::traits::{Accessor, MergeTag, SplitTag};
use std::borrow::Cow;
use std::fs::{File, OpenOptions};
use std::fs::File;
use std::io::Write;
use std::path::Path;
// Exports
pub use item::{ItemKey, ItemValue, TagItem};
pub use tag_type::TagType;
pub use tagext::TagExt;
macro_rules! impl_accessor {
($($item_key:ident => $name:tt),+) => {
paste::paste! {
@ -581,6 +588,7 @@ impl TagExt for Tag {
}
#[derive(Debug, Clone, Default)]
#[allow(missing_docs)]
pub struct SplitTagRemainder;
impl SplitTag for Tag {
@ -599,74 +607,14 @@ impl MergeTag for SplitTagRemainder {
}
}
/// The tag's format
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum TagType {
/// This covers both APEv1 and APEv2 as it doesn't matter much
Ape,
/// Represents an ID3v1 tag
Id3v1,
/// This covers all ID3v2 versions since they all get upgraded to ID3v2.4
Id3v2,
/// Represents an MP4 ilst atom
Mp4Ilst,
/// Represents vorbis comments
VorbisComments,
/// Represents a RIFF INFO LIST
RiffInfo,
/// Represents AIFF text chunks
AiffText,
}
impl TagType {
/// Remove a tag from a [`Path`]
///
/// # Errors
///
/// See [`TagType::remove_from`]
pub fn remove_from_path(&self, path: impl AsRef<Path>) -> Result<()> {
let mut file = OpenOptions::new().read(true).write(true).open(path)?;
self.remove_from(&mut file)
}
#[allow(clippy::shadow_unrelated)]
/// Remove a tag from a [`File`]
///
/// # Errors
///
/// * It is unable to guess the file format
/// * The format doesn't support the tag
/// * It is unable to write to the file
pub fn remove_from(&self, file: &mut File) -> Result<()> {
let probe = Probe::new(file).guess_file_type()?;
let Some(file_type) = probe.file_type() else {
err!(UnknownFormat);
};
// TODO: This should not have to be manually updated
let special_exceptions = ((file_type == FileType::Ape
|| file_type == FileType::Mpc
|| file_type == FileType::Flac)
&& *self == TagType::Id3v2)
|| file_type == FileType::Mpc && *self == TagType::Id3v1;
if !special_exceptions && !file_type.supports_tag_type(*self) {
err!(UnsupportedTag);
}
let file = probe.into_inner();
utils::write_tag(&Tag::new(*self), file, file_type, WriteOptions::default()) // TODO
}
}
#[cfg(test)]
mod tests {
use super::try_parse_year;
use crate::config::WriteOptions;
use crate::prelude::*;
use crate::tag::utils::test_utils::read_path;
use crate::{Picture, PictureType, Tag, TagType};
use crate::tag::{Tag, TagType};
use crate::{Picture, PictureType};
use std::io::{Seek, Write};
use std::process::Command;

69
src/tag/tag_type.rs Normal file
View file

@ -0,0 +1,69 @@
use super::{utils, Tag};
use crate::config::WriteOptions;
use crate::file::FileType;
use crate::macros::err;
use crate::probe::Probe;
use std::fs::{File, OpenOptions};
use std::path::Path;
/// The tag's format
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum TagType {
/// This covers both APEv1 and APEv2 as it doesn't matter much
Ape,
/// Represents an ID3v1 tag
Id3v1,
/// This covers all ID3v2 versions since they all get upgraded to ID3v2.4
Id3v2,
/// Represents an MP4 ilst atom
Mp4Ilst,
/// Represents vorbis comments
VorbisComments,
/// Represents a RIFF INFO LIST
RiffInfo,
/// Represents AIFF text chunks
AiffText,
}
impl TagType {
/// Remove a tag from a [`Path`]
///
/// # Errors
///
/// See [`TagType::remove_from`]
pub fn remove_from_path(&self, path: impl AsRef<Path>) -> crate::error::Result<()> {
let mut file = OpenOptions::new().read(true).write(true).open(path)?;
self.remove_from(&mut file)
}
#[allow(clippy::shadow_unrelated)]
/// Remove a tag from a [`File`]
///
/// # Errors
///
/// * It is unable to guess the file format
/// * The format doesn't support the tag
/// * It is unable to write to the file
pub fn remove_from(&self, file: &mut File) -> crate::error::Result<()> {
let probe = Probe::new(file).guess_file_type()?;
let Some(file_type) = probe.file_type() else {
err!(UnknownFormat);
};
// TODO: This should not have to be manually updated
let special_exceptions = ((file_type == FileType::Ape
|| file_type == FileType::Mpc
|| file_type == FileType::Flac)
&& *self == TagType::Id3v2)
|| file_type == FileType::Mpc && *self == TagType::Id3v1;
if !special_exceptions && !file_type.supports_tag_type(*self) {
err!(UnsupportedTag);
}
let file = probe.into_inner();
utils::write_tag(&Tag::new(*self), file, file_type, WriteOptions::default()) // TODO
}
}

135
src/tag/tagext.rs Normal file
View file

@ -0,0 +1,135 @@
use crate::config::WriteOptions;
use crate::tag::Tag;
use crate::traits::Accessor;
use std::fs::File;
use std::path::Path;
/// A set of common methods between tags
///
/// This provides a set of methods to make interaction with all tags a similar
/// experience.
///
/// This can be implemented downstream to provide a familiar interface for custom tags.
pub trait TagExt: Accessor + Into<Tag> + Sized {
/// The associated error which can be returned from IO operations
type Err: From<std::io::Error>;
/// The type of key used in the tag for non-mutating functions
type RefKey<'a>
where
Self: 'a;
/// Returns the number of items in the tag
///
/// This will also include any extras, such as pictures.
///
/// # Example
///
/// ```rust
/// use lofty::{Accessor, ItemKey, Tag, TagExt};
/// # let tag_type = lofty::TagType::Id3v2;
///
/// let mut tag = Tag::new(tag_type);
/// assert_eq!(tag.len(), 0);
///
/// tag.set_artist(String::from("Foo artist"));
/// assert_eq!(tag.len(), 1);
/// ```
fn len(&self) -> usize;
/// Whether the tag contains an item with the key
///
/// # Example
///
/// ```rust
/// use lofty::{Accessor, ItemKey, Tag, TagExt};
/// # let tag_type = lofty::TagType::Id3v2;
///
/// let mut tag = Tag::new(tag_type);
/// assert!(tag.is_empty());
///
/// tag.set_artist(String::from("Foo artist"));
/// assert!(tag.contains(&ItemKey::TrackArtist));
/// ```
fn contains<'a>(&'a self, key: Self::RefKey<'a>) -> bool;
/// Whether the tag has any items
///
/// # Example
///
/// ```rust
/// use lofty::{Accessor, Tag, TagExt};
/// # let tag_type = lofty::TagType::Id3v2;
///
/// let mut tag = Tag::new(tag_type);
/// assert!(tag.is_empty());
///
/// tag.set_artist(String::from("Foo artist"));
/// assert!(!tag.is_empty());
/// ```
fn is_empty(&self) -> bool;
/// Save the tag to a path
///
/// # Errors
///
/// * Path doesn't exist
/// * Path is not writable
/// * See [`TagExt::save_to`]
fn save_to_path<P: AsRef<Path>>(
&self,
path: P,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err> {
self.save_to(
&mut std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)?,
write_options,
)
}
/// Save the tag to a [`File`]
///
/// # Errors
///
/// * The file format could not be determined
/// * Attempting to write a tag to a format that does not support it.
fn save_to(
&self,
file: &mut File,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err>;
#[allow(clippy::missing_errors_doc)]
/// Dump the tag to a writer
///
/// This will only write the tag, it will not produce a usable file.
fn dump_to<W: std::io::Write>(
&self,
writer: &mut W,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err>;
/// Remove a tag from a [`Path`]
///
/// # Errors
///
/// See [`TagExt::remove_from`]
fn remove_from_path<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), Self::Err>;
/// Remove a tag from a [`File`]
///
/// # Errors
///
/// * It is unable to guess the file format
/// * The format doesn't support the tag
/// * It is unable to write to the file
fn remove_from(&self, file: &mut File) -> std::result::Result<(), Self::Err>;
/// Clear the tag, removing all items
///
/// NOTE: This will **not** remove any format-specific extras, such as flags
fn clear(&mut self);
}

View file

@ -99,7 +99,7 @@ pub(crate) fn dump_tag<W: Write>(
#[cfg(test)]
// Used for tag conversion tests
pub(crate) mod test_utils {
use crate::{ItemKey, Tag, TagType};
use crate::tag::{ItemKey, Tag, TagType};
use std::fs::File;
use std::io::Read;

View file

@ -118,141 +118,8 @@ accessor_trait! {
[year ]<u32>, [comment ]<Cow<'_, str>, String>,
}
use crate::config::WriteOptions;
use crate::tag::Tag;
use std::fs::File;
use std::path::Path;
/// A set of common methods between tags
///
/// This provides a set of methods to make interaction with all tags a similar
/// experience.
///
/// This can be implemented downstream to provide a familiar interface for custom tags.
pub trait TagExt: Accessor + Into<Tag> + Sized {
/// The associated error which can be returned from IO operations
type Err: From<std::io::Error>;
/// The type of key used in the tag for non-mutating functions
type RefKey<'a>
where
Self: 'a;
/// Returns the number of items in the tag
///
/// This will also include any extras, such as pictures.
///
/// # Example
///
/// ```rust
/// use lofty::{Accessor, ItemKey, Tag, TagExt};
/// # let tag_type = lofty::TagType::Id3v2;
///
/// let mut tag = Tag::new(tag_type);
/// assert_eq!(tag.len(), 0);
///
/// tag.set_artist(String::from("Foo artist"));
/// assert_eq!(tag.len(), 1);
/// ```
fn len(&self) -> usize;
/// Whether the tag contains an item with the key
///
/// # Example
///
/// ```rust
/// use lofty::{Accessor, ItemKey, Tag, TagExt};
/// # let tag_type = lofty::TagType::Id3v2;
///
/// let mut tag = Tag::new(tag_type);
/// assert!(tag.is_empty());
///
/// tag.set_artist(String::from("Foo artist"));
/// assert!(tag.contains(&ItemKey::TrackArtist));
/// ```
fn contains<'a>(&'a self, key: Self::RefKey<'a>) -> bool;
/// Whether the tag has any items
///
/// # Example
///
/// ```rust
/// use lofty::{Accessor, Tag, TagExt};
/// # let tag_type = lofty::TagType::Id3v2;
///
/// let mut tag = Tag::new(tag_type);
/// assert!(tag.is_empty());
///
/// tag.set_artist(String::from("Foo artist"));
/// assert!(!tag.is_empty());
/// ```
fn is_empty(&self) -> bool;
/// Save the tag to a path
///
/// # Errors
///
/// * Path doesn't exist
/// * Path is not writable
/// * See [`TagExt::save_to`]
fn save_to_path<P: AsRef<Path>>(
&self,
path: P,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err> {
self.save_to(
&mut std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)?,
write_options,
)
}
/// Save the tag to a [`File`]
///
/// # Errors
///
/// * The file format could not be determined
/// * Attempting to write a tag to a format that does not support it.
fn save_to(
&self,
file: &mut File,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err>;
#[allow(clippy::missing_errors_doc)]
/// Dump the tag to a writer
///
/// This will only write the tag, it will not produce a usable file.
fn dump_to<W: std::io::Write>(
&self,
writer: &mut W,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err>;
/// Remove a tag from a [`Path`]
///
/// # Errors
///
/// See [`TagExt::remove_from`]
fn remove_from_path<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), Self::Err>;
/// Remove a tag from a [`File`]
///
/// # Errors
///
/// * It is unable to guess the file format
/// * The format doesn't support the tag
/// * It is unable to write to the file
fn remove_from(&self, file: &mut File) -> std::result::Result<(), Self::Err>;
/// Clear the tag, removing all items
///
/// NOTE: This will **not** remove any format-specific extras, such as flags
fn clear(&mut self);
}
/// Split (and merge) tags.
///
/// Useful and required for implementing lossless read/modify/write round trips.

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::ParseOptions;
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::ParseOptions;
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::ParseOptions;
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::ParseOptions;
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -3,7 +3,8 @@ use lofty::config::ParseOptions;
use lofty::file::{FileType, TaggedFile};
use lofty::musepack::MpcFile;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -4,7 +4,8 @@ use lofty::file::FileType;
use lofty::id3::v2::{Frame, FrameFlags, FrameId, FrameValue, Id3v2Tag, KeyValueFrame};
use lofty::mpeg::MpegFile;
use lofty::prelude::*;
use lofty::{Probe, Tag, TagType};
use lofty::tag::{Tag, TagType};
use lofty::Probe;
use std::borrow::Cow;
use std::io::{Seek, Write};

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::{ParseOptions, WriteOptions};
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -30,10 +30,10 @@ macro_rules! verify_artist {
assert_eq!(tag.item_count(), $item_count);
assert_eq!(
tag.get(&lofty::ItemKey::TrackArtist),
Some(&lofty::TagItem::new(
lofty::ItemKey::TrackArtist,
lofty::ItemValue::Text(String::from($expected_value))
tag.get(&lofty::prelude::ItemKey::TrackArtist),
Some(&lofty::tag::TagItem::new(
lofty::prelude::ItemKey::TrackArtist,
lofty::tag::ItemValue::Text(String::from($expected_value))
))
);
@ -62,9 +62,9 @@ macro_rules! set_artist {
set_artist!($file_write, $new_value, tag)
};
($file_write:ident, $new_value:literal, $tag:ident) => {
$tag.insert_unchecked(lofty::TagItem::new(
lofty::ItemKey::TrackArtist,
lofty::ItemValue::Text(String::from($new_value)),
$tag.insert_unchecked(lofty::tag::TagItem::new(
lofty::prelude::ItemKey::TrackArtist,
lofty::tag::ItemValue::Text(String::from($new_value)),
));
$file_write.rewind().unwrap();

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::ParseOptions;
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -2,7 +2,8 @@ use crate::{set_artist, temp_file, verify_artist};
use lofty::config::ParseOptions;
use lofty::file::FileType;
use lofty::prelude::*;
use lofty::{Probe, TagType};
use lofty::tag::TagType;
use lofty::Probe;
use std::io::{Seek, Write};

View file

@ -1,7 +1,8 @@
// Tests for special case conversions
use lofty::id3::v2::{CommentFrame, Frame, FrameFlags, FrameId, Id3v2Tag, UnsynchronizedTextFrame};
use lofty::{ItemKey, Tag, TagType, TextEncoding};
use lofty::tag::{ItemKey, Tag, TagType};
use lofty::TextEncoding;
use std::borrow::Cow;
#[test]