mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 06:34:18 +00:00
lofty_attr: Generate internal write modules
This commit is contained in:
parent
364b9a741c
commit
3b58f16996
10 changed files with 165 additions and 145 deletions
121
lofty_attr/src/internal.rs
Normal file
121
lofty_attr/src/internal.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Items that only pertain to internal usage of lofty_attr
|
||||
|
||||
use crate::FieldContents;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn opt_internal_file_type(
|
||||
struct_name: String,
|
||||
) -> Option<(proc_macro2::TokenStream, bool)> {
|
||||
const LOFTY_FILE_TYPES: [&str; 10] = [
|
||||
"AIFF", "APE", "FLAC", "MPEG", "MP4", "Opus", "Vorbis", "Speex", "WAV", "WavPack",
|
||||
];
|
||||
|
||||
const ID3V2_STRIPPABLE: [&str; 1] = ["APE"];
|
||||
|
||||
let stripped = struct_name.strip_suffix("File");
|
||||
if let Some(prefix) = stripped {
|
||||
if let Some(pos) = LOFTY_FILE_TYPES
|
||||
.iter()
|
||||
.position(|p| p.eq_ignore_ascii_case(prefix))
|
||||
{
|
||||
let file_ty = LOFTY_FILE_TYPES[pos];
|
||||
let tt = file_ty.parse::<proc_macro2::TokenStream>().unwrap();
|
||||
|
||||
return Some((tt, ID3V2_STRIPPABLE.contains(&file_ty)));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn init_write_lookup(
|
||||
id3v2_strippable: bool,
|
||||
) -> HashMap<&'static str, proc_macro2::TokenStream> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
macro_rules! insert {
|
||||
($map:ident, $key:path, $val:block) => {
|
||||
$map.insert(stringify!($key), quote! { $val })
|
||||
};
|
||||
}
|
||||
|
||||
insert!(map, APE, {
|
||||
crate::ape::tag::ApeTagRef {
|
||||
read_only: false,
|
||||
items: crate::ape::tag::tagitems_into_ape(tag.items()),
|
||||
}
|
||||
.write_to(data)
|
||||
});
|
||||
|
||||
insert!(map, ID3v1, {
|
||||
Into::<crate::id3::v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(data)
|
||||
});
|
||||
|
||||
if id3v2_strippable {
|
||||
insert!(map, ID3v2, {
|
||||
crate::id3::v2::tag::Id3v2TagRef::empty().write_to(data)
|
||||
});
|
||||
} else {
|
||||
insert!(map, ID3v2, {
|
||||
crate::id3::v2::tag::Id3v2TagRef {
|
||||
flags: crate::id3::v2::ID3v2TagFlags::default(),
|
||||
frames: crate::id3::v2::tag::tag_frames(tag),
|
||||
}
|
||||
.write_to(data)
|
||||
});
|
||||
}
|
||||
|
||||
insert!(map, RIFFInfo, {
|
||||
crate::iff::wav::tag::RIFFInfoListRef::new(crate::iff::wav::tag::tagitems_into_riff(
|
||||
tag.items(),
|
||||
))
|
||||
.write_to(data)
|
||||
});
|
||||
|
||||
insert!(map, AIFFText, {
|
||||
crate::iff::aiff::tag::AiffTextChunksRef {
|
||||
name: tag.get_string(&crate::tag::item::ItemKey::TrackTitle),
|
||||
author: tag.get_string(&crate::tag::item::ItemKey::TrackArtist),
|
||||
copyright: tag.get_string(&crate::tag::item::ItemKey::CopyrightMessage),
|
||||
annotations: Some(tag.get_strings(&crate::tag::item::ItemKey::Comment)),
|
||||
comments: None,
|
||||
}
|
||||
.write_to(data)
|
||||
});
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
pub(crate) fn write_module(
|
||||
fields: &[FieldContents],
|
||||
lookup: HashMap<&'static str, proc_macro2::TokenStream>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let applicable_formats = fields.iter().map(|f| {
|
||||
let tag_ty =
|
||||
syn::parse_str::<syn::Path>(&format!("crate::tag::TagType::{}", &f.tag_type)).unwrap();
|
||||
|
||||
let features = f.cfg_features.iter();
|
||||
|
||||
let block = lookup.get(&*tag_ty.segments[3].ident.to_string()).unwrap();
|
||||
|
||||
quote! {
|
||||
#( #features )*
|
||||
#tag_ty => #block,
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
pub(crate) mod write {
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn write_to(data: &mut std::fs::File, tag: &crate::tag::Tag) -> crate::error::Result<()> {
|
||||
match tag.tag_type() {
|
||||
#( #applicable_formats )*
|
||||
_ => crate::macros::err!(UnsupportedTag),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod internal;
|
||||
mod util;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
@ -6,10 +7,6 @@ use quote::{format_ident, quote, quote_spanned, ToTokens};
|
|||
use syn::spanned::Spanned;
|
||||
use syn::{parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, Ident, Type};
|
||||
|
||||
const LOFTY_FILE_TYPES: [&str; 10] = [
|
||||
"AIFF", "APE", "FLAC", "MPEG", "MP4", "Opus", "Vorbis", "Speex", "WAV", "WavPack",
|
||||
];
|
||||
|
||||
/// Creates a file usable by Lofty
|
||||
///
|
||||
/// See [here](https://github.com/Serial-ATA/lofty-rs/tree/main/examples/custom_resolver) for an example of how to use it.
|
||||
|
@ -68,9 +65,30 @@ fn parse(input: DeriveInput, errors: &mut Vec<syn::Error>) -> proc_macro2::Token
|
|||
};
|
||||
|
||||
let struct_name = input.ident.clone();
|
||||
let is_internal = input
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| util::has_path_attr(attr, "internal_write_module_do_not_use_anywhere_else"));
|
||||
|
||||
let file_type = match opt_file_type(struct_name.to_string()) {
|
||||
Some(ft) => ft,
|
||||
// TODO: This is not readable in the slightest
|
||||
|
||||
let opt_file_type = internal::opt_internal_file_type(struct_name.to_string());
|
||||
if opt_file_type.is_none() && 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!(
|
||||
errors,
|
||||
input.ident.span(),
|
||||
"Attempted to use an internal attribute externally"
|
||||
);
|
||||
}
|
||||
|
||||
let mut id3v2_strippable = false;
|
||||
let file_type = match opt_file_type {
|
||||
Some((ft, id3v2_strip)) => {
|
||||
id3v2_strippable = id3v2_strip;
|
||||
ft
|
||||
},
|
||||
_ => match util::get_attr("file_type", &input.attrs) {
|
||||
Some(rfn) => rfn,
|
||||
_ => {
|
||||
|
@ -168,7 +186,7 @@ fn parse(input: DeriveInput, errors: &mut Vec<syn::Error>) -> proc_macro2::Token
|
|||
|
||||
let getters = get_getters(&tag_fields, &struct_name);
|
||||
|
||||
quote! {
|
||||
let mut ret = quote! {
|
||||
#assert_properties_impl
|
||||
|
||||
#( #assert_tag_impl_into )*
|
||||
|
@ -191,7 +209,21 @@ fn parse(input: DeriveInput, errors: &mut Vec<syn::Error>) -> proc_macro2::Token
|
|||
}
|
||||
|
||||
#( #getters )*
|
||||
};
|
||||
|
||||
// Create `write` module if internal
|
||||
if is_internal {
|
||||
let lookup = internal::init_write_lookup(id3v2_strippable);
|
||||
let write_mod = internal::write_module(&tag_fields, lookup);
|
||||
|
||||
ret = quote! {
|
||||
#ret
|
||||
|
||||
#write_mod
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
struct FieldContents {
|
||||
|
@ -252,24 +284,6 @@ fn get_fields<'a>(
|
|||
Some((tag_fields, properties_field))
|
||||
}
|
||||
|
||||
fn opt_file_type(struct_name: String) -> Option<proc_macro2::TokenStream> {
|
||||
let stripped = struct_name.strip_suffix("File");
|
||||
if let Some(prefix) = stripped {
|
||||
if let Some(pos) = LOFTY_FILE_TYPES
|
||||
.iter()
|
||||
.position(|p| p.eq_ignore_ascii_case(prefix))
|
||||
{
|
||||
return Some(
|
||||
LOFTY_FILE_TYPES[pos]
|
||||
.parse::<proc_macro2::TokenStream>()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn should_impl_audiofile(attrs: &[Attribute]) -> bool {
|
||||
for attr in attrs {
|
||||
if util::has_path_attr(attr, "no_audiofile_impl") {
|
||||
|
|
|
@ -9,7 +9,6 @@ pub(crate) mod constants;
|
|||
pub(crate) mod header;
|
||||
mod properties;
|
||||
mod read;
|
||||
pub(crate) mod write;
|
||||
|
||||
#[cfg(feature = "id3v1")]
|
||||
use crate::id3::v1::tag::ID3v1Tag;
|
||||
|
@ -35,6 +34,7 @@ pub use properties::ApeProperties;
|
|||
/// An APE file
|
||||
#[derive(LoftyFile)]
|
||||
#[lofty(read_fn = "read::read_from")]
|
||||
#[lofty(internal_write_module_do_not_use_anywhere_else)]
|
||||
pub struct ApeFile {
|
||||
/// An ID3v1 tag
|
||||
#[cfg(feature = "id3v1")]
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#[cfg(feature = "ape")]
|
||||
use crate::ape;
|
||||
use crate::error::Result;
|
||||
#[cfg(feature = "id3v1")]
|
||||
use crate::id3::v1;
|
||||
#[cfg(feature = "id3v2")]
|
||||
use crate::id3::v2;
|
||||
use crate::macros::err;
|
||||
#[allow(unused_imports)]
|
||||
use crate::tag::{Tag, TagType};
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn write_to(data: &mut File, tag: &Tag) -> Result<()> {
|
||||
match tag.tag_type() {
|
||||
#[cfg(feature = "ape")]
|
||||
TagType::APE => ape::tag::ApeTagRef {
|
||||
read_only: false,
|
||||
items: ape::tag::tagitems_into_ape(tag.items()),
|
||||
}
|
||||
.write_to(data),
|
||||
// This tag can *only* be removed in this format
|
||||
#[cfg(feature = "id3v2")]
|
||||
TagType::ID3v2 => v2::tag::Id3v2TagRef::empty().write_to(data),
|
||||
#[cfg(feature = "id3v1")]
|
||||
TagType::ID3v1 => Into::<v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(data),
|
||||
_ => err!(UnsupportedTag),
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
mod properties;
|
||||
mod read;
|
||||
pub(crate) mod write;
|
||||
|
||||
#[cfg(feature = "id3v2")]
|
||||
use crate::id3::v2::tag::ID3v2Tag;
|
||||
|
@ -18,6 +17,7 @@ cfg_if::cfg_if! {
|
|||
/// An AIFF file
|
||||
#[derive(LoftyFile)]
|
||||
#[lofty(read_fn = "read::read_from")]
|
||||
#[lofty(internal_write_module_do_not_use_anywhere_else)]
|
||||
pub struct AiffFile {
|
||||
/// Any text chunks included in the file
|
||||
#[cfg(feature = "aiff_text_chunks")]
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
use crate::error::Result;
|
||||
#[cfg(feature = "id3v2")]
|
||||
use crate::id3::v2;
|
||||
use crate::macros::err;
|
||||
#[allow(unused_imports)]
|
||||
use crate::tag::{Tag, TagType};
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn write_to(data: &mut File, tag: &Tag) -> Result<()> {
|
||||
match tag.tag_type() {
|
||||
#[cfg(feature = "aiff_text_chunks")]
|
||||
TagType::AIFFText => {
|
||||
use crate::tag::item::ItemKey;
|
||||
|
||||
super::tag::AiffTextChunksRef {
|
||||
name: tag.get_string(&ItemKey::TrackTitle),
|
||||
author: tag.get_string(&ItemKey::TrackArtist),
|
||||
copyright: tag.get_string(&ItemKey::CopyrightMessage),
|
||||
annotations: Some(tag.get_strings(&ItemKey::Comment)),
|
||||
comments: None,
|
||||
}
|
||||
}
|
||||
.write_to(data),
|
||||
#[cfg(feature = "id3v2")]
|
||||
TagType::ID3v2 => v2::tag::Id3v2TagRef {
|
||||
flags: v2::ID3v2TagFlags::default(),
|
||||
frames: v2::tag::tag_frames(tag),
|
||||
}
|
||||
.write_to(data),
|
||||
_ => err!(UnsupportedTag),
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
mod properties;
|
||||
mod read;
|
||||
pub(crate) mod write;
|
||||
|
||||
#[cfg(feature = "id3v2")]
|
||||
use crate::id3::v2::tag::ID3v2Tag;
|
||||
|
@ -20,6 +19,7 @@ pub use crate::iff::wav::properties::{WavFormat, WavProperties};
|
|||
/// A WAV file
|
||||
#[derive(LoftyFile)]
|
||||
#[lofty(read_fn = "read::read_from")]
|
||||
#[lofty(internal_write_module_do_not_use_anywhere_else)]
|
||||
pub struct WavFile {
|
||||
/// A RIFF INFO LIST
|
||||
#[cfg(feature = "riff_info_list")]
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
use crate::error::Result;
|
||||
#[cfg(feature = "id3v2")]
|
||||
use crate::id3::v2;
|
||||
use crate::macros::err;
|
||||
#[allow(unused_imports)]
|
||||
use crate::tag::{Tag, TagType};
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn write_to(data: &mut File, tag: &Tag) -> Result<()> {
|
||||
match tag.tag_type() {
|
||||
#[cfg(feature = "riff_info_list")]
|
||||
TagType::RIFFInfo => {
|
||||
super::tag::RIFFInfoListRef::new(super::tag::tagitems_into_riff(tag.items()))
|
||||
.write_to(data)
|
||||
},
|
||||
#[cfg(feature = "id3v2")]
|
||||
TagType::ID3v2 => v2::tag::Id3v2TagRef {
|
||||
flags: v2::ID3v2TagFlags::default(),
|
||||
frames: v2::tag::tag_frames(tag),
|
||||
}
|
||||
.write_to(data),
|
||||
_ => err!(UnsupportedTag),
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
//! WavPack specific items
|
||||
mod properties;
|
||||
mod read;
|
||||
pub(crate) mod write;
|
||||
|
||||
#[cfg(feature = "ape")]
|
||||
use crate::ape::tag::ApeTag;
|
||||
|
@ -14,8 +13,9 @@ use lofty_attr::LoftyFile;
|
|||
pub use properties::WavPackProperties;
|
||||
|
||||
/// A WavPack file
|
||||
#[derive(Default, LoftyFile)]
|
||||
#[derive(LoftyFile, Default)]
|
||||
#[lofty(read_fn = "read::read_from")]
|
||||
#[lofty(internal_write_module_do_not_use_anywhere_else)]
|
||||
pub struct WavPackFile {
|
||||
/// An ID3v1 tag
|
||||
#[cfg(feature = "id3v1")]
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
#[cfg(feature = "ape")]
|
||||
use crate::ape;
|
||||
use crate::error::Result;
|
||||
#[cfg(feature = "id3v1")]
|
||||
use crate::id3::v1;
|
||||
use crate::macros::err;
|
||||
#[allow(unused_imports)]
|
||||
use crate::tag::{Tag, TagType};
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn write_to(data: &mut File, tag: &Tag) -> Result<()> {
|
||||
match tag.tag_type() {
|
||||
#[cfg(feature = "ape")]
|
||||
TagType::APE => ape::tag::ApeTagRef {
|
||||
read_only: false,
|
||||
items: ape::tag::tagitems_into_ape(tag.items()),
|
||||
}
|
||||
.write_to(data),
|
||||
#[cfg(feature = "id3v1")]
|
||||
TagType::ID3v1 => Into::<v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(data),
|
||||
_ => err!(UnsupportedTag),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue