mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-11 21:22:39 +00:00
lofty_attr: Cleanup LoftyFile derive somewhat
This commit is contained in:
parent
53eb2184b8
commit
a35b35efd1
3 changed files with 169 additions and 129 deletions
|
@ -13,7 +13,7 @@ pub(crate) fn opt_internal_file_type(
|
|||
"AAC", "AIFF", "APE", "FLAC", "MPEG", "MP4", "Opus", "Vorbis", "Speex", "WAV", "WavPack",
|
||||
];
|
||||
|
||||
const ID3V2_STRIPPABLE: [&str; 1] = ["APE"];
|
||||
const ID3V2_STRIPPABLE: [&str; 2] = ["FLAC", "APE"];
|
||||
|
||||
let stripped = struct_name.strip_suffix("File");
|
||||
if let Some(prefix) = stripped {
|
||||
|
@ -95,7 +95,7 @@ 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::TagType::{}", &f.tag_type)).unwrap();
|
||||
|
||||
let features = f.cfg_features.iter();
|
||||
|
||||
|
@ -110,7 +110,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) -> lofty::error::Result<()> {
|
||||
pub(crate) fn write_to(data: &mut ::std::fs::File, tag: &::lofty::Tag) -> ::lofty::error::Result<()> {
|
||||
match tag.tag_type() {
|
||||
#( #applicable_formats )*
|
||||
_ => crate::macros::err!(UnsupportedTag),
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::util::{self, bail};
|
|||
use proc_macro2::{Ident, Span};
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Attribute, DataStruct, DeriveInput, Type};
|
||||
use syn::{Attribute, DataStruct, DeriveInput, Field, Type};
|
||||
|
||||
pub(crate) fn parse(
|
||||
input: &DeriveInput,
|
||||
|
@ -12,6 +12,7 @@ pub(crate) fn parse(
|
|||
errors: &mut Vec<syn::Error>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let impl_audiofile = should_impl_audiofile(&input.attrs);
|
||||
let struct_name = input.ident.clone();
|
||||
|
||||
let read_fn = match util::get_attr("read_fn", &input.attrs) {
|
||||
Some(rfn) => rfn,
|
||||
|
@ -30,8 +31,6 @@ pub(crate) fn parse(
|
|||
_ => proc_macro2::TokenStream::new(),
|
||||
};
|
||||
|
||||
let struct_name = input.ident.clone();
|
||||
|
||||
// TODO: This is not readable in the slightest
|
||||
|
||||
let opt_file_type = internal::opt_internal_file_type(struct_name.to_string());
|
||||
|
@ -42,7 +41,7 @@ pub(crate) fn parse(
|
|||
.iter()
|
||||
.any(|attr| util::has_path_attr(attr, "internal_write_module_do_not_use_anywhere_else"));
|
||||
|
||||
if opt_file_type.is_none() && is_internal {
|
||||
if !has_internal_file_type && is_internal {
|
||||
// TODO: This is the best check we can do for now I think?
|
||||
// Definitely needs some work when a better solution comes out.
|
||||
bail!(
|
||||
|
@ -87,138 +86,35 @@ pub(crate) fn parse(
|
|||
}
|
||||
});
|
||||
|
||||
let save_to_body;
|
||||
if write_fn.is_empty() {
|
||||
let tag_field_save = tag_fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.needs_option {
|
||||
quote! {
|
||||
file.rewind()?;
|
||||
if let Some(ref tag) = self.#name {
|
||||
tag.save_to(file)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
file.rewind()?;
|
||||
self.#name.save_to(file)?;
|
||||
}
|
||||
}
|
||||
});
|
||||
save_to_body = quote! {
|
||||
#(#tag_field_save)*
|
||||
Ok(())
|
||||
};
|
||||
} else {
|
||||
save_to_body = quote! {
|
||||
#write_fn(&self, file)
|
||||
}
|
||||
}
|
||||
|
||||
let tag_exists = tag_fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.needs_option {
|
||||
quote! { self.#name.is_some() }
|
||||
} else {
|
||||
quote! { true }
|
||||
}
|
||||
});
|
||||
let tag_exists_2 = tag_exists.clone();
|
||||
|
||||
let tag_type = tag_fields.iter().map(|f| &f.tag_type);
|
||||
|
||||
let properties_field = if let Some(field) = properties_field {
|
||||
field
|
||||
} else {
|
||||
bail!(errors, input.ident.span(), "Struct has no properties field");
|
||||
};
|
||||
|
||||
let properties_field_ty = &properties_field.ty;
|
||||
let assert_properties_impl = quote_spanned! {properties_field_ty.span()=>
|
||||
struct _AssertIntoFileProperties where #properties_field_ty: std::convert::Into<::lofty::FileProperties>;
|
||||
};
|
||||
|
||||
let audiofile_impl = if impl_audiofile {
|
||||
quote! {
|
||||
impl ::lofty::AudioFile for #struct_name {
|
||||
type Properties = #properties_field_ty;
|
||||
let properties_field = if let Some(field) = properties_field {
|
||||
field
|
||||
} else {
|
||||
bail!(errors, input.ident.span(), "Struct has no properties field");
|
||||
};
|
||||
|
||||
fn read_from<R>(reader: &mut R, parse_options: ::lofty::ParseOptions) -> ::lofty::error::Result<Self>
|
||||
where
|
||||
R: std::io::Read + std::io::Seek,
|
||||
{
|
||||
#read_fn(reader, parse_options)
|
||||
}
|
||||
|
||||
fn save_to(&self, file: &mut ::std::fs::File) -> ::lofty::error::Result<()> {
|
||||
use ::lofty::TagExt as _;
|
||||
use ::std::io::Seek as _;
|
||||
#save_to_body
|
||||
}
|
||||
|
||||
fn properties(&self) -> &Self::Properties {
|
||||
&self.properties
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
fn contains_tag(&self) -> bool {
|
||||
#( #tag_exists )||*
|
||||
}
|
||||
|
||||
#[allow(unreachable_code, unused_variables)]
|
||||
fn contains_tag_type(&self, tag_type: ::lofty::TagType) -> bool {
|
||||
match tag_type {
|
||||
#( ::lofty::TagType::#tag_type => { #tag_exists_2 } ),*
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
generate_audiofile_impl(
|
||||
&struct_name,
|
||||
&tag_fields,
|
||||
properties_field,
|
||||
read_fn,
|
||||
write_fn,
|
||||
)
|
||||
} else {
|
||||
proc_macro2::TokenStream::new()
|
||||
};
|
||||
|
||||
let conditions = tag_fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.needs_option {
|
||||
quote! { if let Some(t) = input.#name { tags.push(t.into()); } }
|
||||
} else {
|
||||
quote! { tags.push(input.#name.into()); }
|
||||
}
|
||||
});
|
||||
let from_taggedfile_impl =
|
||||
generate_from_taggedfile_impl(&struct_name, &tag_fields, file_type, has_internal_file_type);
|
||||
|
||||
let getters = get_getters(&tag_fields, &struct_name);
|
||||
|
||||
let file_type_variant = if has_internal_file_type {
|
||||
quote! { ::lofty::FileType::#file_type }
|
||||
} else {
|
||||
let file_ty_str = file_type.to_string();
|
||||
quote! { ::lofty::FileType::Custom(#file_ty_str) }
|
||||
};
|
||||
|
||||
let mut ret = quote! {
|
||||
#assert_properties_impl
|
||||
|
||||
#( #assert_tag_impl_into )*
|
||||
|
||||
#audiofile_impl
|
||||
|
||||
impl std::convert::From<#struct_name> for lofty::TaggedFile {
|
||||
fn from(input: #struct_name) -> Self {
|
||||
use ::lofty::TaggedFileExt as _;
|
||||
|
||||
lofty::TaggedFile::new(
|
||||
#file_type_variant,
|
||||
lofty::FileProperties::from(input.properties),
|
||||
{
|
||||
let mut tags: Vec<lofty::Tag> = Vec::new();
|
||||
#( #conditions )*
|
||||
|
||||
tags
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
#from_taggedfile_impl
|
||||
|
||||
#( #getters )*
|
||||
};
|
||||
|
@ -398,3 +294,147 @@ fn get_getters<'a>(
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_audiofile_impl(
|
||||
struct_name: &Ident,
|
||||
tag_fields: &[FieldContents],
|
||||
properties_field: &Field,
|
||||
read_fn: proc_macro2::TokenStream,
|
||||
write_fn: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let save_to_body = get_save_to_body(write_fn, &tag_fields);
|
||||
|
||||
let tag_exists = tag_exists_iter(&tag_fields);
|
||||
let tag_exists_2 = tag_exists_iter(&tag_fields);
|
||||
|
||||
let tag_type = tag_fields.iter().map(|f| &f.tag_type);
|
||||
|
||||
let properties_field_ty = &properties_field.ty;
|
||||
let assert_properties_impl = quote_spanned! {properties_field_ty.span()=>
|
||||
struct _AssertIntoFileProperties where #properties_field_ty: ::std::convert::Into<::lofty::FileProperties>;
|
||||
};
|
||||
|
||||
quote! {
|
||||
#assert_properties_impl
|
||||
impl ::lofty::AudioFile for #struct_name {
|
||||
type Properties = #properties_field_ty;
|
||||
|
||||
fn read_from<R>(reader: &mut R, parse_options: ::lofty::ParseOptions) -> ::lofty::error::Result<Self>
|
||||
where
|
||||
R: std::io::Read + std::io::Seek,
|
||||
{
|
||||
#read_fn(reader, parse_options)
|
||||
}
|
||||
|
||||
fn save_to(&self, file: &mut ::std::fs::File) -> ::lofty::error::Result<()> {
|
||||
use ::lofty::TagExt as _;
|
||||
use ::std::io::Seek as _;
|
||||
#save_to_body
|
||||
}
|
||||
|
||||
fn properties(&self) -> &Self::Properties {
|
||||
&self.properties
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
fn contains_tag(&self) -> bool {
|
||||
#( #tag_exists )||*
|
||||
}
|
||||
|
||||
#[allow(unreachable_code, unused_variables)]
|
||||
fn contains_tag_type(&self, tag_type: ::lofty::TagType) -> bool {
|
||||
match tag_type {
|
||||
#( ::lofty::TagType::#tag_type => { #tag_exists_2 } ),*
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_save_to_body(
|
||||
write_fn: proc_macro2::TokenStream,
|
||||
tag_fields: &[FieldContents],
|
||||
) -> proc_macro2::TokenStream {
|
||||
if !write_fn.is_empty() {
|
||||
return quote! {
|
||||
#write_fn(&self, file)
|
||||
};
|
||||
}
|
||||
|
||||
let tag_field_save = tag_fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.needs_option {
|
||||
quote! {
|
||||
file.rewind()?;
|
||||
if let Some(ref tag) = self.#name {
|
||||
tag.save_to(file)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
file.rewind()?;
|
||||
self.#name.save_to(file)?;
|
||||
}
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
#(#tag_field_save)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn tag_exists_iter(
|
||||
tag_fields: &[FieldContents],
|
||||
) -> impl Iterator<Item = proc_macro2::TokenStream> + '_ {
|
||||
tag_fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.needs_option {
|
||||
quote! { self.#name.is_some() }
|
||||
} else {
|
||||
quote! { true }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_from_taggedfile_impl(
|
||||
struct_name: &Ident,
|
||||
tag_fields: &[FieldContents],
|
||||
file_type: proc_macro2::TokenStream,
|
||||
has_internal_file_type: bool,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let conditions = tag_fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.needs_option {
|
||||
quote! { if let Some(t) = input.#name { tags.push(t.into()); } }
|
||||
} else {
|
||||
quote! { tags.push(input.#name.into()); }
|
||||
}
|
||||
});
|
||||
|
||||
let file_type_variant = if has_internal_file_type {
|
||||
quote! { ::lofty::FileType::#file_type }
|
||||
} else {
|
||||
let file_ty_str = file_type.to_string();
|
||||
quote! { ::lofty::FileType::Custom(#file_ty_str) }
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl ::std::convert::From<#struct_name> for ::lofty::TaggedFile {
|
||||
fn from(input: #struct_name) -> Self {
|
||||
use ::lofty::TaggedFileExt as _;
|
||||
|
||||
::lofty::TaggedFile::new(
|
||||
#file_type_variant,
|
||||
::lofty::FileProperties::from(input.properties),
|
||||
{
|
||||
let mut tags: Vec<::lofty::Tag> = Vec::new();
|
||||
#( #conditions )*
|
||||
|
||||
tags
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,12 +109,12 @@ pub(crate) fn parse(
|
|||
#input
|
||||
|
||||
impl #ident {
|
||||
pub(crate) const SUPPORTED_FORMATS: &'static [lofty::FileType] = &[
|
||||
#( lofty::FileType:: #flattened_file_types ),*
|
||||
pub(crate) const SUPPORTED_FORMATS: &'static [::lofty::FileType] = &[
|
||||
#( ::lofty::FileType:: #flattened_file_types ),*
|
||||
];
|
||||
|
||||
pub(crate) const READ_ONLY_FORMATS: &'static [lofty::FileType] = &[
|
||||
#( lofty::FileType:: #read_only_file_types ),*
|
||||
pub(crate) const READ_ONLY_FORMATS: &'static [::lofty::FileType] = &[
|
||||
#( ::lofty::FileType:: #read_only_file_types ),*
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue