From 666ea1fd4336cd0b937f2c346217862b41c1803b Mon Sep 17 00:00:00 2001 From: askiiart Date: Thu, 28 Nov 2024 23:09:32 -0600 Subject: [PATCH] DRY and code cleanup for api.rs --- Cargo.lock | 18 +++---- src/api.rs | 142 ++++++++++++++++++++++++++++++++++++---------------- src/data.rs | 23 ++++----- 3 files changed, 116 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9da6bf..ee58864 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,9 +142,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" @@ -237,12 +237,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1088,9 +1088,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1291,9 +1291,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", diff --git a/src/api.rs b/src/api.rs index f485dba..62b6cc1 100644 --- a/src/api.rs +++ b/src/api.rs @@ -6,9 +6,85 @@ use crate::dummy::create_empty_config; use lazy_static::lazy_static; use rocket::http::Status; use rocket::response::status; +use rocket::FromForm; use rocket::{get, response::content::RawXml}; 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, + /// The apikey, for authentication + apikey: Option, + /// 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` + cat: Option, + /// 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` + attrs: Option, + /// Whether *all* extended attributes should be included in the search results; overrules `attrs` + /// Can be 0 or 1 + extended: Option, + /// How many items to skip/offset by in the results. + offset: Option, + /// The maximum number of items to return - also limited to whatever `limits` is in [`Caps`] + limit: Option, +} + +impl SearchForm { + fn to_parameters(&self, conf: Config) -> SearchParameters { + // TODO: Clean up this code - split it into a separate function? + let mut categories: Option> = 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> = 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 = 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. // // 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> { // 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 - let conf = create_empty_config(); + let mut conf = create_empty_config(); unsafe { if CONFIG.is_none() { return (*STATUS_CONFIG_NOT_SPECIFIED).clone(); } 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> { } #[get("/api?t=search&")] +/// The search function for the API pub(crate) fn search(form: SearchForm) -> status::Custom> { // 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 @@ -66,56 +143,33 @@ pub(crate) fn search(form: SearchForm) -> status::Custom> { } } - // TODO: Clean up this code - split it into a separate function? - let mut apikey: String = "".to_string(); - if !form.apikey.is_none() { - apikey = form.apikey.ok_or("").unwrap(); - } - - let mut categories: Vec = 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 parameters = form.to_parameters(conf.clone()); + let mut unauthorized = false; match conf.auth { Some(auth) => { - if !auth(apikey).unwrap() { - return status::Custom( - Status::Unauthorized, - RawXml("401 Unauthorized".to_string()), - ); + match parameters.apikey { + Some(apikey) => { + if !auth(apikey).unwrap() { + return status::Custom( + Status::Unauthorized, + RawXml("401 Unauthorized".to_string()), + ); + } + } + None => { + unauthorized = true; + } } + // that unwrap_or_else is to return "" if the apikey isn't specified } None => {} } + if unauthorized { + return status::Custom(Status::Unauthorized, RawXml("401 Unauthorized".to_string())); + } + return status::Custom( Status::NotImplemented, RawXml("501 Not Implemented: Search function not implemented".to_string()), diff --git a/src/data.rs b/src/data.rs index 8f16e25..7606602 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,6 +1,6 @@ //! Contains tons of structs used by the library -use rocket::FromForm; +use rocket::FromForm; pub(crate) type AuthFunc = fn(String) -> Result; // TODO: Figure out what the arguments should be for a search function and what it should return pub(crate) type SearchFunc = fn(String, Vec) -> Result; @@ -126,24 +126,19 @@ pub struct Config { } #[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 -pub(crate) struct SearchForm { +pub(crate) struct SearchParameters { /// The text query for the search pub(crate) q: Option, /// The apikey, for authentication pub(crate) apikey: Option, - /// 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` - pub(crate) cat: Option, - /// 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` - pub(crate) attrs: Option, - /// Whether *all* extended attributes should be included in the search results; overrules `attrs` - /// Can be 0 or 1 - pub(crate) extended: Option, + /// A [`Vec`] containing the numeric category IDs to be included in the search results + pub(crate) categories: Option>, + /// A [`Vec`] containing the extended attribute names to be included in the search results + pub(crate) attributes: Option>, + /// Whether *all* extended attributes should be included in the search results; overrules `attributes` + pub(crate) extended_attrs: Option, /// How many items to skip/offset by in the results. pub(crate) offset: Option, /// The maximum number of items to return - also limited to whatever `limits` is in [`Caps`] - pub(crate) limit: Option, + pub(crate) limit: u32, }