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 { fn generate_struct_field(&self) -> TokenStream {
let name_snake = self.name_snake(); let name_snake = self.name_snake();
let type_name = util::to_ident(&self.to_owned_type()); 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(); let orig_name = self.name();
util::add_docs( util::add_docs(
&self.get_type_info().description, &self.get_type_info().description,
quote! { quote! {
#[serde(rename = #orig_name)] #[serde(rename = #orig_name)]
pub #name_snake: #type_name pub #name_snake: #type_
}, },
) )
} }
@ -292,7 +297,10 @@ impl<'a> GroupMethod<'a> {
None => return quote! {}, 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! { quote! {
#[derive(Debug, serde::Deserialize)] #[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 method_url = format!("/api/v2/{}/{}", self.group.url, self.method.url);
let (response_type, response_parse) = match self.method.types.response() { 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() }), 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( Number(
TypeInfo { TypeInfo {
name: "id", name: "id",
is_optional: false,
is_list: false,
description: Some( description: Some(
"ID of the search job", "ID of the search job",
), ),
is_optional: false,
is_list: false,
}, },
), ),
Number( Number(
TypeInfo { TypeInfo {
name: "limit", name: "limit",
is_optional: true,
is_list: false,
description: Some( description: Some(
"max number of results to return. 0 or negative means no limit", "max number of results to return. 0 or negative means no limit",
), ),
is_optional: true,
is_list: false,
}, },
), ),
Number( Number(
TypeInfo { TypeInfo {
name: "offset", name: "offset",
is_optional: true,
is_list: false,
description: Some( description: Some(
"result to start at. A negative number means count backwards (e.g. -2 returns the 2 most recent results)", "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( Object(
@ -49,71 +50,71 @@ ApiMethod {
String( String(
TypeInfo { TypeInfo {
name: "descrLink", name: "descrLink",
is_optional: false,
is_list: false,
description: Some( description: Some(
"URL of the torrent's description page", "URL of the torrent's description page",
), ),
is_optional: false,
is_list: false,
}, },
), ),
String( String(
TypeInfo { TypeInfo {
name: "fileName", name: "fileName",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Name of the file", "Name of the file",
), ),
is_optional: false,
is_list: false,
}, },
), ),
Number( Number(
TypeInfo { TypeInfo {
name: "fileSize", name: "fileSize",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Size of the file in Bytes", "Size of the file in Bytes",
), ),
is_optional: false,
is_list: false,
}, },
), ),
String( String(
TypeInfo { TypeInfo {
name: "fileUrl", name: "fileUrl",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Torrent download link (usually either .torrent file or magnet link)", "Torrent download link (usually either .torrent file or magnet link)",
), ),
is_optional: false,
is_list: false,
}, },
), ),
Number( Number(
TypeInfo { TypeInfo {
name: "nbLeechers", name: "nbLeechers",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Number of leechers", "Number of leechers",
), ),
is_optional: false,
is_list: false,
}, },
), ),
Number( Number(
TypeInfo { TypeInfo {
name: "nbSeeders", name: "nbSeeders",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Number of seeders", "Number of seeders",
), ),
is_optional: false,
is_list: false,
}, },
), ),
String( String(
TypeInfo { TypeInfo {
name: "siteUrl", name: "siteUrl",
is_optional: false,
is_list: false,
description: Some( description: Some(
"URL of the torrent site", "URL of the torrent site",
), ),
is_optional: false,
is_list: false,
}, },
), ),
], ],
@ -126,36 +127,37 @@ ApiMethod {
Object { Object {
type_info: TypeInfo { type_info: TypeInfo {
name: "results", name: "results",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Array of result objects- see table below", "Array of result objects- see table below",
), ),
is_optional: false,
is_list: true,
}, },
ref_type: "Result", ref_type: "",
}, },
), ),
String( String(
TypeInfo { TypeInfo {
name: "status", name: "status",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Current status of the search job (either Running or Stopped)", "Current status of the search job (either Running or Stopped)",
), ),
is_optional: false,
is_list: false,
}, },
), ),
Number( Number(
TypeInfo { TypeInfo {
name: "total", name: "total",
is_optional: false,
is_list: false,
description: Some( description: Some(
"Total number of results. If the status is Running this number may continue to increase", "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() .collect()
} }
pub fn response(&self) -> Option<&Vec<types::Type>> { pub fn response(&self) -> Option<&TypeWithoutName> {
self.composite_types.iter().find_map(|type_| match type_ { self.composite_types.iter().find_map(|type_| match type_ {
CompositeType::Response(p) => Some(&p.types), CompositeType::Response(p) => Some(p),
_ => None, _ => None,
}) })
} }
@ -104,11 +104,12 @@ pub struct TypeWithName {
#[derive(Debug)] #[derive(Debug)]
pub struct TypeWithoutName { pub struct TypeWithoutName {
pub types: Vec<types::Type>, pub types: Vec<types::Type>,
pub is_list: bool,
} }
impl TypeWithoutName { impl TypeWithoutName {
pub fn new(types: Vec<types::Type>) -> Self { pub fn new(types: Vec<types::Type>, is_list: bool) -> Self {
Self { types } Self { types, is_list }
} }
} }
@ -270,6 +271,7 @@ impl md_parser::Table {
Some(CompositeType::Response(TypeWithoutName::new( Some(CompositeType::Response(TypeWithoutName::new(
self.to_types(), self.to_types(),
input_name.to_lowercase().contains("array"),
))) )))
} }
@ -280,6 +282,7 @@ impl md_parser::Table {
Some(CompositeType::Parameters(TypeWithoutName::new( Some(CompositeType::Parameters(TypeWithoutName::new(
self.to_types(), self.to_types(),
input_name.to_lowercase().contains("array"),
))) )))
} }
@ -409,4 +412,14 @@ mod tests {
fn enum_test() { fn enum_test() {
run_test!("enum"); 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)] #[derive(Debug, Clone)]
pub struct TypeInfo { pub struct TypeInfo {
pub name: String, pub name: String,
pub is_optional: bool,
pub is_list: bool,
pub description: Option<String>, pub description: Option<String>,
is_optional: bool,
is_list: bool,
} }
impl TypeInfo { impl TypeInfo {
pub fn new(name: &str, is_optional: bool, is_list: bool, description: Option<String>) -> Self { pub fn new(name: &str, is_optional: bool, is_list: bool, description: Option<String>) -> Self {
Self { Self {
name: name.into(), name: name.into(),
description,
is_optional, is_optional,
is_list, is_list,
description,
} }
} }
} }
@ -93,6 +93,10 @@ impl Type {
self.get_type_info().is_optional 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 { pub fn get_type_info(&self) -> &TypeInfo {
match self { match self {
Type::Number(t) => t, Type::Number(t) => t,
@ -116,8 +120,21 @@ impl Type {
.clone() .clone()
.map(|desc| desc.contains("array")) .map(|desc| desc.contains("array"))
.unwrap_or(false); .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| { let create_object_type = |name: &str| {
Some(Type::Object(Object { Some(Type::Object(Object {
@ -126,7 +143,7 @@ impl Type {
})) }))
}; };
match type_as_str { match type_without_array.trim() {
"raw" => None, "raw" => None,
"bool" => Some(Type::Bool(create_type_info())), "bool" => Some(Type::Bool(create_type_info())),
"integer" | "number" | "int" => Some(Type::Number(create_type_info())), "integer" | "number" | "int" => Some(Type::Number(create_type_info())),

View File

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