mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 06:34:18 +00:00
Accessor: Move to lofty::tag
This commit is contained in:
parent
ca8a3cb5b9
commit
adea986132
15 changed files with 141 additions and 156 deletions
|
@ -27,7 +27,7 @@ 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**:
|
||||
- **Tag**/**Accessor**/**TagType**/**TagExt**/**TagItem**/**ItemKey**/**ItemValue**:
|
||||
- ⚠️ Important ⚠️: Moved to `lofty::tag` ([PR](https://github.com/Serial-ATA/lofty-rs/pull/374))
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -7,8 +7,8 @@ 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::ItemValueRef;
|
||||
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{try_parse_year, Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
|
@ -769,7 +769,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn track_number_tag_to_ape() {
|
||||
use crate::traits::Accessor;
|
||||
let track_number = 1;
|
||||
|
||||
let mut tag = Tag::new(TagType::Ape);
|
||||
|
@ -787,7 +786,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn track_total_tag_to_ape() {
|
||||
use crate::traits::Accessor;
|
||||
let track_total = 2;
|
||||
|
||||
let mut tag = Tag::new(TagType::Ape);
|
||||
|
@ -805,7 +803,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn track_number_and_track_total_tag_to_ape() {
|
||||
use crate::traits::Accessor;
|
||||
let track_number = 1;
|
||||
let track_total = 2;
|
||||
|
||||
|
@ -829,7 +826,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn disk_number_tag_to_ape() {
|
||||
use crate::traits::Accessor;
|
||||
let disk_number = 1;
|
||||
|
||||
let mut tag = Tag::new(TagType::Ape);
|
||||
|
@ -847,7 +843,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn disk_total_tag_to_ape() {
|
||||
use crate::traits::Accessor;
|
||||
let disk_total = 2;
|
||||
|
||||
let mut tag = Tag::new(TagType::Ape);
|
||||
|
@ -865,7 +860,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn disk_number_and_disk_total_tag_to_ape() {
|
||||
use crate::traits::Accessor;
|
||||
let disk_number = 1;
|
||||
let disk_total = 2;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::config::WriteOptions;
|
||||
use crate::error::{LoftyError, Result};
|
||||
use crate::id3::v1::constants::GENRES;
|
||||
use crate::tag::{ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
|
|
|
@ -18,8 +18,8 @@ use crate::id3::v2::util::pairs::{
|
|||
};
|
||||
use crate::id3::v2::KeyValueFrame;
|
||||
use crate::picture::{Picture, PictureType, TOMBSTONE_PICTURE};
|
||||
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{try_parse_year, Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
use crate::util::text::{decode_text, TextDecodeOptions, TextEncoding};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
|
|
@ -438,7 +438,6 @@ fn popm_frame() {
|
|||
|
||||
#[test]
|
||||
fn multi_value_frame_to_tag() {
|
||||
use crate::traits::Accessor;
|
||||
let mut tag = Id3v2Tag::default();
|
||||
|
||||
tag.set_artist(String::from("foo\0bar\0baz"));
|
||||
|
@ -450,7 +449,6 @@ fn multi_value_frame_to_tag() {
|
|||
|
||||
#[test]
|
||||
fn multi_item_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let mut tag = Tag::new(TagType::Id3v2);
|
||||
|
||||
tag.push_unchecked(TagItem::new(
|
||||
|
@ -823,7 +821,6 @@ fn set_disk_total_and_disk() {
|
|||
|
||||
#[test]
|
||||
fn track_number_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let track_number = 1;
|
||||
|
||||
let mut tag = Tag::new(TagType::Id3v2);
|
||||
|
@ -841,7 +838,6 @@ fn track_number_tag_to_id3v2() {
|
|||
|
||||
#[test]
|
||||
fn track_total_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let track_total = 2;
|
||||
|
||||
let mut tag = Tag::new(TagType::Id3v2);
|
||||
|
@ -859,7 +855,6 @@ fn track_total_tag_to_id3v2() {
|
|||
|
||||
#[test]
|
||||
fn track_number_and_track_total_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let track_number = 1;
|
||||
let track_total = 2;
|
||||
|
||||
|
@ -883,7 +878,6 @@ fn track_number_and_track_total_tag_to_id3v2() {
|
|||
|
||||
#[test]
|
||||
fn disk_number_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let disk_number = 1;
|
||||
|
||||
let mut tag = Tag::new(TagType::Id3v2);
|
||||
|
@ -901,7 +895,6 @@ fn disk_number_tag_to_id3v2() {
|
|||
|
||||
#[test]
|
||||
fn disk_total_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let disk_total = 2;
|
||||
|
||||
let mut tag = Tag::new(TagType::Id3v2);
|
||||
|
@ -919,7 +912,6 @@ fn disk_total_tag_to_id3v2() {
|
|||
|
||||
#[test]
|
||||
fn disk_number_and_disk_total_tag_to_id3v2() {
|
||||
use crate::traits::Accessor;
|
||||
let disk_number = 1;
|
||||
let disk_total = 2;
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::config::WriteOptions;
|
|||
use crate::error::{LoftyError, Result};
|
||||
use crate::iff::chunk::Chunks;
|
||||
use crate::macros::err;
|
||||
use crate::tag::{ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
|
|
|
@ -3,8 +3,8 @@ mod write;
|
|||
|
||||
use crate::config::WriteOptions;
|
||||
use crate::error::{LoftyError, Result};
|
||||
use crate::tag::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{try_parse_year, Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
|
|
|
@ -194,6 +194,6 @@ pub mod prelude {
|
|||
|
||||
pub use crate::error::LoftyError;
|
||||
pub use crate::file::{AudioFile, TaggedFileExt};
|
||||
pub use crate::tag::{ItemKey, TagExt};
|
||||
pub use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
pub use crate::tag::{Accessor, ItemKey, TagExt};
|
||||
pub use crate::traits::{MergeTag, SplitTag};
|
||||
}
|
||||
|
|
|
@ -9,8 +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::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{try_parse_year, Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
use atom::{AdvisoryRating, Atom, AtomData};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
|
|
@ -6,8 +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::{try_parse_year, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::tag::{try_parse_year, Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
|
|
|
@ -134,8 +134,7 @@ mod tests {
|
|||
use crate::id3::v2::Id3v2Tag;
|
||||
use crate::properties::FileProperties;
|
||||
use crate::resolve::{register_custom_resolver, FileResolver};
|
||||
use crate::tag::TagType;
|
||||
use crate::traits::Accessor;
|
||||
use crate::tag::{Accessor, TagType};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek};
|
||||
|
|
119
src/tag/accessor.rs
Normal file
119
src/tag/accessor.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
// This defines the `Accessor` trait, used to define unified getters/setters for commonly
|
||||
// accessed tag values.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// accessor_trait! {
|
||||
// [field_name]<type>
|
||||
// }
|
||||
//
|
||||
// * `field_name` is the name of the method to access the field. If a name consists of multiple segments,
|
||||
// such as `track_number`, they should be separated by spaces like so: [track number]<type>.
|
||||
//
|
||||
// * `type` is the return type for `Accessor::field_name`. By default, this type will also be used
|
||||
// in the setter.
|
||||
//
|
||||
// An owned type can also be specified for the setter:
|
||||
//
|
||||
// accessor_trait! {
|
||||
// field_name<type, owned_type>
|
||||
// }
|
||||
macro_rules! accessor_trait {
|
||||
($([$($name:tt)+] < $($ty:ty),+ >),+ $(,)?) => {
|
||||
/// Provides accessors for common items
|
||||
///
|
||||
/// This attempts to only provide methods for items that all tags have in common,
|
||||
/// but there may be exceptions.
|
||||
pub trait Accessor {
|
||||
$(
|
||||
accessor_trait! { @GETTER [$($name)+] $($ty),+ }
|
||||
|
||||
accessor_trait! { @SETTER [$($name)+] $($ty),+ }
|
||||
|
||||
accessor_trait! { @REMOVE [$($name)+] $($ty),+ }
|
||||
)+
|
||||
}
|
||||
};
|
||||
(@GETTER [$($name:tt)+] $ty:ty $(, $_ty:tt)?) => {
|
||||
accessor_trait! { @GET_METHOD [$($name)+] Option<$ty> }
|
||||
};
|
||||
(@SETTER [$($name:tt)+] $_ty:ty, $owned_ty:tt) => {
|
||||
accessor_trait! { @SETTER [$($name)+] $owned_ty }
|
||||
};
|
||||
(@SETTER [$($name:tt)+] $ty:ty) => {
|
||||
accessor_trait! { @SET_METHOD [$($name)+] $ty }
|
||||
};
|
||||
(@REMOVE [$($name:tt)+] $_ty:ty, $owned_ty:tt) => {
|
||||
accessor_trait! { @REMOVE [$($name)+] $owned_ty }
|
||||
};
|
||||
(@REMOVE [$($name:tt)+] $ty:ty) => {
|
||||
accessor_trait! { @REMOVE_METHOD [$($name)+], $ty }
|
||||
};
|
||||
(@GET_METHOD [$name:tt $($other:tt)*] Option<$ret_ty:ty>) => {
|
||||
paste::paste! {
|
||||
#[doc = "Returns the " $name $(" " $other)*]
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use lofty::{Tag, Accessor};
|
||||
///
|
||||
/// # let tag_type = lofty::TagType::Id3v2;
|
||||
/// let mut tag = Tag::new(tag_type);
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), None);"]
|
||||
/// ```
|
||||
fn [<
|
||||
$name $(_ $other)*
|
||||
>] (&self) -> Option<$ret_ty> { None }
|
||||
}
|
||||
};
|
||||
(@SET_METHOD [$name:tt $($other:tt)*] $owned_ty:ty) => {
|
||||
paste::paste! {
|
||||
#[doc = "Sets the " $name $(" " $other)*]
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use lofty::{Tag, Accessor};
|
||||
///
|
||||
/// let mut tag = Tag::new(tag_type);
|
||||
#[doc = "tag.set_" $name $(_ $other)* "(value);"]
|
||||
///
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), Some(value));"]
|
||||
/// ```
|
||||
fn [<
|
||||
set_ $name $(_ $other)*
|
||||
>] (&mut self , _value: $owned_ty) {}
|
||||
}
|
||||
};
|
||||
(@REMOVE_METHOD [$name:tt $($other:tt)*], $ty:ty) => {
|
||||
paste::paste! {
|
||||
#[doc = "Removes the " $name $(" " $other)*]
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use lofty::{Tag, Accessor};
|
||||
///
|
||||
/// let mut tag = Tag::new(tag_type);
|
||||
#[doc = "tag.set_" $name $(_ $other)* "(value);"]
|
||||
///
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), Some(value));"]
|
||||
///
|
||||
#[doc = "tag.remove_" $name $(_ $other)* "();"]
|
||||
///
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), None);"]
|
||||
/// ```
|
||||
fn [<
|
||||
remove_ $name $(_ $other)*
|
||||
>] (&mut self) {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
accessor_trait! {
|
||||
[artist]<Cow<'_, str>, String>, [title ]<Cow<'_, str>, String>,
|
||||
[album ]<Cow<'_, str>, String>, [genre ]<Cow<'_, str>, String>,
|
||||
[track ]<u32>, [track total]<u32>,
|
||||
[disk ]<u32>, [disk total ]<u32>,
|
||||
[year ]<u32>, [comment ]<Cow<'_, str>, String>,
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
//! Utilities for generic tag handling
|
||||
|
||||
mod accessor;
|
||||
pub(crate) mod item;
|
||||
mod tag_type;
|
||||
mod tagext;
|
||||
|
@ -10,7 +11,7 @@ use crate::error::{LoftyError, Result};
|
|||
use crate::macros::err;
|
||||
use crate::picture::{Picture, PictureType};
|
||||
use crate::probe::Probe;
|
||||
use crate::traits::{Accessor, MergeTag, SplitTag};
|
||||
use crate::traits::{MergeTag, SplitTag};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
|
@ -18,6 +19,7 @@ use std::io::Write;
|
|||
use std::path::Path;
|
||||
|
||||
// Exports
|
||||
pub use accessor::Accessor;
|
||||
pub use item::{ItemKey, ItemValue, TagItem};
|
||||
pub use tag_type::TagType;
|
||||
pub use tagext::TagExt;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::config::WriteOptions;
|
||||
use crate::tag::Tag;
|
||||
use crate::traits::Accessor;
|
||||
use crate::tag::{Accessor, Tag};
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
|
120
src/traits.rs
120
src/traits.rs
|
@ -1,123 +1,3 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
// This defines the `Accessor` trait, used to define unified getters/setters for commonly
|
||||
// accessed tag values.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// accessor_trait! {
|
||||
// [field_name]<type>
|
||||
// }
|
||||
//
|
||||
// * `field_name` is the name of the method to access the field. If a name consists of multiple segments,
|
||||
// such as `track_number`, they should be separated by spaces like so: [track number]<type>.
|
||||
//
|
||||
// * `type` is the return type for `Accessor::field_name`. By default, this type will also be used
|
||||
// in the setter.
|
||||
//
|
||||
// An owned type can also be specified for the setter:
|
||||
//
|
||||
// accessor_trait! {
|
||||
// field_name<type, owned_type>
|
||||
// }
|
||||
macro_rules! accessor_trait {
|
||||
($([$($name:tt)+] < $($ty:ty),+ >),+ $(,)?) => {
|
||||
/// Provides accessors for common items
|
||||
///
|
||||
/// This attempts to only provide methods for items that all tags have in common,
|
||||
/// but there may be exceptions.
|
||||
pub trait Accessor {
|
||||
$(
|
||||
accessor_trait! { @GETTER [$($name)+] $($ty),+ }
|
||||
|
||||
accessor_trait! { @SETTER [$($name)+] $($ty),+ }
|
||||
|
||||
accessor_trait! { @REMOVE [$($name)+] $($ty),+ }
|
||||
)+
|
||||
}
|
||||
};
|
||||
(@GETTER [$($name:tt)+] $ty:ty $(, $_ty:tt)?) => {
|
||||
accessor_trait! { @GET_METHOD [$($name)+] Option<$ty> }
|
||||
};
|
||||
(@SETTER [$($name:tt)+] $_ty:ty, $owned_ty:tt) => {
|
||||
accessor_trait! { @SETTER [$($name)+] $owned_ty }
|
||||
};
|
||||
(@SETTER [$($name:tt)+] $ty:ty) => {
|
||||
accessor_trait! { @SET_METHOD [$($name)+] $ty }
|
||||
};
|
||||
(@REMOVE [$($name:tt)+] $_ty:ty, $owned_ty:tt) => {
|
||||
accessor_trait! { @REMOVE [$($name)+] $owned_ty }
|
||||
};
|
||||
(@REMOVE [$($name:tt)+] $ty:ty) => {
|
||||
accessor_trait! { @REMOVE_METHOD [$($name)+], $ty }
|
||||
};
|
||||
(@GET_METHOD [$name:tt $($other:tt)*] Option<$ret_ty:ty>) => {
|
||||
paste::paste! {
|
||||
#[doc = "Returns the " $name $(" " $other)*]
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use lofty::{Tag, Accessor};
|
||||
///
|
||||
/// # let tag_type = lofty::TagType::Id3v2;
|
||||
/// let mut tag = Tag::new(tag_type);
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), None);"]
|
||||
/// ```
|
||||
fn [<
|
||||
$name $(_ $other)*
|
||||
>] (&self) -> Option<$ret_ty> { None }
|
||||
}
|
||||
};
|
||||
(@SET_METHOD [$name:tt $($other:tt)*] $owned_ty:ty) => {
|
||||
paste::paste! {
|
||||
#[doc = "Sets the " $name $(" " $other)*]
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use lofty::{Tag, Accessor};
|
||||
///
|
||||
/// let mut tag = Tag::new(tag_type);
|
||||
#[doc = "tag.set_" $name $(_ $other)* "(value);"]
|
||||
///
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), Some(value));"]
|
||||
/// ```
|
||||
fn [<
|
||||
set_ $name $(_ $other)*
|
||||
>] (&mut self , _value: $owned_ty) {}
|
||||
}
|
||||
};
|
||||
(@REMOVE_METHOD [$name:tt $($other:tt)*], $ty:ty) => {
|
||||
paste::paste! {
|
||||
#[doc = "Removes the " $name $(" " $other)*]
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use lofty::{Tag, Accessor};
|
||||
///
|
||||
/// let mut tag = Tag::new(tag_type);
|
||||
#[doc = "tag.set_" $name $(_ $other)* "(value);"]
|
||||
///
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), Some(value));"]
|
||||
///
|
||||
#[doc = "tag.remove_" $name $(_ $other)* "();"]
|
||||
///
|
||||
#[doc = "assert_eq!(tag." $name $(_ $other)* "(), None);"]
|
||||
/// ```
|
||||
fn [<
|
||||
remove_ $name $(_ $other)*
|
||||
>] (&mut self) {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
accessor_trait! {
|
||||
[artist]<Cow<'_, str>, String>, [title ]<Cow<'_, str>, String>,
|
||||
[album ]<Cow<'_, str>, String>, [genre ]<Cow<'_, str>, String>,
|
||||
[track ]<u32>, [track total]<u32>,
|
||||
[disk ]<u32>, [disk total ]<u32>,
|
||||
[year ]<u32>, [comment ]<Cow<'_, str>, String>,
|
||||
}
|
||||
|
||||
use crate::tag::Tag;
|
||||
|
||||
/// Split (and merge) tags.
|
||||
|
|
Loading…
Reference in a new issue