DRY and code cleanup for api.rs
This commit is contained in:
parent
c1483ab210
commit
666ea1fd43
3 changed files with 116 additions and 67 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -142,9 +142,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.8.0"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
|
@ -237,12 +237,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1088,9 +1088,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.7"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
|
@ -1291,9 +1291,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.40"
|
version = "0.1.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-attributes",
|
"tracing-attributes",
|
||||||
|
|
132
src/api.rs
132
src/api.rs
|
@ -6,9 +6,85 @@ use crate::dummy::create_empty_config;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
use rocket::FromForm;
|
||||||
use rocket::{get, response::content::RawXml};
|
use rocket::{get, response::content::RawXml};
|
||||||
use xml::writer::{EmitterConfig, XmlEvent};
|
use xml::writer::{EmitterConfig, XmlEvent};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, FromForm)]
|
||||||
|
/// A struct used by the API's search functions to hold its query parameters
|
||||||
|
/// Currently required (AFAIK) because of limitations with rocket
|
||||||
|
struct SearchForm {
|
||||||
|
/// The text query for the search
|
||||||
|
q: Option<String>,
|
||||||
|
/// The apikey, for authentication
|
||||||
|
apikey: Option<String>,
|
||||||
|
/// The list of numeric category IDs to be included in the search results
|
||||||
|
/// Returned by Rocket.rs as a string of comma-separated values, then split in the function to a `Vec<u32>`
|
||||||
|
cat: Option<String>,
|
||||||
|
/// The list of extended attribute names to be included in the search results
|
||||||
|
/// Returned by Rocket.rs as a string of comma-separated values, then split in the function to a `Vec<String>`
|
||||||
|
attrs: Option<String>,
|
||||||
|
/// Whether *all* extended attributes should be included in the search results; overrules `attrs`
|
||||||
|
/// Can be 0 or 1
|
||||||
|
extended: Option<u8>,
|
||||||
|
/// How many items to skip/offset by in the results.
|
||||||
|
offset: Option<u32>,
|
||||||
|
/// The maximum number of items to return - also limited to whatever `limits` is in [`Caps`]
|
||||||
|
limit: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchForm {
|
||||||
|
fn to_parameters(&self, conf: Config) -> SearchParameters {
|
||||||
|
// TODO: Clean up this code - split it into a separate function?
|
||||||
|
let mut categories: Option<Vec<u32>> = None;
|
||||||
|
if !self.cat.is_none() {
|
||||||
|
// unholy amalgation of code to make the comma-separated list of strings into a vector of integers
|
||||||
|
categories = Some(
|
||||||
|
self.cat
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("")
|
||||||
|
.unwrap()
|
||||||
|
.split(",")
|
||||||
|
.filter_map(|s| s.parse().ok())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut extended_attribute_names: Option<Vec<String>> = None;
|
||||||
|
if !self.attrs.is_none() {
|
||||||
|
extended_attribute_names = Some(
|
||||||
|
self.attrs
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("")
|
||||||
|
.unwrap()
|
||||||
|
.split(",")
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut extended_attrs: Option<bool> = None;
|
||||||
|
if !self.extended.is_none() && self.extended.ok_or(false).unwrap() == 1 {
|
||||||
|
extended_attrs = Some(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut limit: u32 = self.limit.ok_or("").unwrap_or(conf.caps.limits.max);
|
||||||
|
if limit > conf.caps.limits.max {
|
||||||
|
limit = conf.caps.limits.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchParameters {
|
||||||
|
q: self.q.clone(),
|
||||||
|
apikey: self.apikey.clone(),
|
||||||
|
categories: categories,
|
||||||
|
attributes: extended_attribute_names,
|
||||||
|
extended_attrs: extended_attrs,
|
||||||
|
offset: self.offset,
|
||||||
|
limit: limit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Holds the config for torznab-toolkit.
|
// Holds the config for torznab-toolkit.
|
||||||
//
|
//
|
||||||
// A search function (`/api?t=search`) and capabilities (`/api?t=caps` - `Caps`) are required, everything else is optional.
|
// A search function (`/api?t=search`) and capabilities (`/api?t=caps` - `Caps`) are required, everything else is optional.
|
||||||
|
@ -33,12 +109,12 @@ lazy_static! {
|
||||||
pub(crate) fn caps() -> status::Custom<RawXml<String>> {
|
pub(crate) fn caps() -> status::Custom<RawXml<String>> {
|
||||||
// The compiler won't let you get a field from a struct in the Option here, since the default is None
|
// The compiler won't let you get a field from a struct in the Option here, since the default is None
|
||||||
// So this is needed
|
// So this is needed
|
||||||
let conf = create_empty_config();
|
let mut conf = create_empty_config();
|
||||||
unsafe {
|
unsafe {
|
||||||
if CONFIG.is_none() {
|
if CONFIG.is_none() {
|
||||||
return (*STATUS_CONFIG_NOT_SPECIFIED).clone();
|
return (*STATUS_CONFIG_NOT_SPECIFIED).clone();
|
||||||
} else {
|
} else {
|
||||||
let conf: Config = CONFIG.clone().ok_or("").unwrap();
|
conf = CONFIG.clone().ok_or("").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +130,7 @@ pub(crate) fn caps() -> status::Custom<RawXml<String>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/api?t=search&<form..>")]
|
#[get("/api?t=search&<form..>")]
|
||||||
|
/// The search function for the API
|
||||||
pub(crate) fn search(form: SearchForm) -> status::Custom<RawXml<String>> {
|
pub(crate) fn search(form: SearchForm) -> status::Custom<RawXml<String>> {
|
||||||
// The compiler won't let you get a field from a struct in the Option here, since the default is None
|
// The compiler won't let you get a field from a struct in the Option here, since the default is None
|
||||||
// So this is needed
|
// So this is needed
|
||||||
|
@ -66,46 +143,13 @@ pub(crate) fn search(form: SearchForm) -> status::Custom<RawXml<String>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Clean up this code - split it into a separate function?
|
let parameters = form.to_parameters(conf.clone());
|
||||||
let mut apikey: String = "".to_string();
|
|
||||||
if !form.apikey.is_none() {
|
|
||||||
apikey = form.apikey.ok_or("").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut categories: Vec<u32> = Vec::new();
|
|
||||||
if !form.cat.is_none() {
|
|
||||||
// unholy amalgation of code to make the comma-separated list of strings into a vector of integers
|
|
||||||
categories = form
|
|
||||||
.cat
|
|
||||||
.ok_or("")
|
|
||||||
.unwrap()
|
|
||||||
.split(",")
|
|
||||||
.filter_map(|s| s.parse().ok())
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut extended_attribute_names: String = "".to_string();
|
|
||||||
if !form.attrs.is_none() {
|
|
||||||
extended_attribute_names = form.attrs.ok_or("").unwrap().split(",").collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut extended_attrs: bool = false;
|
|
||||||
if !form.extended.is_none() && form.extended.ok_or(false).unwrap() == 1 {
|
|
||||||
extended_attrs = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut offset: u32 = 0;
|
|
||||||
if !form.offset.is_none() {
|
|
||||||
offset = form.offset.ok_or(0).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut limit: u32 = form.limit.ok_or("").unwrap_or(conf.caps.limits.max);
|
|
||||||
if limit > conf.caps.limits.max {
|
|
||||||
limit = conf.caps.limits.max;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let mut unauthorized = false;
|
||||||
match conf.auth {
|
match conf.auth {
|
||||||
Some(auth) => {
|
Some(auth) => {
|
||||||
|
match parameters.apikey {
|
||||||
|
Some(apikey) => {
|
||||||
if !auth(apikey).unwrap() {
|
if !auth(apikey).unwrap() {
|
||||||
return status::Custom(
|
return status::Custom(
|
||||||
Status::Unauthorized,
|
Status::Unauthorized,
|
||||||
|
@ -113,9 +157,19 @@ pub(crate) fn search(form: SearchForm) -> status::Custom<RawXml<String>> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
unauthorized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// that unwrap_or_else is to return "" if the apikey isn't specified
|
||||||
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if unauthorized {
|
||||||
|
return status::Custom(Status::Unauthorized, RawXml("401 Unauthorized".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
return status::Custom(
|
return status::Custom(
|
||||||
Status::NotImplemented,
|
Status::NotImplemented,
|
||||||
RawXml("501 Not Implemented: Search function not implemented".to_string()),
|
RawXml("501 Not Implemented: Search function not implemented".to_string()),
|
||||||
|
|
23
src/data.rs
23
src/data.rs
|
@ -1,6 +1,6 @@
|
||||||
//! Contains tons of structs used by the library
|
//! Contains tons of structs used by the library
|
||||||
use rocket::FromForm;
|
|
||||||
|
|
||||||
|
use rocket::FromForm;
|
||||||
pub(crate) type AuthFunc = fn(String) -> Result<bool, String>;
|
pub(crate) type AuthFunc = fn(String) -> Result<bool, String>;
|
||||||
// TODO: Figure out what the arguments should be for a search function and what it should return
|
// TODO: Figure out what the arguments should be for a search function and what it should return
|
||||||
pub(crate) type SearchFunc = fn(String, Vec<String>) -> Result<String, String>;
|
pub(crate) type SearchFunc = fn(String, Vec<String>) -> Result<String, String>;
|
||||||
|
@ -126,24 +126,19 @@ pub struct Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, FromForm)]
|
#[derive(Debug, Clone, PartialEq, Eq, FromForm)]
|
||||||
/// A struct used by the API's search functions to hold its query parameters
|
pub(crate) struct SearchParameters {
|
||||||
/// Currently required (AFAIK) because of limitations with rocket
|
|
||||||
pub(crate) struct SearchForm {
|
|
||||||
/// The text query for the search
|
/// The text query for the search
|
||||||
pub(crate) q: Option<String>,
|
pub(crate) q: Option<String>,
|
||||||
/// The apikey, for authentication
|
/// The apikey, for authentication
|
||||||
pub(crate) apikey: Option<String>,
|
pub(crate) apikey: Option<String>,
|
||||||
/// The list of numeric category IDs to be included in the search results
|
/// A [`Vec`] containing the numeric category IDs to be included in the search results
|
||||||
/// Returned by Rocket.rs as a string of comma-separated values, then split in the function to a `Vec<u32>`
|
pub(crate) categories: Option<Vec<u32>>,
|
||||||
pub(crate) cat: Option<String>,
|
/// A [`Vec`] containing the extended attribute names to be included in the search results
|
||||||
/// The list of extended attribute names to be included in the search results
|
pub(crate) attributes: Option<Vec<String>>,
|
||||||
/// Returned by Rocket.rs as a string of comma-separated values, then split in the function to a `Vec<String>`
|
/// Whether *all* extended attributes should be included in the search results; overrules `attributes`
|
||||||
pub(crate) attrs: Option<String>,
|
pub(crate) extended_attrs: Option<bool>,
|
||||||
/// Whether *all* extended attributes should be included in the search results; overrules `attrs`
|
|
||||||
/// Can be 0 or 1
|
|
||||||
pub(crate) extended: Option<u8>,
|
|
||||||
/// How many items to skip/offset by in the results.
|
/// How many items to skip/offset by in the results.
|
||||||
pub(crate) offset: Option<u32>,
|
pub(crate) offset: Option<u32>,
|
||||||
/// The maximum number of items to return - also limited to whatever `limits` is in [`Caps`]
|
/// The maximum number of items to return - also limited to whatever `limits` is in [`Caps`]
|
||||||
pub(crate) limit: Option<u32>,
|
pub(crate) limit: u32,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue