This commit is contained in:
2022-07-10 03:42:04 +00:00
committed by GitHub
parent bbd6a8d694
commit 22185554c1
10 changed files with 272 additions and 1 deletions

9
Cargo.lock generated
View File

@@ -316,6 +316,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "convert_case"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8"
[[package]]
name = "core-foundation"
version = "0.9.3"
@@ -359,7 +365,7 @@ version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"convert_case 0.4.0",
"proc-macro2",
"quote",
"rustc_version",
@@ -1330,6 +1336,7 @@ dependencies = [
name = "proc_macros"
version = "0.0.1"
dependencies = [
"convert_case 0.5.0",
"parse_duration",
"quote",
"syn",

View File

@@ -11,6 +11,7 @@ path = "proc_macros.rs"
proc-macro = true
[dependencies]
convert_case = "0.5"
parse_duration = "2"
quote = "1"
syn = { version = "1", features = ["full"] }

View File

@@ -1,3 +1,5 @@
use convert_case::Case;
use convert_case::Casing;
use proc_macro::TokenStream;
use quote::quote;
use syn::bracketed;
@@ -511,3 +513,70 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream {
};
stream.into()
}
struct CheckJsonKeys {
pub keys: Vec<(LitStr, bool)>,
}
impl Parse for CheckJsonKeys {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut keys = Vec::new();
let first: LitStr = input.parse()?;
match input.parse::<token::Add>() {
Ok(_) => {
keys.push((first, true));
}
Err(_) => {
keys.push((first, false));
}
}
while !input.is_empty() {
let _: token::Comma = input.parse()?;
if input.is_empty() {
break;
}
let key: LitStr = input.parse()?;
match input.parse::<token::Add>() {
Ok(_) => {
keys.push((key, true));
}
Err(_) => {
keys.push((key, false));
}
}
}
Ok(Self { keys })
}
}
#[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(gettext("The value of the key <key> is missing.").replace("<key>", key))?;
}));
} else {
streams.push(quote!(#key => {}));
}
}
let stream = quote!(
{
use crate::ext::try_err::TryErr;
use crate::gettext;
self.data.is_object().try_err(gettext("Data is not a object."))?;
for (key, _) in self.data.entries() {
match key {
#(#streams)*
_ => { Err(format!("{} {}", gettext("Key <key> is handled:").replace("<key>", key).as_str(), self.data))?; }
}
}
}
);
stream.into()
}

View File

@@ -36,3 +36,13 @@ impl<T, E, E2> TryErr<T, E> for Result<T, E2> {
}
}
}
impl<E> TryErr<(), E> for bool {
fn try_err(self, err: E) -> Result<(), E> {
if self {
Ok(())
} else {
Err(err)
}
}
}

122
src/fanbox/article/block.rs Normal file
View File

@@ -0,0 +1,122 @@
use super::super::check::CheckUnkown;
use json::JsonValue;
use proc_macros::check_json_keys;
use std::fmt::Debug;
pub struct FanboxArticleParagraphBoldStyle {
pub data: JsonValue,
}
impl FanboxArticleParagraphBoldStyle {
#[inline]
pub fn length(&self) -> Option<u64> {
self.data["length"].as_u64()
}
#[inline]
/// Create a new instance
pub fn new(data: &JsonValue) -> Self {
Self { data: data.clone() }
}
#[inline]
pub fn offset(&self) -> Option<u64> {
self.data["offset"].as_u64()
}
}
impl CheckUnkown for FanboxArticleParagraphBoldStyle {
fn check_unknown(&self) -> Result<(), crate::fanbox::error::FanboxAPIError> {
check_json_keys!("offset"+, "length"+,);
Ok(())
}
}
impl Debug for FanboxArticleParagraphBoldStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FanboxArticleParagraphBoldStyle")
.field("length", &self.length())
.field("offset", &self.offset())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum FanboxArticleParagraphStyle {
Bold(FanboxArticleParagraphBoldStyle),
Unknown(JsonValue),
}
impl FanboxArticleParagraphStyle {
#[inline]
/// Create a new instance
pub fn new(data: &JsonValue) -> Self {
match data["type"].as_str() {
Some(t) => match t {
"bold" => Self::Bold(FanboxArticleParagraphBoldStyle::new(data)),
_ => Self::Unknown(data.clone()),
},
None => Self::Unknown(data.clone()),
}
}
}
pub struct FanboxArticleParagraphBlock {
pub data: JsonValue,
}
impl FanboxArticleParagraphBlock {
#[inline]
/// Create a new instance
pub fn new(data: &JsonValue) -> Self {
Self { data: data.clone() }
}
#[inline]
pub fn styles(&self) -> Option<Vec<FanboxArticleParagraphStyle>> {
let styles = &self.data["styles"];
if styles.is_array() {
let mut list = Vec::new();
for i in styles.members() {
list.push(FanboxArticleParagraphStyle::new(i))
}
Some(list)
} else {
None
}
}
#[inline]
pub fn text(&self) -> Option<&str> {
self.data["text"].as_str()
}
}
impl Debug for FanboxArticleParagraphBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FanboxArticleParagraphBlock")
.field("styles", &self.styles())
.field("text", &self.text())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum FanboxArticleBlock {
Paragraph(FanboxArticleParagraphBlock),
Unknown(JsonValue),
}
impl FanboxArticleBlock {
#[inline]
/// Create a new instance
pub fn new(data: &JsonValue) -> Self {
match data["type"].as_str() {
Some(t) => match t {
"p" => Self::Paragraph(FanboxArticleParagraphBlock::new(data)),
_ => Self::Unknown(data.clone()),
},
None => Self::Unknown(data.clone()),
}
}
}

View File

@@ -0,0 +1,45 @@
use super::block::FanboxArticleBlock;
use crate::fanbox_api::FanboxClientInternal;
use json::JsonValue;
use std::fmt::Debug;
use std::sync::Arc;
/// Article body
pub struct FanboxArticleBody {
/// Raw data
pub data: JsonValue,
client: Arc<FanboxClientInternal>,
}
impl FanboxArticleBody {
#[inline]
pub fn blocks(&self) -> Option<Vec<FanboxArticleBlock>> {
let blocks = &self.data["blocks"];
if blocks.is_array() {
let mut list = Vec::new();
for i in blocks.members() {
list.push(FanboxArticleBlock::new(i))
}
Some(list)
} else {
None
}
}
#[inline]
/// Create a new instance
pub fn new(data: &JsonValue, client: Arc<FanboxClientInternal>) -> Self {
Self {
data: data.clone(),
client,
}
}
}
impl Debug for FanboxArticleBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FanboxArticleBody")
.field("blocks", &self.blocks())
.finish_non_exhaustive()
}
}

View File

@@ -0,0 +1,2 @@
pub mod block;
pub mod body;

6
src/fanbox/check.rs Normal file
View File

@@ -0,0 +1,6 @@
use super::error::FanboxAPIError;
/// Check if have data that we don't handle
pub trait CheckUnkown {
fn check_unknown(&self) -> Result<(), FanboxAPIError>;
}

View File

@@ -1,3 +1,5 @@
pub mod article;
pub mod check;
pub mod comment;
pub mod comment_list;
pub mod creator;

View File

@@ -1,3 +1,4 @@
use super::article::body::FanboxArticleBody;
use super::comment_list::FanboxCommentList;
use crate::fanbox_api::FanboxClientInternal;
use crate::parser::json::parse_u64;
@@ -16,6 +17,11 @@ pub struct FanboxPostArticle {
}
impl FanboxPostArticle {
#[inline]
pub fn body(&self) -> FanboxArticleBody {
FanboxArticleBody::new(&self.data["body"], Arc::clone(&self.client))
}
#[inline]
pub fn comment_count(&self) -> Option<u64> {
self.data["commentCount"].as_u64()
@@ -161,6 +167,7 @@ impl Debug for FanboxPostArticle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FanboxPostArticle")
.field("id", &self.id())
.field("body", &self.body())
.field("comment_count", &self.comment_count())
.field("comment_list", &self.comment_list())
.field("cover_image_url", &self.cover_image_url())