Support for list return

This commit is contained in:
Joel Wachsler 2022-07-22 22:22:57 +00:00
parent 4a1db65877
commit ab816e14b1
12 changed files with 764 additions and 465 deletions

File diff suppressed because it is too large Load Diff

View File

@ -150,13 +150,18 @@ impl types::Type {
fn generate_struct_field(&self) -> TokenStream {
let name_snake = self.name_snake();
let type_name = util::to_ident(&self.to_owned_type());
let type_ = if self.is_list() {
quote! { std::vec::Vec<#type_name> }
} else {
quote! { #type_name }
};
let orig_name = self.name();
util::add_docs(
&self.get_type_info().description,
quote! {
#[serde(rename = #orig_name)]
pub #name_snake: #type_name
pub #name_snake: #type_
},
)
}
@ -292,7 +297,10 @@ impl<'a> GroupMethod<'a> {
None => return quote! {},
};
let struct_fields = response.iter().map(|field| field.generate_struct_field());
let struct_fields = response
.types
.iter()
.map(|field| field.generate_struct_field());
quote! {
#[derive(Debug, serde::Deserialize)]
@ -347,7 +355,16 @@ impl<'a> GroupMethod<'a> {
let method_url = format!("/api/v2/{}/{}", self.group.url, self.method.url);
let (response_type, response_parse) = match self.method.types.response() {
Some(_) => (quote! { Response }, quote! { .json::<Response>() }),
Some(resp) => {
if resp.is_list {
(
quote! { std::vec::Vec<Response> },
quote! { .json::<std::vec::Vec<Response>>() },
)
} else {
(quote! { Response }, quote! { .json::<Response>() })
}
}
None => (quote! { String }, quote! { .text() }),
};

View File

@ -0,0 +1,26 @@
ApiMethod {
name: "foo",
description: None,
url: "foo",
types: CompositeTypes {
composite_types: [
Response(
TypeWithoutName {
types: [
Number(
TypeInfo {
name: "amount_left",
description: Some(
"Amount of data left to download (bytes)",
),
is_optional: false,
is_list: true,
},
),
],
is_list: false,
},
),
],
},
}

View File

@ -0,0 +1,9 @@
## Testing
Name: `foo`
The response is a JSON object with the following fields
Property | Type | Description
---------------------|---------|------------
`amount_left` | integer array | Amount of data left to download (bytes)

View File

@ -0,0 +1,52 @@
TokenTree {
title: None,
content: [],
children: [
TokenTree {
title: Some(
"Testing",
),
content: [
Text(
"",
),
Text(
"Name: `foo`",
),
Text(
"",
),
Text(
"The response is a JSON object with the following fields",
),
Text(
"",
),
Table(
Table {
header: TableRow {
raw: "Property | Type | Description",
columns: [
"Property",
"Type",
"Description",
],
},
split: "---------------------|---------|------------",
rows: [
TableRow {
raw: "`amount_left` | integer array | Amount of data left to download (bytes)",
columns: [
"amount_left",
"integer array",
"Amount of data left to download (bytes)",
],
},
],
},
),
],
children: [],
},
],
}

View File

@ -0,0 +1,26 @@
ApiMethod {
name: "foo",
description: None,
url: "foo",
types: CompositeTypes {
composite_types: [
Response(
TypeWithoutName {
types: [
Number(
TypeInfo {
name: "added_on",
description: Some(
"Time (Unix Epoch) when the torrent was added to the client",
),
is_optional: false,
is_list: false,
},
),
],
is_list: true,
},
),
],
},
}

View File

@ -0,0 +1,9 @@
## Testing
Name: `foo`
The response is a JSON array with the following fields
Property | Type | Description
---------------------|---------|------------
`added_on` | integer | Time (Unix Epoch) when the torrent was added to the client

View File

@ -0,0 +1,52 @@
TokenTree {
title: None,
content: [],
children: [
TokenTree {
title: Some(
"Testing",
),
content: [
Text(
"",
),
Text(
"Name: `foo`",
),
Text(
"",
),
Text(
"The response is a JSON array with the following fields",
),
Text(
"",
),
Table(
Table {
header: TableRow {
raw: "Property | Type | Description",
columns: [
"Property",
"Type",
"Description",
],
},
split: "---------------------|---------|------------",
rows: [
TableRow {
raw: "`added_on` | integer | Time (Unix Epoch) when the torrent was added to the client",
columns: [
"added_on",
"integer",
"Time (Unix Epoch) when the torrent was added to the client",
],
},
],
},
),
],
children: [],
},
],
}

View File

@ -12,34 +12,35 @@ ApiMethod {
Number(
TypeInfo {
name: "id",
is_optional: false,
is_list: false,
description: Some(
"ID of the search job",
),
is_optional: false,
is_list: false,
},
),
Number(
TypeInfo {
name: "limit",
is_optional: true,
is_list: false,
description: Some(
"max number of results to return. 0 or negative means no limit",
),
is_optional: true,
is_list: false,
},
),
Number(
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)",
),
is_optional: true,
is_list: false,
},
),
],
is_list: false,
},
),
Object(
@ -49,71 +50,71 @@ ApiMethod {
String(
TypeInfo {
name: "descrLink",
is_optional: false,
is_list: false,
description: Some(
"URL of the torrent's description page",
),
is_optional: false,
is_list: false,
},
),
String(
TypeInfo {
name: "fileName",
is_optional: false,
is_list: false,
description: Some(
"Name of the file",
),
is_optional: false,
is_list: false,
},
),
Number(
TypeInfo {
name: "fileSize",
is_optional: false,
is_list: false,
description: Some(
"Size of the file in Bytes",
),
is_optional: false,
is_list: false,
},
),
String(
TypeInfo {
name: "fileUrl",
is_optional: false,
is_list: false,
description: Some(
"Torrent download link (usually either .torrent file or magnet link)",
),
is_optional: false,
is_list: false,
},
),
Number(
TypeInfo {
name: "nbLeechers",
is_optional: false,
is_list: false,
description: Some(
"Number of leechers",
),
is_optional: false,
is_list: false,
},
),
Number(
TypeInfo {
name: "nbSeeders",
is_optional: false,
is_list: false,
description: Some(
"Number of seeders",
),
is_optional: false,
is_list: false,
},
),
String(
TypeInfo {
name: "siteUrl",
is_optional: false,
is_list: false,
description: Some(
"URL of the torrent site",
),
is_optional: false,
is_list: false,
},
),
],
@ -126,36 +127,37 @@ ApiMethod {
Object {
type_info: TypeInfo {
name: "results",
is_optional: false,
is_list: false,
description: Some(
"Array of result objects- see table below",
),
is_optional: false,
is_list: true,
},
ref_type: "Result",
ref_type: "",
},
),
String(
TypeInfo {
name: "status",
is_optional: false,
is_list: false,
description: Some(
"Current status of the search job (either Running or Stopped)",
),
is_optional: false,
is_list: false,
},
),
Number(
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",
),
is_optional: false,
is_list: false,
},
),
],
is_list: false,
},
),
],

View File

@ -53,9 +53,9 @@ impl CompositeTypes {
.collect()
}
pub fn response(&self) -> Option<&Vec<types::Type>> {
pub fn response(&self) -> Option<&TypeWithoutName> {
self.composite_types.iter().find_map(|type_| match type_ {
CompositeType::Response(p) => Some(&p.types),
CompositeType::Response(p) => Some(p),
_ => None,
})
}
@ -104,11 +104,12 @@ pub struct TypeWithName {
#[derive(Debug)]
pub struct TypeWithoutName {
pub types: Vec<types::Type>,
pub is_list: bool,
}
impl TypeWithoutName {
pub fn new(types: Vec<types::Type>) -> Self {
Self { types }
pub fn new(types: Vec<types::Type>, is_list: bool) -> Self {
Self { types, is_list }
}
}
@ -270,6 +271,7 @@ impl md_parser::Table {
Some(CompositeType::Response(TypeWithoutName::new(
self.to_types(),
input_name.to_lowercase().contains("array"),
)))
}
@ -280,6 +282,7 @@ impl md_parser::Table {
Some(CompositeType::Parameters(TypeWithoutName::new(
self.to_types(),
input_name.to_lowercase().contains("array"),
)))
}
@ -409,4 +412,14 @@ mod tests {
fn enum_test() {
run_test!("enum");
}
#[test]
fn array_result() {
run_test!("array_result");
}
#[test]
fn array_field() {
run_test!("array_field");
}
}

View File

@ -15,18 +15,18 @@ pub struct TypeDescription {
#[derive(Debug, Clone)]
pub struct TypeInfo {
pub name: String,
pub is_optional: bool,
pub is_list: bool,
pub description: Option<String>,
is_optional: bool,
is_list: bool,
}
impl TypeInfo {
pub fn new(name: &str, is_optional: bool, is_list: bool, description: Option<String>) -> Self {
Self {
name: name.into(),
description,
is_optional,
is_list,
description,
}
}
}
@ -93,6 +93,10 @@ impl Type {
self.get_type_info().is_optional
}
pub fn is_list(&self) -> bool {
self.get_type_info().is_list
}
pub fn get_type_info(&self) -> &TypeInfo {
match self {
Type::Number(t) => t,
@ -116,8 +120,21 @@ impl Type {
.clone()
.map(|desc| desc.contains("array"))
.unwrap_or(false);
let create_type_info =
|| TypeInfo::new(type_name, is_optional, is_list, description.clone());
let (type_without_array, type_contains_array) = if type_as_str.contains("array") {
(type_as_str.replace("array", ""), true)
} else {
(type_as_str.to_owned(), false)
};
let create_type_info = || {
TypeInfo::new(
type_name,
is_optional,
is_list || type_contains_array,
description.clone(),
)
};
let create_object_type = |name: &str| {
Some(Type::Object(Object {
@ -126,7 +143,7 @@ impl Type {
}))
};
match type_as_str {
match type_without_array.trim() {
"raw" => None,
"bool" => Some(Type::Bool(create_type_info())),
"integer" | "number" | "int" => Some(Type::Number(create_type_info())),

View File

@ -10,7 +10,7 @@ mod foo {
#[tokio::main]
async fn main() -> Result<()> {
let _ = foo::api_impl::ApplicationPreferencesBittorrentProtocol::TCP;
let _ = foo::api_impl::application::preferences::BittorrentProtocol::TCP;
Ok(())
}