mecomp_storage/db/schemas/
collection.rs1use 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 #[cfg_attr(feature = "db", field(dt = "record"))]
21 pub id: CollectionId,
22
23 #[cfg_attr(feature = "db", field(dt = "string"))]
25 #[cfg_attr(feature = "db", index(unique))]
26 pub name: String,
27
28 #[cfg_attr(
30 feature = "db",
31 field(
32 "TYPE any VALUE <future> {
33LET $songs = (SELECT runtime FROM $this.id->?->song);
34RETURN IF $songs IS NONE { 0s } ELSE { $songs.fold(0s, |$acc, $song| $acc + $song.runtime) };
35}"
36 )
37 )]
38 #[cfg_attr(
39 feature = "db",
40 serde(
41 serialize_with = "super::serialize_duration_as_sql_duration",
42 deserialize_with = "super::deserialize_duration_from_sql_duration"
43 )
44 )]
45 pub runtime: Duration,
46
47 #[cfg_attr(
49 feature = "db",
50 field(
51 "TYPE any VALUE <future> {
52LET $count = (SELECT count() FROM $this.id->?->song GROUP ALL);
53RETURN IF $count IS NONE { 0 } ELSE IF $count.len() == 0 { 0 } ELSE { ($count[0]).count };
54}"
55 )
56 )]
57 pub song_count: u64,
58}
59
60impl Collection {
61 pub const BRIEF_FIELDS: &'static [&'static str] = &["id", "name"];
62
63 #[must_use]
64 #[inline]
65 pub fn generate_id() -> CollectionId {
66 RecordId::from_table_key(TABLE_NAME, Id::ulid())
67 }
68}
69
70#[derive(Debug, Default)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize))]
72pub struct CollectionChangeSet {
73 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
74 pub name: Option<String>,
75}
76
77#[derive(Clone, Debug, PartialEq, Eq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub struct CollectionBrief {
80 pub id: CollectionId,
81 pub name: String,
82}
83
84impl From<Collection> for CollectionBrief {
85 #[inline]
86 fn from(collection: Collection) -> Self {
87 Self {
88 id: collection.id,
89 name: collection.name,
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 use pretty_assertions::assert_eq;
99 use rstest::fixture;
100
101 #[fixture]
102 fn collection() -> Collection {
103 Collection {
104 id: RecordId::from((TABLE_NAME, "id")),
105 name: "collection".into(),
106 runtime: Duration::from_secs(3600),
107 song_count: 100,
108 }
109 }
110
111 #[fixture]
112 fn collection_brief() -> CollectionBrief {
113 CollectionBrief {
114 id: RecordId::from((TABLE_NAME, "id")),
115 name: "collection".into(),
116 }
117 }
118
119 #[test]
120 fn test_collection_brief_from_collection() {
121 let actual: CollectionBrief = collection().into();
122 assert_eq!(actual, collection_brief());
123 }
124}