Group by function
This commit is contained in:
parent
201ba025f3
commit
aaf3153205
|
@ -7,14 +7,15 @@ use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::{parser, types, util};
|
use crate::{md_parser, parser, types, util};
|
||||||
|
|
||||||
use self::skeleton::{auth_ident, generate_skeleton};
|
use self::skeleton::{auth_ident, generate_skeleton};
|
||||||
|
|
||||||
pub fn generate(ast: &syn::DeriveInput, api_content: &str) -> TokenStream {
|
pub fn generate(ast: &syn::DeriveInput, api_content: &str) -> TokenStream {
|
||||||
let ident = &ast.ident;
|
let ident = &ast.ident;
|
||||||
|
|
||||||
let api_groups = parser::parse_api_groups(api_content);
|
let token_tree = md_parser::TokenTreeFactory::create(api_content);
|
||||||
|
let api_groups = parser::parse_api_groups(token_tree);
|
||||||
|
|
||||||
let skeleton = generate_skeleton(ident);
|
let skeleton = generate_skeleton(ident);
|
||||||
let groups = generate_groups(api_groups);
|
let groups = generate_groups(api_groups);
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
use crate::md_parser;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
api_method::parse_api_method, description::parse_group_description, url_parser::get_group_url,
|
|
||||||
ApiGroup,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn parse_api_group(tree: md_parser::TokenTree) -> ApiGroup {
|
|
||||||
let methods = tree
|
|
||||||
.children
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(parse_api_method)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let group_description = parse_group_description(&tree.content);
|
|
||||||
let group_url = get_group_url(&tree.content);
|
|
||||||
|
|
||||||
let name = tree
|
|
||||||
.title
|
|
||||||
.unwrap()
|
|
||||||
.to_lowercase()
|
|
||||||
.trim_end_matches("(experimental)")
|
|
||||||
.trim()
|
|
||||||
.replace(' ', "_");
|
|
||||||
|
|
||||||
ApiGroup {
|
|
||||||
name,
|
|
||||||
methods,
|
|
||||||
description: group_description,
|
|
||||||
url: group_url,
|
|
||||||
}
|
|
||||||
}
|
|
17
qbittorrent-web-api-gen/src/parser/group/description.rs
Normal file
17
qbittorrent-web-api-gen/src/parser/group/description.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::md_parser;
|
||||||
|
|
||||||
|
pub fn parse_group_description(content: &[md_parser::MdContent]) -> Option<String> {
|
||||||
|
let return_desc = content
|
||||||
|
.iter()
|
||||||
|
.map(|row| row.inner_value_as_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if return_desc.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(return_desc)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,5 @@
|
||||||
use crate::md_parser::MdContent;
|
use crate::md_parser::MdContent;
|
||||||
|
|
||||||
pub fn parse_group_description(content: &[MdContent]) -> Option<String> {
|
|
||||||
let return_desc = content
|
|
||||||
.iter()
|
|
||||||
.map(|row| row.inner_value_as_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n")
|
|
||||||
.trim()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
if return_desc.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(return_desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_method_description(content: &[MdContent]) -> Option<String> {
|
pub fn parse_method_description(content: &[MdContent]) -> Option<String> {
|
||||||
let return_desc = content
|
let return_desc = content
|
||||||
.iter()
|
.iter()
|
|
@ -1,18 +1,36 @@
|
||||||
use crate::md_parser;
|
mod description;
|
||||||
|
mod return_type;
|
||||||
|
mod url;
|
||||||
|
|
||||||
use super::{
|
use crate::{
|
||||||
description::parse_method_description, parameters::parse_parameters,
|
md_parser,
|
||||||
return_type::parse_return_type, url_parser::get_method_url, util, ApiMethod,
|
parser::{parameters::parse_parameters, util},
|
||||||
|
types,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse_api_method(child: md_parser::TokenTree) -> Option<ApiMethod> {
|
use self::{
|
||||||
|
description::parse_method_description,
|
||||||
|
return_type::{parse_return_type, ReturnType},
|
||||||
|
url::get_method_url,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ApiMethod {
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub parameters: Option<Vec<types::Type>>,
|
||||||
|
pub return_type: Option<ReturnType>,
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_api_method(child: &md_parser::TokenTree) -> Option<ApiMethod> {
|
||||||
util::find_content_starts_with(&child.content, "Name: ")
|
util::find_content_starts_with(&child.content, "Name: ")
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
name.trim_start_matches("Name: ")
|
name.trim_start_matches("Name: ")
|
||||||
.trim_matches('`')
|
.trim_matches('`')
|
||||||
.to_string()
|
.to_string()
|
||||||
})
|
})
|
||||||
.map(|name| to_api_method(&child, &name))
|
.map(|name| to_api_method(child, &name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_api_method(child: &md_parser::TokenTree, name: &str) -> ApiMethod {
|
fn to_api_method(child: &md_parser::TokenTree, name: &str) -> ApiMethod {
|
|
@ -1,8 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
md_parser::MdContent,
|
md_parser::MdContent,
|
||||||
parser::{object_types::parse_object_types, types, ReturnType, ReturnTypeParameter},
|
parser::{object_types::parse_object_types, types, ReturnTypeParameter},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ReturnType {
|
||||||
|
pub is_list: bool,
|
||||||
|
pub parameters: Vec<ReturnTypeParameter>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_return_type(content: &[MdContent]) -> Option<ReturnType> {
|
pub fn parse_return_type(content: &[MdContent]) -> Option<ReturnType> {
|
||||||
let table = content
|
let table = content
|
||||||
.iter()
|
.iter()
|
9
qbittorrent-web-api-gen/src/parser/group/method/url.rs
Normal file
9
qbittorrent-web-api-gen/src/parser/group/method/url.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::{md_parser, parser::util};
|
||||||
|
|
||||||
|
pub fn get_method_url(content: &[md_parser::MdContent]) -> String {
|
||||||
|
const START: &str = "Name: ";
|
||||||
|
|
||||||
|
util::find_content_starts_with(content, START)
|
||||||
|
.map(|text| text.trim_start_matches(START).trim_matches('`').to_string())
|
||||||
|
.expect("Could find method url")
|
||||||
|
}
|
40
qbittorrent-web-api-gen/src/parser/group/mod.rs
Normal file
40
qbittorrent-web-api-gen/src/parser/group/mod.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
mod description;
|
||||||
|
mod method;
|
||||||
|
mod url;
|
||||||
|
|
||||||
|
use crate::md_parser;
|
||||||
|
|
||||||
|
use self::{description::parse_group_description, method::parse_api_method, url::get_group_url};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ApiGroup {
|
||||||
|
pub name: String,
|
||||||
|
pub methods: Vec<ApiMethod>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
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();
|
||||||
|
|
||||||
|
let group_description = parse_group_description(&tree.content);
|
||||||
|
let group_url = get_group_url(&tree.content);
|
||||||
|
|
||||||
|
let name = tree
|
||||||
|
.title
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.to_lowercase()
|
||||||
|
.trim_end_matches("(experimental)")
|
||||||
|
.trim()
|
||||||
|
.replace(' ', "_");
|
||||||
|
|
||||||
|
ApiGroup {
|
||||||
|
name,
|
||||||
|
methods,
|
||||||
|
description: group_description,
|
||||||
|
url: group_url,
|
||||||
|
}
|
||||||
|
}
|
14
qbittorrent-web-api-gen/src/parser/group/url.rs
Normal file
14
qbittorrent-web-api-gen/src/parser/group/url.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::{md_parser, parser::util};
|
||||||
|
|
||||||
|
pub fn get_group_url(content: &[md_parser::MdContent]) -> String {
|
||||||
|
let row = util::find_content_contains(content, "API methods are under")
|
||||||
|
.expect("Could not find api method");
|
||||||
|
|
||||||
|
let re = Regex::new(r#"All (?:\w+\s?)+ API methods are under "(\w+)", e.g."#)
|
||||||
|
.expect("Failed to create regex");
|
||||||
|
|
||||||
|
let res = re.captures(&row).expect("Failed find capture");
|
||||||
|
res[1].to_string()
|
||||||
|
}
|
|
@ -1,39 +1,12 @@
|
||||||
use crate::{md_parser, types};
|
use crate::{md_parser, types};
|
||||||
|
|
||||||
use self::api_group::parse_api_group;
|
use self::group::parse_api_group;
|
||||||
|
|
||||||
mod api_group;
|
mod group;
|
||||||
mod api_method;
|
|
||||||
mod description;
|
|
||||||
mod object_types;
|
mod object_types;
|
||||||
mod parameters;
|
mod parameters;
|
||||||
mod return_type;
|
|
||||||
mod url_parser;
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ApiGroup {
|
|
||||||
pub name: String,
|
|
||||||
pub methods: Vec<ApiMethod>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ApiMethod {
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub parameters: Option<Vec<types::Type>>,
|
|
||||||
pub return_type: Option<ReturnType>,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ReturnType {
|
|
||||||
pub is_list: bool,
|
|
||||||
pub parameters: Vec<ReturnTypeParameter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ReturnTypeParameter {
|
pub struct ReturnTypeParameter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -41,10 +14,18 @@ pub struct ReturnTypeParameter {
|
||||||
pub return_type: types::Type,
|
pub return_type: types::Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_api_groups(content: &str) -> Vec<ApiGroup> {
|
pub use group::ApiGroup;
|
||||||
parse_groups(extract_relevant_parts(md_parser::TokenTreeFactory::create(
|
pub use group::ApiMethod;
|
||||||
content,
|
|
||||||
)))
|
pub fn parse_api_groups(token_tree: md_parser::TokenTree) -> Vec<ApiGroup> {
|
||||||
|
parse_groups(extract_relevant_parts(token_tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_groups(trees: Vec<md_parser::TokenTree>) -> Vec<ApiGroup> {
|
||||||
|
trees
|
||||||
|
.into_iter()
|
||||||
|
.map(|tree| parse_api_group(&tree))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_relevant_parts(tree: md_parser::TokenTree) -> Vec<md_parser::TokenTree> {
|
fn extract_relevant_parts(tree: md_parser::TokenTree) -> Vec<md_parser::TokenTree> {
|
||||||
|
@ -64,10 +45,6 @@ fn extract_relevant_parts(tree: md_parser::TokenTree) -> Vec<md_parser::TokenTre
|
||||||
relevant
|
relevant
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_groups(trees: Vec<md_parser::TokenTree>) -> Vec<ApiGroup> {
|
|
||||||
trees.into_iter().map(parse_api_group).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
use crate::{md_parser::MdContent, parser::util};
|
|
||||||
|
|
||||||
pub fn get_group_url(content: &[MdContent]) -> String {
|
|
||||||
let row = util::find_content_contains(content, "API methods are under")
|
|
||||||
.expect("Could not find api method");
|
|
||||||
|
|
||||||
let re = Regex::new(r#"All (?:\w+\s?)+ API methods are under "(\w+)", e.g."#)
|
|
||||||
.expect("Failed to create regex");
|
|
||||||
|
|
||||||
let res = re.captures(&row).expect("Failed find capture");
|
|
||||||
res[1].to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_method_url(content: &[MdContent]) -> String {
|
|
||||||
const START: &str = "Name: ";
|
|
||||||
|
|
||||||
util::find_content_starts_with(content, START)
|
|
||||||
.map(|text| text.trim_start_matches(START).trim_matches('`').to_string())
|
|
||||||
.expect("Could find method url")
|
|
||||||
}
|
|
|
@ -2,26 +2,14 @@ use crate::md_parser::MdContent;
|
||||||
|
|
||||||
pub fn find_content_starts_with(content: &[MdContent], starts_with: &str) -> Option<String> {
|
pub fn find_content_starts_with(content: &[MdContent], starts_with: &str) -> Option<String> {
|
||||||
content.iter().find_map(|row| match row {
|
content.iter().find_map(|row| match row {
|
||||||
MdContent::Text(content) => {
|
MdContent::Text(content) if content.starts_with(starts_with) => Some(content.into()),
|
||||||
if content.starts_with(starts_with) {
|
|
||||||
Some(content.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_content_contains(content: &[MdContent], contains: &str) -> Option<String> {
|
pub fn find_content_contains(content: &[MdContent], contains: &str) -> Option<String> {
|
||||||
content.iter().find_map(|row| match row {
|
content.iter().find_map(|row| match row {
|
||||||
MdContent::Text(content) => {
|
MdContent::Text(content) if content.contains(contains) => Some(content.into()),
|
||||||
if content.contains(contains) {
|
|
||||||
Some(content.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user