Skip to main content

selene_core/library/artist/
trait_impls.rs

1use std::convert::Infallible;
2
3use chrono::{DateTime, Utc};
4use lunar_lib::database::{
5    CompareAndSwapTransaction, CustomTransactionError, DatabaseEntry, DatabaseError,
6    TransactionError, Tree,
7};
8
9use crate::{
10    database::{
11        Createable, LibraryDb, Patchable, artist_tree, patch_option_replace, patch_replace,
12        patch_vec, tx_extensions::CasTxExtensions,
13    },
14    library::{
15        album::Album,
16        artist::{Artist, ArtistId},
17        track::Track,
18    },
19};
20
21impl std::hash::Hash for Artist {
22    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
23        state.write(&*self.id);
24    }
25}
26
27impl PartialEq for Artist {
28    fn eq(&self, other: &Self) -> bool {
29        self.id() == other.id()
30    }
31}
32
33impl Eq for Artist {}
34
35impl DatabaseEntry for Artist {
36    type Id = ArtistId;
37    type EntryDb = LibraryDb;
38    const VERSION_NUMBER: u32 = 1;
39
40    fn tree(db: &Self::EntryDb) -> Tree {
41        artist_tree(db)
42    }
43
44    fn id(&self) -> Self::Id {
45        self.id
46    }
47
48    fn pre_upsert(
49        &mut self,
50        cas_tx: &CompareAndSwapTransaction<Self::EntryDb>,
51    ) -> Result<(), TransactionError> {
52        let mut albums = self.tx_albums(cas_tx)?;
53
54        albums.sort_unstable_by(|a, b| {
55            let a_date = a.date.unwrap_or(DateTime::<Utc>::MAX_UTC);
56            let b_date = b.date.unwrap_or(DateTime::<Utc>::MAX_UTC);
57            a_date.cmp(&b_date).then(a.name.cmp(&b.name))
58        });
59
60        self.albums = albums.iter().map(Album::id).collect();
61
62        Ok(())
63    }
64}
65
66impl Patchable<Self> for Artist {
67    fn patch(&mut self, patch: Self) {
68        let Artist {
69            id: _,
70            name,
71            cover_art,
72            description,
73            tracks,
74            albums,
75            version: _,
76        } = patch;
77
78        patch_option_replace(&mut self.cover_art, cover_art);
79        patch_option_replace(&mut self.description, description);
80        patch_replace(&mut self.name, Some(name));
81        patch_vec(&mut self.tracks, tracks);
82        patch_vec(&mut self.albums, albums);
83    }
84}
85
86impl std::ops::Deref for Artist {
87    type Target = ArtistId;
88
89    fn deref(&self) -> &Self::Target {
90        &self.id
91    }
92}
93
94#[derive(Debug, Clone)]
95pub struct ArtistCreateArgs {
96    pub name: String,
97    pub albums: Vec<Album>,
98    pub tracks: Vec<Track>,
99}
100
101impl ArtistCreateArgs {
102    #[must_use]
103    pub fn new(name: String, albums: Vec<Album>, tracks: Vec<Track>) -> Self {
104        Self {
105            name,
106            albums,
107            tracks,
108        }
109    }
110}
111
112impl Createable for Artist {
113    type CreateArgs = ArtistCreateArgs;
114    type Err = Infallible;
115
116    fn tx_create(
117        cas_tx: &mut CompareAndSwapTransaction<Self::EntryDb>,
118        args: Self::CreateArgs,
119    ) -> Result<Self, CustomTransactionError<Self::Err>> {
120        let mut artist = Artist::new(args.name).ok_or(TransactionError::Database(
121            DatabaseError::InvalidInput("Name field must not be empty".to_owned()),
122        ))?;
123        let artist_id = artist.id();
124
125        for track in &args.tracks {
126            if !artist.tracks.contains(&track.id()) {
127                artist.tracks.push(track.id());
128            }
129        }
130
131        cas_tx.tx_insert(artist.clone())?;
132
133        for mut track in args.tracks {
134            track.metadata.artists.add_artist(artist_id);
135            cas_tx.tx_patch(track)?;
136        }
137
138        Ok(artist)
139    }
140}