mecomp_storage/db/schemas/
collection.rs

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