mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 06:34:18 +00:00
Create impl_tag attribute proc macro
This commit is contained in:
parent
2f9200a556
commit
0028651f70
5 changed files with 245 additions and 1 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
/target/
|
**/target/
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
|
@ -33,6 +33,10 @@ thiserror = "1.0.24"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
|
|
||||||
|
[dependencies.lofty_attr]
|
||||||
|
path = "./lofty-attr"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
full = ["all_tags", "duration"]
|
full = ["all_tags", "duration"]
|
||||||
|
|
46
lofty-attr/Cargo.lock
generated
Normal file
46
lofty-attr/Cargo.lock
generated
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lofty_attr"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
17
lofty-attr/Cargo.toml
Normal file
17
lofty-attr/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "lofty_attr"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Serial <69764315+Serial-ATA@users.noreply.github.com>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/Serial-ATA/lofty-rs"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0.9"
|
||||||
|
|
||||||
|
[dependencies.syn]
|
||||||
|
version = "1.0.72"
|
||||||
|
features = ["full"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
177
lofty-attr/src/lib.rs
Normal file
177
lofty-attr/src/lib.rs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, AttributeArgs, Error, ItemStruct, Meta, NestedMeta};
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub fn impl_tag(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as ItemStruct);
|
||||||
|
let args = parse_macro_input!(args as AttributeArgs);
|
||||||
|
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Error::new(
|
||||||
|
input.ident.span(),
|
||||||
|
"impl_tag requires an inner tag and TagType",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (NestedMeta::Meta(Meta::Path(inner)), NestedMeta::Meta(tag_type)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
|
if let Some(inner) = inner.get_ident() {
|
||||||
|
let input_ident = input.ident;
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct #input_ident {
|
||||||
|
inner: #inner,
|
||||||
|
#[cfg(feature = "duration")]
|
||||||
|
duration: Option<std::time::Duration>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for #input_ident {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: #inner::default(),
|
||||||
|
#[cfg(feature = "duration")]
|
||||||
|
duration: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #input_ident {
|
||||||
|
/// Creates a new default tag
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
impl ToAnyTag for #input_ident {
|
||||||
|
fn to_anytag(&self) -> AnyTag<'_> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAny for #input_ident {
|
||||||
|
fn to_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn to_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioTag for #input_ident {}
|
||||||
|
|
||||||
|
// From wrapper to inner (same type)
|
||||||
|
impl From<#input_ident> for #inner {
|
||||||
|
fn from(inp: #input_ident) -> Self {
|
||||||
|
inp.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From inner to wrapper (same type)
|
||||||
|
impl From<#inner> for #input_ident {
|
||||||
|
fn from(inp: #inner) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: inp,
|
||||||
|
#[cfg(feature = "duration")]
|
||||||
|
duration: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a #input_ident> for AnyTag<'a> {
|
||||||
|
fn from(inp: &'a #input_ident) -> Self {
|
||||||
|
Self {
|
||||||
|
title: inp.title(),
|
||||||
|
artists: inp.artists_vec(),
|
||||||
|
year: inp.year().map(|y| y as i32),
|
||||||
|
album: Album::new(
|
||||||
|
inp.album_title(),
|
||||||
|
inp.album_artists_vec(),
|
||||||
|
inp.album_covers(),
|
||||||
|
),
|
||||||
|
track_number: inp.track_number(),
|
||||||
|
total_tracks: inp.total_tracks(),
|
||||||
|
disc_number: inp.disc_number(),
|
||||||
|
total_discs: inp.total_discs(),
|
||||||
|
comments: None, // TODO
|
||||||
|
date: inp.date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<AnyTag<'a>> for #input_ident {
|
||||||
|
fn from(inp: AnyTag<'a>) -> Self {
|
||||||
|
let mut tag = #input_ident::default();
|
||||||
|
|
||||||
|
if let Some(v) = inp.title() {
|
||||||
|
tag.set_title(v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.artists_as_string() {
|
||||||
|
tag.set_artist(&v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.year {
|
||||||
|
tag.set_year(v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.album().title {
|
||||||
|
tag.set_album_title(v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.album().artists {
|
||||||
|
tag.set_album_artist(&v.join("/"))
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.track_number() {
|
||||||
|
tag.set_track_number(v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.total_tracks() {
|
||||||
|
tag.set_total_tracks(v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.disc_number() {
|
||||||
|
tag.set_disc_number(v)
|
||||||
|
}
|
||||||
|
if let Some(v) = inp.total_discs() {
|
||||||
|
tag.set_total_discs(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From dyn AudioTag to wrapper (any type)
|
||||||
|
impl From<Box<dyn AudioTag>> for #input_ident {
|
||||||
|
fn from(inp: Box<dyn AudioTag>) -> Self {
|
||||||
|
let mut inp = inp;
|
||||||
|
if let Some(t_refmut) = inp.to_any_mut().downcast_mut::<#input_ident>() {
|
||||||
|
let t = std::mem::replace(t_refmut, #input_ident::new()); // TODO: can we avoid creating the dummy tag?
|
||||||
|
t
|
||||||
|
} else {
|
||||||
|
let mut t = inp.to_dyn_tag(#tag_type);
|
||||||
|
let t_refmut = t.to_any_mut().downcast_mut::<#input_ident>().unwrap();
|
||||||
|
let t = std::mem::replace(t_refmut, #input_ident::new());
|
||||||
|
t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From dyn AudioTag to inner (any type)
|
||||||
|
impl From<Box<dyn AudioTag>> for #inner {
|
||||||
|
fn from(inp: Box<dyn AudioTag>) -> Self {
|
||||||
|
let t: #input_ident = inp.into();
|
||||||
|
t.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return TokenStream::from(expanded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::new(input.ident.span(), "impl_tag provided invalid arguments")
|
||||||
|
.to_compile_error()
|
||||||
|
.into()
|
||||||
|
}
|
Loading…
Reference in a new issue