- update function signatures
- add tests (doesn't verify anything, but useful to manually test something/run bits) - improve documentation
This commit is contained in:
parent
cdb0a82181
commit
52a8c48825
5 changed files with 189 additions and 65 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -1255,7 +1255,8 @@ name = "torznab-toolkit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rocket",
|
"rocket",
|
||||||
"xml",
|
"serde",
|
||||||
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1568,15 +1569,6 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xml"
|
|
||||||
version = "0.8.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ede1c99c55b4b3ad0349018ef0eccbe954ce9c342334410707ee87177fcf2ab4"
|
|
||||||
dependencies = [
|
|
||||||
"xml-rs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.23"
|
version = "0.8.23"
|
||||||
|
|
|
@ -5,4 +5,5 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = "0.5.1"
|
rocket = "0.5.1"
|
||||||
xml = "0.8.20"
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
|
xml-rs = "0.8.23"
|
||||||
|
|
135
src/api.rs
135
src/api.rs
|
@ -1,7 +1,134 @@
|
||||||
|
//! Contains the actual Torznab API
|
||||||
|
use std::io::stdout;
|
||||||
|
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
use rocket::get;
|
use rocket::get;
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::response::status;
|
||||||
|
use xml::writer::{EmitterConfig, XmlEvent};
|
||||||
|
|
||||||
/// A struct that holds configuration for torznab-toolkit
|
|
||||||
/// A search function (/api?t=search) and capabilities (/api?t=caps - struct Caps) required
|
/// Holds the config for torznab-toolkit.
|
||||||
/// Everything else is optional
|
///
|
||||||
pub static mut config: Option<Config> = None;
|
/// A search function (`/api?t=search`) and capabilities (`/api?t=caps` - `Caps`) are required, everything else is optional.
|
||||||
|
///
|
||||||
|
/// <div class="warning">It's required to be set to <i>something</i>, which is why it's an Option set to None.
|
||||||
|
///
|
||||||
|
/// However, this is NOT optional, and attempting to do anything with CONFIG not set will return an `Err`.</div>
|
||||||
|
pub static mut CONFIG: Option<Config> = None;
|
||||||
|
|
||||||
|
/// Capabilities API endpoint (`/api?t=caps`)
|
||||||
|
// FIXME: VERY incomplete
|
||||||
|
// TODO: Finish it (duh) and add optional apikey
|
||||||
|
#[get("/api?t=caps")]
|
||||||
|
pub(crate) fn caps() -> status::Custom<String> {
|
||||||
|
unsafe {
|
||||||
|
if CONFIG == None {
|
||||||
|
return status::Custom(
|
||||||
|
Status::InternalServerError,
|
||||||
|
"500 Internal server error: Config not specified".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = stdout();
|
||||||
|
let mut writer = EmitterConfig::new().create_writer(output);
|
||||||
|
|
||||||
|
writer.write(XmlEvent::start_element("caps")).unwrap();
|
||||||
|
writer.write(XmlEvent::start_element("server")).unwrap();
|
||||||
|
writer.write(XmlEvent::end_element()).unwrap();
|
||||||
|
writer.write(XmlEvent::start_element("caps")).unwrap();
|
||||||
|
writer.write(XmlEvent::end_element()).unwrap();
|
||||||
|
return status::Custom(Status::Ok, stringify!(writer).to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn dummy_search_func(a: String, b: Vec<String>) -> Result<String, String> {
|
||||||
|
return Ok("hi".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dummy_auth_func(a: String) -> Result<bool, String> {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_config() -> Config {
|
||||||
|
let mut searching = Vec::new();
|
||||||
|
searching.push(SearchInfo {
|
||||||
|
search_type: "search".to_string(),
|
||||||
|
available: true,
|
||||||
|
supported_params: vec!["id".to_string()],
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut subcategories = Vec::new();
|
||||||
|
subcategories.push(Subcategory {
|
||||||
|
id: "a".to_string(),
|
||||||
|
name: "b".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut categories = Vec::new();
|
||||||
|
categories.push(Category {
|
||||||
|
id: "a".to_string(),
|
||||||
|
name: "b".to_string(),
|
||||||
|
subcategories: subcategories,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut genres = Vec::new();
|
||||||
|
genres.push(Genre {
|
||||||
|
id: "a".to_string(),
|
||||||
|
category_id: "b".to_string(),
|
||||||
|
name: "c".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut tags = Vec::new();
|
||||||
|
tags.push(Tag {
|
||||||
|
id: "a".to_string(),
|
||||||
|
category_id: "b".to_string(),
|
||||||
|
name: "c".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Config {
|
||||||
|
search: dummy_search_func,
|
||||||
|
auth: Some(dummy_auth_func),
|
||||||
|
caps: Caps {
|
||||||
|
server_info: ServerInfo {
|
||||||
|
title: Some("Test Torznab server".to_string()),
|
||||||
|
email: Some("test@example.com".to_string()),
|
||||||
|
image: None,
|
||||||
|
version: Some("1.0".to_string()),
|
||||||
|
},
|
||||||
|
limits: Limits {
|
||||||
|
max: 100,
|
||||||
|
default: 20,
|
||||||
|
},
|
||||||
|
searching: searching,
|
||||||
|
categories: categories,
|
||||||
|
genres: Some(genres),
|
||||||
|
tags: Some(tags),
|
||||||
|
},
|
||||||
|
book: None,
|
||||||
|
movie: None,
|
||||||
|
music: None,
|
||||||
|
tvsearch: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_with_config() {
|
||||||
|
unsafe {
|
||||||
|
CONFIG = Some(create_config());
|
||||||
|
println!("{:?}", CONFIG);
|
||||||
|
}
|
||||||
|
caps();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_config() {
|
||||||
|
unsafe {
|
||||||
|
println!("{:?}", CONFIG);
|
||||||
|
}
|
||||||
|
caps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
98
src/data.rs
98
src/data.rs
|
@ -1,23 +1,25 @@
|
||||||
pub(crate) type AuthFunc = fn(String) -> Result<String, String>;
|
//! Contains tons of structs used by the library
|
||||||
|
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
|
||||||
pub(crate) type SearchFunc = fn(String, Vec<String>) -> Result<String, String>;
|
pub(crate) type SearchFunc = fn(String, Vec<String>) -> Result<String, String>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// Specify the ServerInfo to be listed in <server> for `/api?t=caps`
|
||||||
|
///
|
||||||
|
/// These fields are just those listed in the example on [torznab.github.io](https://torznab.github.io), there's no actual specification for thse fields.
|
||||||
|
/// TODO: Update this to have customizable fields instead
|
||||||
pub struct ServerInfo {
|
pub struct ServerInfo {
|
||||||
/// Specify the ServerInfo to be listed in <server> for `/api?t=caps`
|
|
||||||
///
|
|
||||||
/// These fields are just those listed in the example on [torznab.github.io](https://torznab.github.io), there's no actual specification for thse fields.
|
|
||||||
/// TODO: Update this to have customizable fields instead
|
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// The maximum and defaults for the `limit` parameter in queries
|
||||||
|
/// `max` is the maximum number of results the program can return
|
||||||
|
/// `default` is the default number of results the program will return
|
||||||
pub struct Limits {
|
pub struct Limits {
|
||||||
/// The maximum and defaults for the `limit` parameter in queries
|
|
||||||
/// `max` is the maximum number of results the program can return
|
|
||||||
/// `default` is the default number of results the program will return
|
|
||||||
/*
|
/*
|
||||||
I don't know why this would possibly need to be a u64, I can't imagine you'll be returning 18 quintillion results or whatever
|
I don't know why this would possibly need to be a u64, I can't imagine you'll be returning 18 quintillion results or whatever
|
||||||
In fact, I *really* hope you aren't - if you are, you're doing something extremely wrong
|
In fact, I *really* hope you aren't - if you are, you're doing something extremely wrong
|
||||||
|
@ -27,68 +29,72 @@ pub struct Limits {
|
||||||
pub default: u64,
|
pub default: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// A struct holding the info for a type of search
|
||||||
|
/// - `search_type` must be `search`, `tv-search`, `movie-search`, `audio-search`, or `book-search`
|
||||||
|
/// - `available`
|
||||||
pub struct SearchInfo {
|
pub struct SearchInfo {
|
||||||
/// A struct holding the info for a type of search
|
|
||||||
/// - `search_type` must be `search`, `tv-search`, `movie-search`, `audio-search`, or `book-search`
|
|
||||||
/// - `available`
|
|
||||||
pub search_type: String,
|
pub search_type: String,
|
||||||
pub available: bool,
|
pub available: bool,
|
||||||
pub supported_params: Vec<String>,
|
pub supported_params: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// Contains subcategories, for use in `Category`
|
||||||
pub struct Subcategory {
|
pub struct Subcategory {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// Contains a category, for use in `Caps` and searches as a query parameter
|
||||||
pub struct Category {
|
pub struct Category {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub subcategories: Vec<Subcategory>,
|
pub subcategories: Vec<Subcategory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// Contains a genre, for use in `Caps` and searches as a query parameter
|
||||||
pub struct Genre {
|
pub struct Genre {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub category_id: String,
|
pub category_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// Contains a tag, for use in `Caps` and searches as a query parameter
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub category_id: String,
|
pub category_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// Holds the configuration for the capabilities of the Torznab server
|
||||||
|
///
|
||||||
|
/// - server_info: `ServerInfo`
|
||||||
|
/// - see: `ServerInfo` docs
|
||||||
|
/// - limits: `Limits`
|
||||||
|
/// - specifies the max and default items listed when searching
|
||||||
|
/// - see: `Limits` docs
|
||||||
|
/// - searching: `Vec<SearchInfo>`
|
||||||
|
/// - specifies the capabilities of each search mode
|
||||||
|
/// - see: `SearchInfo` docs
|
||||||
|
/// - categories: `Vec<Category>`
|
||||||
|
/// - lists known categories
|
||||||
|
/// - see: `Category` docs
|
||||||
|
/// - genres: `Option<Vec<Genre>>`
|
||||||
|
/// - lists known genres, optional
|
||||||
|
/// - see: `Genre` docs
|
||||||
|
///
|
||||||
|
/// <div class="warning">Note that this library might not support all the capabilities listed in yet, so check the README before listing capabilities, or just accept that unsupported capabilities will return error 404.
|
||||||
|
///
|
||||||
|
/// It's recommended to add any capabilities you want, and set `available` to `false` in the `Caps` struct for any currently unsupported search types.</div>
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// TODO: Add a way to partially(?) generate automatically from the Config
|
||||||
pub struct Caps {
|
pub struct Caps {
|
||||||
/// Holds the configuration for the capabilities of the Torznab server
|
|
||||||
///
|
|
||||||
/// - server_info: `ServerInfo`
|
|
||||||
/// - see: `ServerInfo` docs
|
|
||||||
/// - limits: `Limits`
|
|
||||||
/// - specifies the max and default items listed when searching
|
|
||||||
/// - see: `Limits` docs
|
|
||||||
/// - searching: `Vec<SearchInfo>`
|
|
||||||
/// - specifies the capabilities of each search mode
|
|
||||||
/// - see: `SearchInfo` docs
|
|
||||||
/// - categories: `Vec<Category>`
|
|
||||||
/// - lists known categories
|
|
||||||
/// - see: `Category` docs
|
|
||||||
/// - genres: `Option<Vec<Genre>>`
|
|
||||||
/// - lists known genres, optional
|
|
||||||
/// - see: `Genre` docs
|
|
||||||
///
|
|
||||||
/// <div class="warning">Note that this library might not support all the capabilities listed in yet, so check the README before listing capabilities, or just accept that unsupported capabilities will return error 404.
|
|
||||||
///
|
|
||||||
/// It's recommended to add any capabilities you want, and set `available` to `false` in the `Caps` struct for any currently unsupported search types.</div>
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// TODO: Add a way to partially(?) generate automatically from the Config
|
|
||||||
pub server_info: ServerInfo,
|
pub server_info: ServerInfo,
|
||||||
pub limits: Limits,
|
pub limits: Limits,
|
||||||
pub searching: Vec<SearchInfo>,
|
pub searching: Vec<SearchInfo>,
|
||||||
|
@ -97,11 +103,11 @@ pub struct Caps {
|
||||||
pub tags: Option<Vec<Tag>>,
|
pub tags: Option<Vec<Tag>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
/// A struct that holds configuration for torznab-toolkit
|
||||||
|
/// A search function (/api?t=search) and capabilities (/api?t=caps - struct Caps) required
|
||||||
|
/// Everything else is optional
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// A struct that holds configuration for torznab-toolkit
|
|
||||||
/// A search function (/api?t=search) and capabilities (/api?t=caps - struct Caps) required
|
|
||||||
/// Everything else is optional
|
|
||||||
pub search: SearchFunc, // NOTE: This is NOT optional,
|
pub search: SearchFunc, // NOTE: This is NOT optional,
|
||||||
pub auth: Option<AuthFunc>,
|
pub auth: Option<AuthFunc>,
|
||||||
pub caps: Caps,
|
pub caps: Caps,
|
||||||
|
|
|
@ -23,10 +23,8 @@ pub mod data;
|
||||||
|
|
||||||
use rocket::{self};
|
use rocket::{self};
|
||||||
|
|
||||||
|
/// Runs the server
|
||||||
pub fn run(conf: data::Config, caps: data::Caps) -> Result<bool, String> {
|
pub fn run(conf: data::Config, caps: data::Caps) -> Result<bool, String> {
|
||||||
/// Runs the server
|
rocket::build().mount("/", rocket::routes![]).launch();
|
||||||
//rocket::build()
|
|
||||||
// .mount("/", rocket::routes![conf.caps])
|
|
||||||
// .launch();
|
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue