You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.2 KiB
Rust

use std::{borrow::Cow, fmt::Display};
use askama::Template;
use serde::{Deserialize, Serialize};
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
use crate::{
providers::{
bandcamp::{BandcampArtistId, BandcampReleaseId},
spotify::SpotifyId,
Provider,
},
templates, Configuration,
};
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct FeedData {
pub(crate) artist: Artist,
pub(crate) releases: Vec<Release>,
pub(crate) retrieved_at: OffsetDateTime,
}
impl FeedData {
pub(crate) fn feed_id(&self) -> String {
let domain = &Configuration::get().http.domain;
format!(
"tag:{},2022:artist:{}:{}",
domain,
self.provider().id(),
self.artist_id(),
)
}
pub(crate) fn feed_url(&self) -> String {
let domain = &Configuration::get().http.domain;
format!(
"https://{}/releases/{}/{}",
domain,
self.provider().id(),
self.artist_id(),
)
}
pub(crate) fn provider(&self) -> Provider {
match &self.artist.id {
ArtistId::Spotify(_) => Provider::Spotify,
ArtistId::Bandcamp(_) => Provider::Bandcamp,
}
}
pub(crate) fn retrieved_at(&self) -> String {
self.retrieved_at.format(&Rfc3339).unwrap()
}
pub(crate) fn feed_entries(&self) -> impl Iterator<Item = (&'_ Release, String)> {
self.releases.iter().map(|r| {
let entry_content = templates::FeedEntry::new(r).render().unwrap();
(r, entry_content)
})
}
fn artist_id(&self) -> &dyn Display {
match &self.artist.id {
ArtistId::Spotify(id) => id,
ArtistId::Bandcamp(id) => id,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct Artist {
pub(crate) name: String,
pub(crate) id: ArtistId,
}
impl Artist {
pub(crate) fn url(&self) -> Cow<'_, str> {
match &self.id {
ArtistId::Spotify(id) => Cow::Owned(format!("https://open.spotify.com/artist/{}", id)),
ArtistId::Bandcamp(id) => Cow::Owned(id.url()),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) enum ArtistId {
Spotify(SpotifyId),
Bandcamp(BandcampArtistId),
}
impl ArtistId {
pub(crate) fn cache_key(&self) -> String {
format!("{}:{}", self.provider().id(), self.id())
}
fn provider(&self) -> Provider {
match self {
ArtistId::Spotify(_) => Provider::Spotify,
ArtistId::Bandcamp(_) => Provider::Bandcamp,
}
}
fn id(&self) -> &dyn Display {
match &self {
ArtistId::Spotify(id) => id,
ArtistId::Bandcamp(id) => id,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct Release {
pub(crate) id: ReleaseId,
pub(crate) name: String,
pub(crate) artists: Vec<Artist>,
pub(crate) release_date: OffsetDateTime,
pub(crate) cover_image: Option<String>,
}
impl Release {
pub(crate) fn entry_id(&self) -> String {
let domain = &Configuration::get().http.domain;
format!(
"tag:{},2022:release:{}:{}",
domain,
self.provider().id(),
self.id()
)
}
pub(crate) fn artists(&self) -> &[Artist] {
&self.artists
}
pub(crate) fn release_date(&self) -> String {
self.release_date.format(&Rfc3339).unwrap()
}
pub(crate) fn url(&self) -> String {
match &self.id {
ReleaseId::Spotify(id) => format!("https://open.spotify.com/album/{}", id),
ReleaseId::Bandcamp(id) => id.to_string(),
}
}
pub(crate) fn provider(&self) -> Provider {
match &self.id {
ReleaseId::Spotify(_) => Provider::Spotify,
ReleaseId::Bandcamp(_) => Provider::Bandcamp,
}
}
pub(crate) fn cover_art(&self) -> Option<&str> {
self.cover_image.as_deref()
}
fn id(&self) -> &dyn Display {
match &self.id {
ReleaseId::Spotify(id) => id,
ReleaseId::Bandcamp(id) => id,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) enum ReleaseId {
Spotify(SpotifyId),
Bandcamp(BandcampReleaseId),
}