Introduce enum and enum dispatching for backends
This way handlers don't need to do case matching on backend ID strings anymore. * Rename `backend` to `backend_id` where we have a backend ID * Add `get` function and `Backends` enum to the `backend` module * Add a depend on the `enum_dispatch` crate
This commit is contained in:
parent
cb40f6b192
commit
49e0e47ba2
|
@ -538,6 +538,18 @@ dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum_dispatch"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -1521,6 +1533,7 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"cached",
|
"cached",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"enum_dispatch",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_dyn_templates",
|
"rocket_dyn_templates",
|
||||||
|
|
|
@ -11,6 +11,7 @@ license = "MIT"
|
||||||
async-trait = "0.1.57"
|
async-trait = "0.1.57"
|
||||||
cached = { version = "0.38.0", features = ["async"] }
|
cached = { version = "0.38.0", features = ["async"] }
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
|
enum_dispatch = "0.3.8"
|
||||||
reqwest = { version = "0.11.10", features = ["json"] }
|
reqwest = { version = "0.11.10", features = ["json"] }
|
||||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||||
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
||||||
|
|
|
@ -9,14 +9,30 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
|
||||||
use crate::Result;
|
use crate::{Error, Result};
|
||||||
|
|
||||||
pub(crate) mod mixcloud;
|
pub(crate) mod mixcloud;
|
||||||
|
|
||||||
|
/// Retrieves the back-end for the provided ID (if supported).
|
||||||
|
pub(crate) fn get(backend: &str) -> Result<Backends> {
|
||||||
|
match backend {
|
||||||
|
"mixcloud" => Ok(Backends::Mixcloud(mixcloud::backend())),
|
||||||
|
_ => Err(Error::UnsupportedBackend(backend.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The support back-ends.
|
||||||
|
#[enum_dispatch(Backend)]
|
||||||
|
pub(crate) enum Backends {
|
||||||
|
Mixcloud(mixcloud::Backend),
|
||||||
|
}
|
||||||
|
|
||||||
/// Functionality of a content back-end.
|
/// Functionality of a content back-end.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
#[enum_dispatch]
|
||||||
pub(crate) trait Backend {
|
pub(crate) trait Backend {
|
||||||
/// Returns the name of the backend.
|
/// Returns the name of the backend.
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::Config;
|
||||||
/// Constructs a feed as string from a back-end channel using the `rss` crate.
|
/// Constructs a feed as string from a back-end channel using the `rss` crate.
|
||||||
///
|
///
|
||||||
/// It requires the backend and configuration to be able to construct download URLs.
|
/// It requires the backend and configuration to be able to construct download URLs.
|
||||||
pub(crate) fn construct(backend: &str, config: &Config, channel: Channel) -> rss::Channel {
|
pub(crate) fn construct(backend_id: &str, config: &Config, channel: Channel) -> rss::Channel {
|
||||||
let category = CategoryBuilder::default()
|
let category = CategoryBuilder::default()
|
||||||
.name(
|
.name(
|
||||||
channel
|
channel
|
||||||
|
@ -41,7 +41,7 @@ pub(crate) fn construct(backend: &str, config: &Config, channel: Channel) -> rss
|
||||||
let items = channel
|
let items = channel
|
||||||
.items
|
.items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| construct_item(backend, config, item, &mut last_build))
|
.map(|item| construct_item(backend_id, config, item, &mut last_build))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let itunes_ext = ITunesChannelExtensionBuilder::default()
|
let itunes_ext = ITunesChannelExtensionBuilder::default()
|
||||||
.author(channel.author)
|
.author(channel.author)
|
||||||
|
@ -76,7 +76,7 @@ pub(crate) fn construct(backend: &str, config: &Config, channel: Channel) -> rss
|
||||||
/// It also bumps the last build timestamp if the last updated timestamp is later than the current
|
/// It also bumps the last build timestamp if the last updated timestamp is later than the current
|
||||||
/// value.
|
/// value.
|
||||||
fn construct_item(
|
fn construct_item(
|
||||||
backend: &str,
|
backend_id: &str,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
item: Item,
|
item: Item,
|
||||||
last_build: &mut DateTime<Utc>,
|
last_build: &mut DateTime<Utc>,
|
||||||
|
@ -93,7 +93,7 @@ fn construct_item(
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let url = uri!(
|
let url = uri!(
|
||||||
Absolute::parse(&config.url).expect("valid URL"),
|
Absolute::parse(&config.url).expect("valid URL"),
|
||||||
crate::get_download(backend = backend, file = item.enclosure.file)
|
crate::get_download(backend_id = backend_id, file = item.enclosure.file)
|
||||||
);
|
);
|
||||||
let enclosure = EnclosureBuilder::default()
|
let enclosure = EnclosureBuilder::default()
|
||||||
.url(url.to_string())
|
.url(url.to_string())
|
||||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -18,7 +18,7 @@ use rocket::serde::{Deserialize, Serialize};
|
||||||
use rocket::{get, routes, Build, Request, Responder, Rocket, State};
|
use rocket::{get, routes, Build, Request, Responder, Rocket, State};
|
||||||
use rocket_dyn_templates::{context, Template};
|
use rocket_dyn_templates::{context, Template};
|
||||||
|
|
||||||
use crate::backends::{mixcloud, Backend};
|
use crate::backends::Backend;
|
||||||
|
|
||||||
pub(crate) mod backends;
|
pub(crate) mod backends;
|
||||||
pub(crate) mod feed;
|
pub(crate) mod feed;
|
||||||
|
@ -80,32 +80,26 @@ pub(crate) struct Config {
|
||||||
struct RssFeed(String);
|
struct RssFeed(String);
|
||||||
|
|
||||||
/// Retrieves a download by redirecting to the URL resolved by the selected back-end.
|
/// Retrieves a download by redirecting to the URL resolved by the selected back-end.
|
||||||
#[get("/download/<backend>/<file..>")]
|
#[get("/download/<backend_id>/<file..>")]
|
||||||
pub(crate) async fn get_download(file: PathBuf, backend: &str) -> Result<Redirect> {
|
pub(crate) async fn get_download(file: PathBuf, backend_id: &str) -> Result<Redirect> {
|
||||||
match backend {
|
let backend = backends::get(backend_id)?;
|
||||||
"mixcloud" => mixcloud::backend()
|
|
||||||
.redirect_url(&file)
|
backend.redirect_url(&file).await.map(Redirect::to)
|
||||||
.await
|
|
||||||
.map(Redirect::to),
|
|
||||||
_ => Err(Error::UnsupportedBackend(backend.to_string())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for retrieving the RSS feed of a channel on a certain back-end.
|
/// Handler for retrieving the RSS feed of a channel on a certain back-end.
|
||||||
///
|
///
|
||||||
/// The limit parameter determines the maximum of items that can be in the feed.
|
/// The limit parameter determines the maximum of items that can be in the feed.
|
||||||
#[get("/feed/<backend>/<channel_id>?<limit>")]
|
#[get("/feed/<backend_id>/<channel_id>?<limit>")]
|
||||||
async fn get_feed(
|
async fn get_feed(
|
||||||
backend: &str,
|
backend_id: &str,
|
||||||
channel_id: &str,
|
channel_id: &str,
|
||||||
limit: Option<usize>,
|
limit: Option<usize>,
|
||||||
config: &State<Config>,
|
config: &State<Config>,
|
||||||
) -> Result<RssFeed> {
|
) -> Result<RssFeed> {
|
||||||
let channel = match backend {
|
let backend = backends::get(backend_id)?;
|
||||||
"mixcloud" => mixcloud::backend().channel(channel_id, limit).await?,
|
let channel = backend.channel(channel_id, limit).await?;
|
||||||
_ => return Err(Error::UnsupportedBackend(backend.to_string())),
|
let feed = feed::construct(backend_id, config, channel);
|
||||||
};
|
|
||||||
let feed = feed::construct(backend, config, channel);
|
|
||||||
|
|
||||||
Ok(RssFeed(feed.to_string()))
|
Ok(RssFeed(feed.to_string()))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue