diff --git a/Cargo.lock b/Cargo.lock index 623a1c2..1adfae8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1338,6 +1338,7 @@ version = "0.0.1" dependencies = [ "convert_case 0.5.0", "parse_duration", + "proc-macro2", "quote", "syn", ] diff --git a/proc_macros/Cargo.toml b/proc_macros/Cargo.toml index ae92068..75da460 100644 --- a/proc_macros/Cargo.toml +++ b/proc_macros/Cargo.toml @@ -13,5 +13,6 @@ proc-macro = true [dependencies] convert_case = "0.5" parse_duration = "2" +proc-macro2 = "1" quote = "1" syn = { version = "1", features = ["full"] } diff --git a/proc_macros/proc_macros.rs b/proc_macros/proc_macros.rs index 7921c91..2b32d39 100644 --- a/proc_macros/proc_macros.rs +++ b/proc_macros/proc_macros.rs @@ -516,7 +516,7 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream { } struct CheckJsonKeys { - pub keys: Vec<(LitStr, bool)>, + pub keys: Vec<(LitStr, bool, Option)>, } impl Parse for CheckJsonKeys { @@ -525,11 +525,19 @@ impl Parse for CheckJsonKeys { let first: LitStr = input.parse()?; match input.parse::() { Ok(_) => { - keys.push((first, true)); - } - Err(_) => { - keys.push((first, false)); + keys.push((first, true, None)); } + Err(_) => match input.parse::() { + Ok(_) => { + let content; + bracketed!(content in input); + let childrens = CheckJsonKeys::parse(&content)?; + keys.push((first, false, Some(childrens))); + } + Err(_) => { + keys.push((first, false, None)); + } + }, } while !input.is_empty() { let _: token::Comma = input.parse()?; @@ -539,42 +547,75 @@ impl Parse for CheckJsonKeys { let key: LitStr = input.parse()?; match input.parse::() { Ok(_) => { - keys.push((key, true)); - } - Err(_) => { - keys.push((key, false)); + keys.push((key, true, None)); } + Err(_) => match input.parse::() { + Ok(_) => { + let content; + bracketed!(content in input); + let childrens = CheckJsonKeys::parse(&content)?; + keys.push((key, false, Some(childrens))); + } + Err(_) => { + keys.push((key, false, None)); + } + }, } } Ok(Self { keys }) } } +fn get_check_json_keys_streams( + keys: Vec<(LitStr, bool, Option)>, +) -> Vec { + let mut streams = Vec::new(); + for (key, check, childrens) in keys { + match childrens { + Some(childrens) => { + let streams2 = get_check_json_keys_streams(childrens.keys); + streams.push(quote!(#key => { + let obj = sobj; + obj.is_object().try_err(format!("{} {}", gettext("Data is not a object:"), obj))?; + for (key, sobj) in obj.entries() { + match key { + #(#streams2)* + _ => { Err(format!("{} {}", gettext("Key is handled:").replace("", key).as_str(), obj))?; } + } + } + })) + } + None => { + if check { + let k = key.value(); + let k = k.to_case(Case::Snake); + let fun = Ident::new(&k, key.span()); + streams.push(quote!(#key => { + self.#fun().try_err(format!("{} {}", gettext("The value of the key is missing:").replace("", key).as_str(), obj))?; + })); + } else { + streams.push(quote!(#key => {})); + } + } + } + } + streams +} + #[proc_macro] pub fn check_json_keys(item: TokenStream) -> TokenStream { let CheckJsonKeys { keys } = parse_macro_input!(item as CheckJsonKeys); - let mut streams = Vec::new(); - for (key, check) in keys { - if check { - let k = key.value(); - let k = k.to_case(Case::Snake); - let fun = Ident::new(&k, key.span()); - streams.push(quote!(#key => { - self.#fun().try_err(format!("{} {}", gettext("The value of the key is missing:").replace("", key).as_str(), self.data))?; - })); - } else { - streams.push(quote!(#key => {})); - } - } + let streams = get_check_json_keys_streams(keys); let stream = quote!( { use crate::ext::try_err::TryErr; use crate::gettext; self.data.is_object().try_err(format!("{} {}", gettext("Data is not a object:"), self.data))?; - for (key, _) in self.data.entries() { + let obj = &self.data; + for (key, sobj) in self.data.entries() { match key { #(#streams)* - _ => { Err(format!("{} {}", gettext("Key is handled:").replace("", key).as_str(), self.data))?; } + _ => { Err(format!("{} {}", gettext("Key is handled:").replace("", key).as_str(), obj))?; } } } } diff --git a/src/fanbox/comment.rs b/src/fanbox/comment.rs index f168be5..f01c861 100644 --- a/src/fanbox/comment.rs +++ b/src/fanbox/comment.rs @@ -1,5 +1,7 @@ +use super::check::CheckUnkown; use crate::parser::json::parse_u64; use json::JsonValue; +use proc_macros::check_json_keys; use std::fmt::Debug; pub struct FanboxComment { @@ -83,6 +85,13 @@ impl FanboxComment { } } +impl CheckUnkown for FanboxComment { + fn check_unknown(&self) -> Result<(), super::error::FanboxAPIError> { + check_json_keys!("id"+, "body"+, "createdDatetime"+, "isLiked"+, "isOwn"+, "likeCount"+, "parentCommentId", "replies", "rootCommentId", "user": ["userId", "name", "iconUrl"]); + Ok(()) + } +} + impl Debug for FanboxComment { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FanboxComment")