This commit is contained in:
Joel Wachsler 2022-07-12 10:28:03 +00:00
parent 3559ac6c27
commit f0124d7620
8 changed files with 44 additions and 47 deletions

View File

@ -1,14 +1,32 @@
mod skeleton;
use std::{collections::HashMap, vec::Vec}; use std::{collections::HashMap, vec::Vec};
use case::CaseExt; use case::CaseExt;
use proc_macro2::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use regex::Regex; use regex::Regex;
use crate::{ use crate::{parser, types, util};
parser::{self, types::TypeInfo},
skeleton::auth_ident, use self::skeleton::{auth_ident, generate_skeleton};
util,
}; pub fn generate(ast: &syn::DeriveInput, api_content: &str) -> TokenStream {
let ident = &ast.ident;
let api_groups = parser::parse_api_groups(api_content);
let skeleton = generate_skeleton(ident);
let groups = generate_groups(api_groups);
let impl_ident = syn::Ident::new(&format!("{}_impl", ident).to_snake(), ident.span());
quote! {
pub mod #impl_ident {
#skeleton
#groups
}
}
}
pub fn generate_groups(groups: Vec<parser::ApiGroup>) -> proc_macro2::TokenStream { pub fn generate_groups(groups: Vec<parser::ApiGroup>) -> proc_macro2::TokenStream {
let gr = groups let gr = groups
@ -138,7 +156,7 @@ fn create_method_without_params(
fn create_method_with_params( fn create_method_with_params(
group: &parser::ApiGroup, group: &parser::ApiGroup,
method: &parser::ApiMethod, method: &parser::ApiMethod,
params: &[parser::types::Type], params: &[types::Type],
method_name: &proc_macro2::Ident, method_name: &proc_macro2::Ident,
url: &str, url: &str,
) -> (proc_macro2::TokenStream, Option<proc_macro2::TokenStream>) { ) -> (proc_macro2::TokenStream, Option<proc_macro2::TokenStream>) {
@ -314,7 +332,7 @@ fn create_return_type(
.parameters .parameters
.iter() .iter()
.flat_map(|parameter| match &parameter.return_type { .flat_map(|parameter| match &parameter.return_type {
parser::types::Type::Number(TypeInfo { types::Type::Number(types::TypeInfo {
ref name, ref name,
type_description: Some(type_description), type_description: Some(type_description),
.. ..
@ -352,7 +370,7 @@ fn create_return_type(
}, },
)) ))
} }
parser::types::Type::String(TypeInfo { types::Type::String(types::TypeInfo {
ref name, ref name,
type_description: Some(type_description), type_description: Some(type_description),
.. ..

View File

@ -1,35 +1,16 @@
mod group; mod generate;
mod md_parser; mod md_parser;
mod parser; mod parser;
mod skeleton;
mod util; mod util;
mod types;
use case::CaseExt;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote;
use skeleton::generate_skeleton;
use syn::parse_macro_input; use syn::parse_macro_input;
use crate::group::generate_groups;
const API_CONTENT: &str = include_str!("../api-4_1.md"); const API_CONTENT: &str = include_str!("../api-4_1.md");
#[proc_macro_derive(QBittorrentApiGen, attributes(api_gen))] #[proc_macro_derive(QBittorrentApiGen, attributes(api_gen))]
pub fn derive(input: TokenStream) -> TokenStream { pub fn derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as syn::DeriveInput); let ast = parse_macro_input!(input as syn::DeriveInput);
let ident = &ast.ident; generate::generate(&ast, API_CONTENT).into()
let api_groups = parser::parse_api_groups(API_CONTENT);
let skeleton = generate_skeleton(ident);
let groups = generate_groups(api_groups);
let impl_ident = syn::Ident::new(&format!("{}_impl", ident).to_snake(), ident.span());
quote! {
pub mod #impl_ident {
#skeleton
#groups
}
}
.into()
} }

View File

@ -1,11 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{md_parser::MdContent, parser::types};
md_parser::MdContent,
parser::types::{Type, OPTIONAL},
};
pub fn get_parameters(content: &[MdContent]) -> Option<Vec<Type>> { pub fn get_parameters(content: &[MdContent]) -> Option<Vec<types::Type>> {
let mut it = content let mut it = content
.iter() .iter()
.skip_while(|row| match row { .skip_while(|row| match row {
@ -37,10 +34,10 @@ pub fn get_parameters(content: &[MdContent]) -> Option<Vec<Type>> {
// If the description contains a default value it means that the parameter is optional. // If the description contains a default value it means that the parameter is optional.
Some(desc) if desc.contains("default: ") => { Some(desc) if desc.contains("default: ") => {
// type defines a variable as default if it contains: _optional_ // type defines a variable as default if it contains: _optional_
let name_with_optional = format!("{} {}", row.columns[0], OPTIONAL); let name_with_optional = format!("{} {}", row.columns[0], types::OPTIONAL);
Type::from(&row.columns[1], &name_with_optional, description, &type_map) types::Type::from(&row.columns[1], &name_with_optional, description, &type_map)
} }
_ => Type::from(&row.columns[1], &row.columns[0], description, &type_map), _ => types::Type::from(&row.columns[1], &row.columns[0], description, &type_map),
} }
}) })
.collect(); .collect();

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
md_parser::MdContent, md_parser::MdContent,
parser::{object_types::get_object_types, types::Type, ReturnType, ReturnTypeParameter}, parser::{object_types::get_object_types, types, ReturnType, ReturnTypeParameter},
}; };
pub fn get_return_type(content: &[MdContent]) -> Option<ReturnType> { pub fn get_return_type(content: &[MdContent]) -> Option<ReturnType> {
@ -26,7 +26,7 @@ pub fn get_return_type(content: &[MdContent]) -> Option<ReturnType> {
.map(|parameter| ReturnTypeParameter { .map(|parameter| ReturnTypeParameter {
name: parameter.columns[0].clone(), name: parameter.columns[0].clone(),
description: parameter.columns[2].clone(), description: parameter.columns[2].clone(),
return_type: Type::from( return_type: types::Type::from(
&parameter.columns[1], &parameter.columns[1],
&parameter.columns[0], &parameter.columns[0],
Some(parameter.columns[2].clone()), Some(parameter.columns[2].clone()),

View File

@ -1,12 +1,13 @@
mod group_parser; mod group_parser;
mod object_types; mod object_types;
pub mod types;
mod util; mod util;
use group_parser::parse_groups; use group_parser::parse_groups;
use types::Type;
use crate::md_parser::{self, TokenTree}; use crate::{
md_parser::{self, TokenTree},
types,
};
#[derive(Debug)] #[derive(Debug)]
pub struct ApiGroup { pub struct ApiGroup {
@ -20,7 +21,7 @@ pub struct ApiGroup {
pub struct ApiMethod { pub struct ApiMethod {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub parameters: Option<Vec<Type>>, pub parameters: Option<Vec<types::Type>>,
pub return_type: Option<ReturnType>, pub return_type: Option<ReturnType>,
pub url: String, pub url: String,
} }
@ -35,7 +36,7 @@ pub struct ReturnType {
pub struct ReturnTypeParameter { pub struct ReturnTypeParameter {
pub name: String, pub name: String,
pub description: String, pub description: String,
pub return_type: Type, pub return_type: types::Type,
} }
fn extract_relevant_parts(tree: TokenTree) -> Vec<TokenTree> { fn extract_relevant_parts(tree: TokenTree) -> Vec<TokenTree> {

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{md_parser::MdContent, parser::types::TypeDescriptions}; use crate::{md_parser::MdContent, parser::types};
use super::types::TypeDescription; use super::types::TypeDescription;
@ -17,7 +17,7 @@ pub fn get_object_types(content: &[MdContent]) -> HashMap<String, TypeDescriptio
let enum_types = table let enum_types = table
.rows .rows
.iter() .iter()
.map(|row| TypeDescriptions { .map(|row| types::TypeDescriptions {
value: row.columns[0].to_string(), value: row.columns[0].to_string(),
description: row.columns[1].to_string(), description: row.columns[1].to_string(),
}) })