diff --git a/Cargo.lock b/Cargo.lock index 719d9db..b136888 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 8f2b9dc..963f59b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/src/backends.rs b/src/backends.rs index f01cb00..f8e865c 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -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 { + 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; diff --git a/src/feed.rs b/src/feed.rs index 35a9019..31e41f2 100644 --- a/src/feed.rs +++ b/src/feed.rs @@ -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::>(); 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, @@ -93,7 +93,7 @@ fn construct_item( .collect::>(); 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()) diff --git a/src/lib.rs b/src/lib.rs index 012ee7a..08a4db3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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//")] -pub(crate) async fn get_download(file: PathBuf, backend: &str) -> Result { - match backend { - "mixcloud" => mixcloud::backend() - .redirect_url(&file) - .await - .map(Redirect::to), - _ => Err(Error::UnsupportedBackend(backend.to_string())), - } +#[get("/download//")] +pub(crate) async fn get_download(file: PathBuf, backend_id: &str) -> Result { + 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//?")] +#[get("/feed//?")] async fn get_feed( - backend: &str, + backend_id: &str, channel_id: &str, limit: Option, config: &State, ) -> Result { - 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())) }