mecomp_storage/db/schemas/
collection.rs1#![allow(clippy::module_name_repetitions)]
2use 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 #[cfg_attr(feature = "db", field("any"))]
23 pub id: CollectionId,
24
25 #[cfg_attr(feature = "db", field(dt = "string", index(unique)))]
27 pub name: Arc<str>,
28
29 #[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 #[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}