diff --git a/qbittorrent-web-api-gen/src/generate/group/method/return_type.rs b/qbittorrent-web-api-gen/src/generate/group/method/return_type.rs index c6264f6..4fc68f5 100644 --- a/qbittorrent-web-api-gen/src/generate/group/method/return_type.rs +++ b/qbittorrent-web-api-gen/src/generate/group/method/return_type.rs @@ -15,81 +15,22 @@ pub fn create_return_type( None => return None, }; - let to_enum_name = |name: &str| { - format!( - "{}{}{}", - group.name.to_camel(), - method.name.to_camel(), - name.to_camel() - ) - }; + let to_enum_name = |name: &str| to_enum_name(&group.name, &method.name, name); - let enum_types_with_names = return_type - .parameters - .iter() - .flat_map(|parameter| match ¶meter.return_type { - types::Type::Number(types::TypeInfo { - ref name, - type_description: Some(type_description), - .. - }) => create_enum_field_value(type_description, name, create_number_enum_value), - types::Type::String(types::TypeInfo { - ref name, - type_description: Some(type_description), - .. - }) => create_enum_field_value(type_description, name, create_string_enum_value), - _ => None, - }) - .flat_map(|(name, enum_fields)| { - let enum_name = util::to_ident(&to_enum_name(&name)); - - Some(( - name, - quote! { - #[allow(clippy::enum_variant_names)] - #[derive(Debug, Deserialize, PartialEq, Eq)] - pub enum #enum_name { - #(#enum_fields,)* - } - }, - )) - }); + let enum_types_with_names: Vec<(String, proc_macro2::TokenStream)> = + create_enum_with_names(return_type, &group.name, &method.name); let enum_names: HashMap = enum_types_with_names - .clone() - .map(|(enum_name, _)| (enum_name.clone(), to_enum_name(&enum_name))) + .iter() + .map(|(enum_name, _)| (enum_name.clone(), to_enum_name(enum_name))) .collect(); - let enum_types = enum_types_with_names.map(|(_, enum_type)| enum_type); + let enum_types = enum_types_with_names.iter().map(|(_, enum_type)| enum_type); - let builder_fields = return_type.parameters.iter().map(|parameter| { - let namestr = ¶meter.name; - let name = util::to_ident(&namestr.to_snake().replace("__", "_")); - let enum_name = match enum_names.get(namestr) { - Some(enum_type) => enum_type.to_owned(), - None => parameter.return_type.to_owned_type(), - }; - let rtype = util::to_ident(&enum_name); - let rtype_as_quote = if parameter.return_type.get_type_info().is_list { - quote! { std::vec::Vec<#rtype> } - } else { - quote! { #rtype } - }; - - let generate_field = |field_name| { - quote! { - #[serde(rename = #namestr)] - pub #field_name: #rtype_as_quote - } - }; - - // "type" is a reserved keyword in Rust, so we just add "t_" to it. - if namestr == "type" { - generate_field(format_ident!("t_{}", name)) - } else { - generate_field(name) - } - }); + let builder_fields = return_type + .parameters + .iter() + .map(|parameter| generate_builder_field(parameter, &enum_names)); let return_type_name = util::to_ident(&format!( "{}{}Result", @@ -98,13 +39,9 @@ pub fn create_return_type( )); let result_type = if return_type.is_list { - quote! { - std::vec::Vec<#return_type_name> - } + quote! { std::vec::Vec<#return_type_name> } } else { - quote! { - #return_type_name - } + quote! { #return_type_name } }; Some(( @@ -120,6 +57,87 @@ pub fn create_return_type( )) } +fn create_enum_with_names( + return_type: &parser::ReturnType, + group_name: &str, + method_name: &str, +) -> Vec<(String, proc_macro2::TokenStream)> { + return_type + .parameters + .iter() + .flat_map(create_enum_fields) + .map(|(name, enum_fields)| create_enum(enum_fields, group_name, method_name, name)) + .collect() +} + +fn create_enum( + enum_fields: Vec, + group_name: &str, + method_name: &str, + name: String, +) -> (String, proc_macro2::TokenStream) { + let enum_name = util::to_ident(&to_enum_name(group_name, method_name, &name)); + ( + name, + quote! { + #[allow(clippy::enum_variant_names)] + #[derive(Debug, Deserialize, PartialEq, Eq)] + pub enum #enum_name { + #(#enum_fields,)* + } + }, + ) +} + +fn create_enum_fields( + parameter: &parser::ReturnTypeParameter, +) -> Option<(String, Vec)> { + match ¶meter.return_type { + types::Type::Number(types::TypeInfo { + ref name, + type_description: Some(type_description), + .. + }) => create_enum_field_value(type_description, name, create_number_enum_value), + types::Type::String(types::TypeInfo { + ref name, + type_description: Some(type_description), + .. + }) => create_enum_field_value(type_description, name, create_string_enum_value), + _ => None, + } +} + +fn generate_builder_field( + parameter: &parser::ReturnTypeParameter, + enum_names: &HashMap, +) -> proc_macro2::TokenStream { + let namestr = ¶meter.name; + let name = util::to_ident(&namestr.to_snake().replace("__", "_")); + let enum_name = match enum_names.get(namestr) { + Some(enum_type) => enum_type.to_owned(), + None => parameter.return_type.to_owned_type(), + }; + let rtype = util::to_ident(&enum_name); + let rtype_as_quote = if parameter.return_type.get_type_info().is_list { + quote! { std::vec::Vec<#rtype> } + } else { + quote! { #rtype } + }; + let generate_field = |field_name| { + quote! { + #[serde(rename = #namestr)] + pub #field_name: #rtype_as_quote + } + }; + + // "type" is a reserved keyword in Rust, so we just add "t_" to it. + if namestr == "type" { + generate_field(format_ident!("t_{}", name)) + } else { + generate_field(name) + } +} + fn create_enum_field_value( type_description: &types::TypeDescription, name: &str, @@ -174,3 +192,12 @@ fn create_enum_field( }, ) } + +fn to_enum_name(group_name: &str, method_name: &str, name: &str) -> String { + format!( + "{}{}{}", + group_name.to_camel(), + method_name.to_camel(), + name.to_camel() + ) +} diff --git a/qbittorrent-web-api-gen/src/parser/group/method/mod.rs b/qbittorrent-web-api-gen/src/parser/group/method/mod.rs index 83aaf67..3d374f8 100644 --- a/qbittorrent-web-api-gen/src/parser/group/method/mod.rs +++ b/qbittorrent-web-api-gen/src/parser/group/method/mod.rs @@ -5,11 +5,11 @@ mod url; use crate::{md_parser, parser::util, types}; +pub use return_type::ReturnType; + use self::{ - description::parse_method_description, - parameters::parse_parameters, - return_type::{parse_return_type, ReturnType}, - url::get_method_url, + description::parse_method_description, parameters::parse_parameters, + return_type::parse_return_type, url::get_method_url, }; #[derive(Debug)] diff --git a/qbittorrent-web-api-gen/src/parser/group/mod.rs b/qbittorrent-web-api-gen/src/parser/group/mod.rs index 9debf71..b7b4036 100644 --- a/qbittorrent-web-api-gen/src/parser/group/mod.rs +++ b/qbittorrent-web-api-gen/src/parser/group/mod.rs @@ -5,6 +5,7 @@ mod url; use crate::md_parser; use self::{description::parse_group_description, method::parse_api_method, url::get_group_url}; +pub use method::{ApiMethod, ReturnType}; #[derive(Debug)] pub struct ApiGroup { @@ -14,8 +15,6 @@ pub struct ApiGroup { pub url: String, } -pub use method::ApiMethod; - pub fn parse_api_group(tree: &md_parser::TokenTree) -> ApiGroup { let methods = tree.children.iter().flat_map(parse_api_method).collect(); diff --git a/qbittorrent-web-api-gen/src/parser/mod.rs b/qbittorrent-web-api-gen/src/parser/mod.rs index 35f7a2e..8974168 100644 --- a/qbittorrent-web-api-gen/src/parser/mod.rs +++ b/qbittorrent-web-api-gen/src/parser/mod.rs @@ -12,8 +12,7 @@ pub struct ReturnTypeParameter { pub return_type: types::Type, } -pub use group::ApiGroup; -pub use group::ApiMethod; +pub use group::{ApiGroup, ApiMethod, ReturnType}; pub fn parse_api_groups(token_tree: md_parser::TokenTree) -> Vec { parse_groups(extract_relevant_parts(token_tree))