mecomp_storage/db/schemas/
collection.rs

1#![allow(clippy::module_name_repetitions)]
2//! A collection is an auto currated list of similar songs.
3
4use std::sync::Arc;
5
6#[cfg(not(feature = "db"))]
7use super::{Id, Thing};
8use std::time::Duration;
9#[cfg(feature = "db")]
10use surrealdb::sql::{Id, Thing};
11
12pub type CollectionId = Thing;
13
14pub const TABLE_NAME: &str = "collection";
15
16#[derive(Clone, Debug, PartialEq, Eq)]
17#[cfg_attr(feature = "db", derive(surrealqlx::Table))]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "db", Table("collection"))]
20pub struct Collection {
21    /// the unique identifier for this [`Collection`].
22    #[cfg_attr(feature = "db", field("any"))]
23    pub id: CollectionId,
24
25    /// The name of the collection.
26    #[cfg_attr(feature = "db", field(dt = "string", index(unique)))]
27    pub name: Arc<str>,
28
29    /// Total runtime.
30    #[cfg_attr(feature = "db", field(dt = "duration"))]
31    #[cfg_attr(
32        feature = "db",
33        serde(
34            serialize_with = "super::serialize_duration_as_sql_duration",
35            deserialize_with = "super::deserialize_duration_from_sql_duration"
36        )
37    )]
38    pub runtime: Duration,
39
40    /// the number of songs this collection has.
41    #[cfg_attr(feature = "db", field(dt = "int"))]
42    pub song_count: usize,
43}
44
45impl Collection {
46    #[must_use]
47    #[inline]
48    pub fn generate_id() -> CollectionId {
49        Thing::from((TABLE_NAME, Id::ulid()))
50    }
51}
52
53#[derive(Debug, Default)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55pub struct CollectionChangeSet {
56    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
57    pub name: Option<Arc<str>>,
58    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
59    #[cfg_attr(
60        feature = "db",
61        serde(serialize_with = "super::serialize_duration_option_as_sql_duration")
62    )]
63    pub runtime: Option<Duration>,
64    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
65    pub song_count: Option<usize>,
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub struct CollectionBrief {
71    pub id: CollectionId,
72    pub name: Arc<str>,
73    pub runtime: std::time::Duration,
74    pub songs: usize,
75}
76
77impl From<Collection> for CollectionBrief {
78    #[inline]
79    fn from(collection: Collection) -> Self {
80        Self {
81            id: collection.id,
82            name: collection.name,
83            runtime: collection.runtime,
84            songs: collection.song_count,
85        }
86    }
87}
88
89impl From<&Collection> for CollectionBrief {
90    #[inline]
91    fn from(collection: &Collection) -> Self {
92        Self {
93            id: collection.id.clone(),
94            name: collection.name.clone(),
95            runtime: collection.runtime,
96            songs: collection.song_count,
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    use pretty_assertions::assert_eq;
106    use rstest::{fixture, rstest};
107
108    #[fixture]
109    fn collection() -> Collection {
110        Collection {
111            id: Thing::from((TABLE_NAME, "id")),
112            name: Arc::from("collection"),
113            runtime: Duration::from_secs(3600),
114            song_count: 100,
115        }
116    }
117
118    #[fixture]
119    fn collection_brief() -> CollectionBrief {
120        CollectionBrief {
121            id: Thing::from((TABLE_NAME, "id")),
122            name: Arc::from("collection"),
123            runtime: Duration::from_secs(3600),
124            songs: 100,
125        }
126    }
127
128    #[rstest]
129    #[case(collection(), collection_brief())]
130    #[case(&collection(), collection_brief())]
131    fn test_collection_brief_from_collection<T: Into<CollectionBrief>>(
132        #[case] collection: T,
133        #[case] brief: CollectionBrief,
134    ) {
135        let actual: CollectionBrief = collection.into();
136        assert_eq!(actual, brief);
137    }
138}