diff --git a/src/logic/id3/v2/restrictions.rs b/src/logic/id3/v2/restrictions.rs new file mode 100644 index 00000000..c4db195f --- /dev/null +++ b/src/logic/id3/v2/restrictions.rs @@ -0,0 +1,80 @@ +#[derive(Clone, Copy)] +#[allow(non_camel_case_types)] +/// Restrictions on the tag size +pub enum TagSizeRestrictions { + /// No more than 128 frames and 1 MB total tag size + S_128F_1M, + /// No more than 64 frames and 128 KB total tag size + S_64F_128K, + /// No more than 32 frames and 40 KB total tag size + S_32F_40K, + /// No more than 32 frames and 4 KB total tag size + S_32F_4K, +} + +impl Default for TagSizeRestrictions { + fn default() -> Self { + Self::S_128F_1M + } +} + +#[derive(Clone, Copy)] +#[allow(non_camel_case_types)] +/// Restrictions on text field sizes +pub enum TextSizeRestrictions { + /// No size restrictions + None, + /// No longer than 1024 characters + C_1024, + /// No longer than 128 characters + C_128, + /// No longer than 30 characters + C_30, +} + +impl Default for TextSizeRestrictions { + fn default() -> Self { + Self::None + } +} + +#[derive(Clone, Copy)] +#[allow(non_camel_case_types)] +/// Restrictions on all image sizes +pub enum ImageSizeRestrictions { + /// No size restrictions + None, + /// All images are 256x256 or smaller + P_256, + /// All images are 64x64 or smaller + P_64, + /// All images are **exactly** 64x64 + P_64_64, +} + +impl Default for ImageSizeRestrictions { + fn default() -> Self { + Self::None + } +} + +#[derive(Default, Clone, Copy)] +/// Restrictions on the content of an ID3v2 tag +pub struct TagRestrictions { + /// Restriction on the size of the tag. See [`TagSizeRestrictions`] + pub size: TagSizeRestrictions, + /// Text encoding restrictions + /// + /// `false` - No restrictions + /// `true` - Strings are only encoded with [`TextEncoding::Latin1`](crate::TextEncoding::Latin1) or [`TextEncoding::UTF8`](crate::TextEncoding::UTF8) + pub text_encoding: bool, + /// Restrictions on all text field sizes. See [`TextSizeRestrictions`] + pub text_fields_size: TextSizeRestrictions, + /// Image encoding restrictions + /// + /// `false` - No restrictions + /// `true` - Images can only be `PNG` or `JPEG` + pub image_encoding: bool, + /// Restrictions on all image sizes. See [`ImageSizeRestrictions`] + pub image_size: ImageSizeRestrictions, +} diff --git a/src/types/item.rs b/src/types/item.rs index e05e3b13..d891ed66 100644 --- a/src/types/item.rs +++ b/src/types/item.rs @@ -22,7 +22,7 @@ macro_rules! first_key { // Keys should appear in order of popularity. macro_rules! item_keys { (ALLOWED_UNKNOWN => [$($unknown_tag_type:pat),+]; $($variant:ident => [$($($tag_type:pat)|* => $($key:tt)|+),+]),+) => { - #[derive(PartialEq)] + #[derive(PartialEq, Clone)] #[allow(missing_docs)] #[non_exhaustive] /// A generic representation of a tag's key @@ -44,7 +44,7 @@ macro_rules! item_keys { /// /// NOTE: If used with ID3v2, this will only check against the ID3v2.4 keys. /// If you wish to use a V2 or V3 key, see [`upgrade_v2`](crate::id3::upgrade_v2) and [`upgrade_v3`](crate::id3::upgrade_v3) - pub const fn from_key(tag_type: &TagType, key: &str) -> Option { + pub fn from_key(tag_type: &TagType, key: &str) -> Option { match tag_type { $( $( @@ -62,7 +62,7 @@ macro_rules! item_keys { /// /// NOTE: Since all ID3v2 tags are upgraded to [`Id3v2Version::V4`](crate::id3::Id3v2Version), the /// version provided does not matter. They cannot be downgraded. - pub const fn map_key(&self, tag_type: &TagType) -> Option<&str> { + pub fn map_key(&self, tag_type: &TagType) -> Option<&str> { match (tag_type, self) { $( $( diff --git a/src/types/tag.rs b/src/types/tag.rs index 858fe7f3..a2a07509 100644 --- a/src/types/tag.rs +++ b/src/types/tag.rs @@ -1,5 +1,6 @@ use super::item::ItemKey; use super::picture::{Picture, PictureType}; +use crate::logic::id3::v2::restrictions::TagRestrictions; use crate::logic::id3::v2::Id3v2Version; #[cfg(feature = "quick_tag_accessors")] @@ -13,7 +14,7 @@ macro_rules! common_items { $( #[doc = "Gets the " $name] pub fn $name(&self) -> Option<&str> { - if let Some(ItemValue::Text(txt)) = self.get_item_ref(&ItemKey::$item_key).map(|i| i.value()) { + if let Some(ItemValue::Text(txt)) = self.get_item_ref(&ItemKey::$item_key).map(TagItem::value) { return Some(&*txt) } @@ -35,6 +36,7 @@ macro_rules! common_items { } } +#[derive(Clone)] #[allow(clippy::struct_excessive_bools)] /// **(ID3v2/APEv2 ONLY)** Various flags to describe the content of an item /// @@ -93,6 +95,7 @@ impl Default for TagItemFlags { } } +#[derive(Clone)] /// Represents a tag item (key/value) pub struct TagItem { item_key: ItemKey, @@ -149,11 +152,12 @@ impl TagItem { &self.flags } - pub(crate) fn re_map(self, tag_type: &TagType) -> Option { - self.item_key.map_key(tag_type).is_some().then(|| self) + pub(crate) fn re_map(&self, tag_type: &TagType) -> Option<()> { + self.item_key.map_key(tag_type).is_some().then(|| ()) } } +#[derive(Clone)] /// Represents a tag item's value /// /// NOTES: @@ -166,10 +170,43 @@ pub enum ItemValue { Text(String), /// **(APE/ID3v2 ONLY)** Any UTF-8 encoded locator of external information Locator(String), - /// **(APE ONLY)** Binary information, most likely a picture + /// **(APE/ID3v2 ONLY)** Binary information + /// + /// In the case of ID3v2, this is the type of a [`Id3v2Frame::EncapsulatedObject`](crate::id3::Id3v2Frame::EncapsulatedObject) **and** any unknown frame. + /// + /// For APEv2, no uses of this item type are documented, there's no telling what it could be. Binary(Vec), + /// **(ID3v2 ONLY)** The content of a synchronized text frame, see [`SynchronizedText`](crate::id3::SynchronizedText) + SynchronizedText(Vec<(u32, String)>), } +#[derive(Default, Copy, Clone)] +#[allow(clippy::struct_excessive_bools)] +/// **(ID3v2 ONLY)** Flags that apply to the entire tag +pub struct TagFlags { + /// Whether or not all frames are unsynchronised. See [`TagItemFlags::unsynchronization`] + pub unsynchronisation: bool, + /// Whether or not the header is followed by an extended header + pub extended_header: bool, + /// Indicates if the tag is in an experimental stage + pub experimental: bool, + /// Indicates that the tag includes a footer + pub footer: bool, + /// Whether or not to include a CRC-32 in the extended header + /// + /// NOTE: This **requires** `extended_header` to be set. Otherwise, it will be ignored. + /// + /// This is calculated if the tag is written + pub crc: bool, + /// Restrictions on the tag + /// + /// NOTE: This **requires** `extended_header` to be set. Otherwise, it will be ignored. + /// + /// In addition to being setting this flag, all restrictions must be provided. See [`TagRestrictions`] + pub restrictions: (bool, TagRestrictions), +} + +#[derive(Clone)] /// Represents a parsed tag /// /// NOTE: Items and pictures are separated @@ -177,6 +214,7 @@ pub struct Tag { tag_type: TagType, pictures: Vec, items: Vec, + flags: TagFlags, } impl IntoIterator for Tag { @@ -222,6 +260,7 @@ impl Tag { tag_type, pictures: vec![], items: vec![], + flags: TagFlags::default() } } } @@ -299,7 +338,7 @@ impl Tag { /// When dealing with ID3v2, it may be necessary to use [`insert_item_unchecked`](Tag::insert_item_unchecked). /// See [`id3`](crate::id3) for an explanation. pub fn insert_item(&mut self, item: TagItem) -> bool { - if let Some(item) = item.re_map(&self.tag_type) { + if item.re_map(&self.tag_type).is_some() { self.insert_item_unchecked(item); return true; }