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:
Paul van Tilburg 2022-08-15 20:17:34 +02:00
parent cb40f6b192
commit 49e0e47ba2
Signed by: paul
GPG Key ID: C6DE073EDA9EEC4D
5 changed files with 46 additions and 22 deletions

13
Cargo.lock generated
View File

@ -538,6 +538,18 @@ dependencies = [
"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]]
name = "fastrand"
version = "1.8.0"
@ -1521,6 +1533,7 @@ dependencies = [
"async-trait",
"cached",
"chrono",
"enum_dispatch",
"reqwest",
"rocket",
"rocket_dyn_templates",

View File

@ -11,6 +11,7 @@ license = "MIT"
async-trait = "0.1.57"
cached = { version = "0.38.0", features = ["async"] }
chrono = { version = "0.4.19", features = ["serde"] }
enum_dispatch = "0.3.8"
reqwest = { version = "0.11.10", features = ["json"] }
rocket = { version = "0.5.0-rc.2", features = ["json"] }
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }

View File

@ -9,14 +9,30 @@ use std::path::{Path, PathBuf};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use enum_dispatch::enum_dispatch;
use reqwest::Url;
use crate::Result;
use crate::{Error, Result};
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.
#[async_trait]
#[enum_dispatch]
pub(crate) trait Backend {
/// Returns the name of the backend.
fn name(&self) -> &'static str;

View File

@ -18,7 +18,7 @@ use crate::Config;
/// 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.
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()
.name(
channel
@ -41,7 +41,7 @@ pub(crate) fn construct(backend: &str, config: &Config, channel: Channel) -> rss
let items = channel
.items
.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<_>>();
let itunes_ext = ITunesChannelExtensionBuilder::default()
.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
/// value.
fn construct_item(
backend: &str,
backend_id: &str,
config: &Config,
item: Item,
last_build: &mut DateTime<Utc>,
@ -93,7 +93,7 @@ fn construct_item(
.collect::<Vec<_>>();
let url = uri!(
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()
.url(url.to_string())

View File

@ -18,7 +18,7 @@ use rocket::serde::{Deserialize, Serialize};
use rocket::{get, routes, Build, Request, Responder, Rocket, State};
use rocket_dyn_templates::{context, Template};
use crate::backends::{mixcloud, Backend};
use crate::backends::Backend;
pub(crate) mod backends;
pub(crate) mod feed;
@ -80,32 +80,26 @@ pub(crate) struct Config {
struct RssFeed(String);
/// Retrieves a download by redirecting to the URL resolved by the selected back-end.
#[get("/download/<backend>/<file..>")]
pub(crate) async fn get_download(file: PathBuf, backend: &str) -> Result<Redirect> {
match backend {
"mixcloud" => mixcloud::backend()
.redirect_url(&file)
.await
.map(Redirect::to),
_ => Err(Error::UnsupportedBackend(backend.to_string())),
}
#[get("/download/<backend_id>/<file..>")]
pub(crate) async fn get_download(file: PathBuf, backend_id: &str) -> Result<Redirect> {
let backend = backends::get(backend_id)?;
backend.redirect_url(&file).await.map(Redirect::to)
}
/// 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.
#[get("/feed/<backend>/<channel_id>?<limit>")]
#[get("/feed/<backend_id>/<channel_id>?<limit>")]
async fn get_feed(
backend: &str,
backend_id: &str,
channel_id: &str,
limit: Option<usize>,
config: &State<Config>,
) -> Result<RssFeed> {
let channel = match backend {
"mixcloud" => mixcloud::backend().channel(channel_id, limit).await?,
_ => return Err(Error::UnsupportedBackend(backend.to_string())),
};
let feed = feed::construct(backend, config, channel);
let backend = backends::get(backend_id)?;
let channel = backend.channel(channel_id, limit).await?;
let feed = feed::construct(backend_id, config, channel);
Ok(RssFeed(feed.to_string()))
}