This commit is contained in:
Joel Wachsler 2022-07-22 21:55:26 +00:00
parent 07e825bcd4
commit 4a1db65877
5 changed files with 344 additions and 120 deletions

File diff suppressed because it is too large Load Diff

View File

@ -156,7 +156,7 @@ impl types::Type {
&self.get_type_info().description,
quote! {
#[serde(rename = #orig_name)]
#name_snake: #type_name
pub #name_snake: #type_name
},
)
}
@ -223,6 +223,69 @@ impl<'a> GroupMethod<'a> {
Self { group, method }
}
fn generate_method(&self) -> TokenStream {
let method_name = self.method.name_snake();
let structs = self.method.structs();
let enums = self.method.enums();
let builder = self.generate_request_builder();
let response_struct = self.generate_response_struct();
let request_method = self.generate_request_method();
quote! {
pub mod #method_name {
#structs
#enums
#builder
#response_struct
#request_method
}
}
}
fn generate_request_method(&self) -> TokenStream {
let method_name = self.method.name_snake();
let parameters = self
.method
.types
.mandatory_params()
.iter()
.map(|param| param.to_parameter())
.collect();
let form_builder = self.mandatory_parameters_as_form_builder();
let method_impl = if self.method.types.optional_parameters().is_empty() {
self.generate_send_method(
&method_name,
parameters,
quote! { self.auth },
quote! { form },
quote! {
let form = reqwest::multipart::Form::new();
#form_builder
},
)
} else {
quote! {
pub fn #method_name(&self, #(#parameters),*) -> Builder<'_> {
let form = reqwest::multipart::Form::new();
#form_builder
Builder { group: self, form }
}
}
};
let group_struct_name = self.group.struct_name();
let method_impl_with_docs = util::add_docs(&self.method.description, method_impl);
quote! {
impl<'a> super::#group_struct_name<'a> {
#method_impl_with_docs
}
}
}
fn generate_response_struct(&self) -> TokenStream {
let response = match self.method.types.response() {
Some(res) => res,
@ -239,20 +302,26 @@ impl<'a> GroupMethod<'a> {
}
}
fn generate_optional_builder(&self) -> TokenStream {
let optional_params = match self.method.types.optional_parameters() {
Some(params) => params,
None => return quote! {},
};
/// Returns a TokenStream containing a request builder if there are optional
/// parameters, otherwise an empty TokenStream is returned.
fn generate_request_builder(&self) -> TokenStream {
let optional_params = self.method.types.optional_parameters();
if optional_params.is_empty() {
return quote! {};
}
let builder_methods = optional_params
.iter()
.map(|param| param.generate_optional_builder_method_with_docs());
let group_name = self.group.struct_name();
let mandatory_params = self.mandatory_parameters();
let mandatory_param_form_builder = self.mandatory_parameters_as_form_builder();
let send_method = self.generate_optional_builder_send_method();
let send_method = self.generate_send_method(
&util::to_ident("send"),
vec![],
quote! { self.group.auth },
quote! { self.form },
quote! {},
);
quote! {
pub struct Builder<'a> {
@ -261,104 +330,55 @@ impl<'a> GroupMethod<'a> {
}
impl<'a> Builder<'a> {
pub fn new(group: &'a super::#group_name, #mandatory_params) -> Self {
let form = reqwest::multipart::Form::new();
#mandatory_param_form_builder
Self { group, form }
}
#send_method
#(#builder_methods)*
}
}
}
fn generate_optional_builder_send_method(&self) -> TokenStream {
fn generate_send_method(
&self,
method_name: &Ident,
parameters: Vec<TokenStream>,
auth_access: TokenStream,
form_access: TokenStream,
form_factory: TokenStream,
) -> TokenStream {
let method_url = format!("/api/v2/{}/{}", self.group.url, self.method.url);
match self.method.types.response() {
Some(_) => {
quote! {
pub async fn send(self) -> super::super::Result<Response> {
let res = self
.group
.auth
.authenticated_client(#method_url)
.multipart(self.form)
.send()
.await?
.json::<Response>()
.await?;
Ok(res)
}
}
}
None => {
quote! {
pub async fn send(self) -> super::super::Result<String> {
let res = self
.group
.auth
.authenticated_client(#method_url)
.multipart(self.form)
.send()
.await?
.text()
.await?;
Ok(res)
}
}
}
}
}
fn mandatory_parameters(&self) -> TokenStream {
let mandatory_params = match self.method.types.mandatory_params() {
Some(p) => p,
None => return quote! {},
let (response_type, response_parse) = match self.method.types.response() {
Some(_) => (quote! { Response }, quote! { .json::<Response>() }),
None => (quote! { String }, quote! { .text() }),
};
let params = mandatory_params.iter().map(|param| param.to_parameter());
quote! {
#(#params),*
pub async fn #method_name(self, #(#parameters),*) -> super::super::Result<#response_type> {
#form_factory
let res = #auth_access
.authenticated_client(#method_url)
.multipart(#form_access)
.send()
.await?
#response_parse
.await?;
Ok(res)
}
}
}
fn mandatory_parameters_as_form_builder(&self) -> TokenStream {
let mandatory_params = match self.method.types.mandatory_params() {
Some(p) => p,
None => return quote! {},
};
let builder = mandatory_params
.iter()
let builder = self
.method
.types
.mandatory_params()
.into_iter()
.map(|param| param.generate_form_builder(quote! { form }));
quote! {
#(let #builder)*
}
}
fn generate_method(&self) -> TokenStream {
let method_name = self.method.name_snake();
let structs = self.method.structs();
let enums = self.method.enums();
let builder = self.generate_optional_builder();
let response_struct = self.generate_response_struct();
quote! {
pub mod #method_name {
#structs
#enums
#builder
#response_struct
}
}
}
}
impl types::Type {

View File

@ -13,6 +13,7 @@ ApiMethod {
TypeInfo {
name: "id",
is_optional: false,
is_list: false,
description: Some(
"ID of the search job",
),
@ -22,6 +23,7 @@ ApiMethod {
TypeInfo {
name: "limit",
is_optional: true,
is_list: false,
description: Some(
"max number of results to return. 0 or negative means no limit",
),
@ -31,6 +33,7 @@ ApiMethod {
TypeInfo {
name: "offset",
is_optional: true,
is_list: false,
description: Some(
"result to start at. A negative number means count backwards (e.g. -2 returns the 2 most recent results)",
),
@ -47,6 +50,7 @@ ApiMethod {
TypeInfo {
name: "descrLink",
is_optional: false,
is_list: false,
description: Some(
"URL of the torrent's description page",
),
@ -56,6 +60,7 @@ ApiMethod {
TypeInfo {
name: "fileName",
is_optional: false,
is_list: false,
description: Some(
"Name of the file",
),
@ -65,6 +70,7 @@ ApiMethod {
TypeInfo {
name: "fileSize",
is_optional: false,
is_list: false,
description: Some(
"Size of the file in Bytes",
),
@ -74,6 +80,7 @@ ApiMethod {
TypeInfo {
name: "fileUrl",
is_optional: false,
is_list: false,
description: Some(
"Torrent download link (usually either .torrent file or magnet link)",
),
@ -83,6 +90,7 @@ ApiMethod {
TypeInfo {
name: "nbLeechers",
is_optional: false,
is_list: false,
description: Some(
"Number of leechers",
),
@ -92,6 +100,7 @@ ApiMethod {
TypeInfo {
name: "nbSeeders",
is_optional: false,
is_list: false,
description: Some(
"Number of seeders",
),
@ -101,6 +110,7 @@ ApiMethod {
TypeInfo {
name: "siteUrl",
is_optional: false,
is_list: false,
description: Some(
"URL of the torrent site",
),
@ -117,6 +127,7 @@ ApiMethod {
type_info: TypeInfo {
name: "results",
is_optional: false,
is_list: false,
description: Some(
"Array of result objects- see table below",
),
@ -128,6 +139,7 @@ ApiMethod {
TypeInfo {
name: "status",
is_optional: false,
is_list: false,
description: Some(
"Current status of the search job (either Running or Stopped)",
),
@ -137,6 +149,7 @@ ApiMethod {
TypeInfo {
name: "total",
is_optional: false,
is_list: false,
description: Some(
"Total number of results. If the status is Running this number may continue to increase",
),

View File

@ -27,21 +27,30 @@ impl CompositeTypes {
}
}
pub fn parameters(&self) -> Option<&Vec<types::Type>> {
self.composite_types.iter().find_map(|type_| match type_ {
CompositeType::Parameters(p) => Some(&p.types),
pub fn parameters(&self) -> Vec<&types::Type> {
self.composite_types
.iter()
.find_map(|type_| match type_ {
CompositeType::Parameters(p) => Some(p.types.iter().collect()),
_ => None,
})
.unwrap_or_default()
}
pub fn optional_parameters(&self) -> Option<Vec<&types::Type>> {
pub fn optional_parameters(&self) -> Vec<&types::Type> {
self.parameters()
.map(|params| params.iter().filter(|param| param.is_optional()).collect())
.iter()
.filter(|param| param.is_optional())
.copied()
.collect()
}
pub fn mandatory_params(&self) -> Option<Vec<&types::Type>> {
pub fn mandatory_params(&self) -> Vec<&types::Type> {
self.parameters()
.map(|params| params.iter().filter(|param| !param.is_optional()).collect())
.iter()
.filter(|param| !param.is_optional())
.copied()
.collect()
}
pub fn response(&self) -> Option<&Vec<types::Type>> {
@ -386,11 +395,6 @@ mod tests {
".check"
);
// prevent user from accidentally leaving the current macro in a test
if Path::new(file).exists() {
panic!("Test case already exists: {file}");
}
fs::write(file, api_method_as_str).unwrap();
fs::write(tree_file, tree_as_str).unwrap();
};

View File

@ -16,16 +16,16 @@ pub struct TypeDescription {
pub struct TypeInfo {
pub name: String,
pub is_optional: bool,
// is_list: bool,
pub is_list: bool,
pub description: Option<String>,
}
impl TypeInfo {
pub fn new(name: &str, is_optional: bool, description: Option<String>) -> Self {
pub fn new(name: &str, is_optional: bool, is_list: bool, description: Option<String>) -> Self {
Self {
name: name.into(),
is_optional,
// is_list,
is_list,
description,
}
}
@ -74,10 +74,6 @@ impl Type {
}
}
// pub fn is_list(&self) -> bool {
// self.get_type_info().is_list || matches!(self, Type::StringArray(_))
// }
pub fn to_borrowed_type(&self) -> String {
match self {
Type::Number(_) => "i32".into(),
@ -116,7 +112,12 @@ impl Type {
.trim();
let is_optional = name.contains(OPTIONAL);
let create_type_info = || TypeInfo::new(type_name, is_optional, description.clone());
let is_list = description
.clone()
.map(|desc| desc.contains("array"))
.unwrap_or(false);
let create_type_info =
|| TypeInfo::new(type_name, is_optional, is_list, description.clone());
let create_object_type = |name: &str| {
Some(Type::Object(Object {
@ -126,6 +127,7 @@ impl Type {
};
match type_as_str {
"raw" => None,
"bool" => Some(Type::Bool(create_type_info())),
"integer" | "number" | "int" => Some(Type::Number(create_type_info())),
"string" => Some(Type::String(create_type_info())),