This commit is contained in:
2022-07-15 07:11:32 +00:00
committed by GitHub
parent 2e3761a758
commit 26f2bb32bd
7 changed files with 193 additions and 15 deletions

1
Cargo.lock generated
View File

@@ -1337,6 +1337,7 @@ name = "proc_macros"
version = "0.0.1"
dependencies = [
"convert_case 0.5.0",
"json",
"parse_duration",
"proc-macro2",
"quote",

View File

@@ -12,6 +12,7 @@ proc-macro = true
[dependencies]
convert_case = "0.5"
json = "0"
parse_duration = "2"
proc-macro2 = "1"
quote = "1"

View File

@@ -1,7 +1,10 @@
use convert_case::Case;
use convert_case::Casing;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use std::io::Read;
use std::str::FromStr;
use syn::bracketed;
use syn::parse::Parse;
use syn::parse_macro_input;
@@ -735,3 +738,140 @@ pub fn create_fanbox_download_helper(item: TokenStream) -> TokenStream {
);
stream.into()
}
fn read_json_file<P: AsRef<std::path::Path> + ?Sized>(p: &P) -> json::JsonValue {
let mut f = std::fs::File::open(p).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
json::parse(s.as_str()).unwrap()
}
#[proc_macro]
pub fn define_exif_data_source(item: TokenStream) -> TokenStream {
let file_path = parse_macro_input!(item as LitStr).value();
let path =
std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join(file_path);
let data = read_json_file(&path);
let mut streams = Vec::new();
for (key, data) in data.entries() {
let doc = match data["description"].as_str() {
Some(doc) => {
let doc = LitStr::new(doc, Span::call_site());
quote!(#[doc = #doc])
}
None => {
quote!()
}
};
let fname = Ident::new(key, Span::call_site());
let result = proc_macro2::TokenStream::from_str(match data["return"].as_str() {
Some(r) => r,
None => "String",
})
.unwrap();
streams.push(quote!(
#doc
#[inline]
fn #fname(&self) -> Option<#result> {
None
}
));
}
let stream = quote!(
#(#streams)*
);
stream.into()
}
struct CallParentDataSourceFun {
pub file: LitStr,
pub expr: Expr,
pub keys: Vec<Ident>,
pub is_include: bool,
}
impl Parse for CallParentDataSourceFun {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let filel = Lit::parse(input)?;
let file = match filel {
Lit::Str(s) => s,
_ => {
return Err(syn::Error::new(filel.span(), "Unsupported literal."));
}
};
token::Comma::parse(input)?;
let expr = input.parse()?;
let mut keys = Vec::new();
let mut is_include = false;
let mut is_first = true;
loop {
if input.cursor().eof() {
break;
}
token::Comma::parse(input)?;
if input.cursor().eof() {
break;
}
if is_first {
is_first = false;
match syn::Lit::parse(input) {
Ok(lit) => {
match lit {
syn::Lit::Bool(lit) => {
is_include = lit.value();
}
_ => {
return Err(syn::Error::new(lit.span(), "Unsupported literal."));
}
}
continue;
}
Err(_) => {}
}
}
keys.push(Ident::parse(input)?);
}
Ok(Self {
file,
expr,
keys,
is_include,
})
}
}
#[proc_macro]
pub fn call_parent_data_source_fun(item: TokenStream) -> TokenStream {
let CallParentDataSourceFun {
file,
expr,
keys,
is_include,
} = parse_macro_input!(item as CallParentDataSourceFun);
let path =
std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join(file.value());
let data = read_json_file(&path);
let mut streams = Vec::new();
for (key, data) in data.entries() {
let is_in = keys.iter().find(|&r| r.to_string() == key).is_some();
if is_in != is_include {
continue;
}
let fname = Ident::new(key, Span::call_site());
let result = proc_macro2::TokenStream::from_str(match data["return"].as_str() {
Some(r) => r,
None => "String",
})
.unwrap();
streams.push(quote!(
#[inline]
fn #fname(&self) -> Option<#result> {
(#expr).#fname()
}
));
}
let stream = quote!(
#(#streams)*
);
stream.into()
}

View File

@@ -6,27 +6,31 @@ use crate::exif::ExifTypeID;
use crate::exif::ExifValue;
use crate::ext::try_err::TryErr;
use crate::parser::description::parse_description;
use proc_macros::call_parent_data_source_fun;
use proc_macros::define_exif_data_source;
use std::convert::TryFrom;
use std::ffi::OsStr;
use utf16string::LittleEndian;
use utf16string::WString;
pub trait ExifDataSource {
fn image_author(&self) -> Option<String> {
None
}
define_exif_data_source!("src/data/exif_data_source.json");
}
fn image_comment(&self) -> Option<String> {
None
}
impl<T: ExifDataSource> ExifDataSource for std::sync::Arc<T> {
call_parent_data_source_fun!("src/data/exif_data_source.json", *self,);
}
fn image_id(&self) -> Option<String> {
None
}
fn image_title(&self) -> Option<String> {
None
}
impl<T: ExifDataSource> ExifDataSource for Option<T> {
call_parent_data_source_fun!(
"src/data/exif_data_source.json",
match self {
Some(data) => data,
None => {
return None;
}
},
);
}
fn add_image_id<D: ExifDataSource>(data: &mut ExifData, d: &D) -> Result<(), ()> {

View File

@@ -0,0 +1,6 @@
{
"image_author": { "return": "String", "description": "The author of the image." },
"image_comment": { "return": "String", "description": "The comment of the image." },
"image_id": { "return": "String", "description": "The image id." },
"image_title": { "return": "String", "description": "The title of the image." }
}

View File

@@ -1,12 +1,18 @@
#[cfg(feature = "exif")]
use super::exif::ExifDataSource;
use crate::fanbox::post::FanboxPost;
use crate::pixiv_link::PixivID;
use crate::pixiv_link::ToPixivID;
use json::JsonValue;
#[cfg(feature = "exif")]
use proc_macros::call_parent_data_source_fun;
pub struct FanboxData {
pub id: PixivID,
/// Raw data
pub raw: JsonValue,
#[cfg(feature = "exif")]
pub exif_data: Option<Box<dyn ExifDataSource>>,
}
impl FanboxData {
@@ -15,8 +21,28 @@ impl FanboxData {
Some(id) => Some(Self {
id,
raw: post.get_json().clone(),
#[cfg(feature = "exif")]
exif_data: None,
}),
None => None,
}
}
}
#[cfg(feature = "exif")]
impl ExifDataSource for FanboxData {
call_parent_data_source_fun!(
"src/data/exif_data_source.json",
match &self.exif_data {
Some(d) => d,
None => {
return None;
}
},
image_id,
);
fn image_id(&self) -> Option<String> {
Some(self.id.to_link())
}
}

View File

@@ -131,7 +131,7 @@ impl Main {
if d.is_downloaded() {
#[cfg(feature = "exif")]
{
if add_exifdata_to_image(&file_name, &*datas, np).is_err() {
if add_exifdata_to_image(&file_name, &datas, np).is_err() {
println!(
"{} {}",
gettext("Failed to add exif data to image:"),
@@ -150,7 +150,7 @@ impl Main {
#[cfg(feature = "exif")]
{
if helper.update_exif() && file_name.exists() {
if add_exifdata_to_image(&file_name, &*datas, np).is_err() {
if add_exifdata_to_image(&file_name, &datas, np).is_err() {
println!(
"{} {}",
gettext("Failed to add exif data to image:"),