1use std::time::{Duration, UNIX_EPOCH};
2
3use rusqlite::{named_params, Connection, Row};
4
5use crate::track::Track;
6
7#[derive(Clone, Debug)]
9pub struct TrackDB {
10 pub id: u64,
11 pub artist: String,
12 pub title: String,
13 pub album: String,
14 pub genre: String,
15 pub file: String,
16 pub duration: Duration,
17 pub name: String,
18 pub ext: String,
19 pub directory: String,
20 pub last_modified: String,
21 pub last_position: Duration,
22}
23
24impl TrackDB {
25 pub fn try_from_row_id(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
29 let d_u64: u64 = row.get(6)?;
30 let last_position_u64: u64 = row.get(11)?;
31 Ok(TrackDB {
32 id: row.get(0)?,
33 artist: row.get(1)?,
34 title: row.get(2)?,
35 album: row.get(3)?,
36 genre: row.get(4)?,
37 file: row.get(5)?,
38 duration: Duration::from_secs(d_u64),
39 name: row.get(7)?,
40 ext: row.get(8)?,
41 directory: row.get(9)?,
42 last_modified: row.get(10)?,
43 last_position: Duration::from_secs(last_position_u64),
44 })
45 }
46
47 pub fn try_from_row_named(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
49 let d_u64: u64 = row.get("duration")?;
51 let last_position_u64: u64 = row.get("last_position")?;
52 Ok(TrackDB {
53 id: row.get("id")?,
54 artist: row.get("artist")?,
55 title: row.get("title")?,
56 album: row.get("album")?,
57 genre: row.get("genre")?,
58 file: row.get("file")?,
59 duration: Duration::from_secs(d_u64),
60 name: row.get("name")?,
61 ext: row.get("ext")?,
62 directory: row.get("directory")?,
63 last_modified: row.get("last_modified")?,
64 last_position: Duration::from_secs(last_position_u64),
65 })
66 }
67}
68
69#[derive(Clone, Debug)]
73pub struct TrackDBInsertable<'a> {
74 pub artist: &'a str,
77 pub title: &'a str,
78 pub album: &'a str,
79 pub genre: &'a str,
80 pub file: &'a str,
81 pub duration: Duration,
82 pub name: &'a str,
83 pub ext: &'a str,
84 pub directory: &'a str,
85 pub last_modified: String,
86 pub last_position: Duration,
87}
88
89pub mod const_unknown {
91 use crate::const_str;
92
93 const_str! {
94 UNKNOWN_ARTIST "Unknown Artist",
95 UNKNOWN_TITLE "Unknown Title",
96 UNKNOWN_ALBUM "empty",
97 UNKNOWN_GENRE "no type",
98 UNKNOWN_FILE "Unknown File",
99 }
100
101 #[allow(unused)]
106 pub const OLD_UNSUPPORTED: &str = "Unsupported?";
107
108 }
111use const_unknown::{UNKNOWN_ALBUM, UNKNOWN_ARTIST, UNKNOWN_FILE, UNKNOWN_GENRE, UNKNOWN_TITLE};
112
113impl<'a> From<&'a Track> for TrackDBInsertable<'a> {
114 fn from(value: &'a Track) -> Self {
115 Self {
116 artist: value.artist().unwrap_or(UNKNOWN_ARTIST),
117 title: value.title().unwrap_or(UNKNOWN_TITLE),
118 album: value.album().unwrap_or(UNKNOWN_ALBUM),
119 genre: value.genre().unwrap_or(UNKNOWN_GENRE),
120 file: value.file().unwrap_or(UNKNOWN_FILE),
121 duration: value.duration(),
122 name: value.name().unwrap_or_default(),
123 ext: value.ext().unwrap_or_default(),
124 directory: value.directory().unwrap_or_default(),
125 last_modified: value
126 .last_modified
127 .duration_since(UNIX_EPOCH)
128 .unwrap_or_default()
129 .as_secs()
130 .to_string(),
131 last_position: Duration::default(),
132 }
133 }
134}
135
136impl TrackDBInsertable<'_> {
137 #[inline]
139 pub fn insert_track(&self, con: &Connection) -> Result<usize, rusqlite::Error> {
140 con.execute(
141 "INSERT INTO tracks (artist, title, album, genre, file, duration, name, ext, directory, last_modified, last_position)
142 values (:artist, :title, :album, :genre, :file, :duration, :name, :ext, :directory, :last_modified, :last_position)",
143 named_params![
144 ":artist": &self.artist,
145 ":title": &self.title,
146 ":album": &self.album,
147 ":genre": &self.genre,
148 ":file": &self.file,
149 ":duration": &self.duration.as_secs(),
150 ":name": &self.name,
151 ":ext": &self.ext,
152 ":directory": &self.directory,
153 ":last_modified": &self.last_modified,
154 ":last_position": &self.last_position.as_secs().to_string(),
155 ],
156 )
157 }
158}
159
160pub trait Indexable {
165 fn meta_file(&self) -> Option<&str>;
166 fn meta_title(&self) -> Option<&str>;
167 fn meta_album(&self) -> Option<&str>;
168 fn meta_artist(&self) -> Option<&str>;
169 fn meta_genre(&self) -> Option<&str>;
170 fn meta_duration(&self) -> Duration;
171}
172
173impl Indexable for Track {
174 fn meta_file(&self) -> Option<&str> {
175 self.file()
176 }
177 fn meta_title(&self) -> Option<&str> {
178 self.title()
179 }
180 fn meta_album(&self) -> Option<&str> {
181 self.album()
182 }
183 fn meta_artist(&self) -> Option<&str> {
184 self.artist()
185 }
186 fn meta_genre(&self) -> Option<&str> {
187 self.genre()
188 }
189 fn meta_duration(&self) -> Duration {
190 self.duration()
191 }
192}
193
194impl Indexable for TrackDB {
195 fn meta_file(&self) -> Option<&str> {
196 if self.file == UNKNOWN_FILE {
197 return None;
198 }
199 Some(&self.file)
200 }
201 fn meta_title(&self) -> Option<&str> {
202 if self.title == UNKNOWN_TITLE {
203 return None;
204 }
205 Some(&self.title)
206 }
207 fn meta_album(&self) -> Option<&str> {
208 if self.album == UNKNOWN_ALBUM {
209 return None;
210 }
211 Some(&self.album)
212 }
213 fn meta_artist(&self) -> Option<&str> {
214 if self.artist == UNKNOWN_ARTIST {
215 return None;
216 }
217 Some(&self.artist)
218 }
219 fn meta_genre(&self) -> Option<&str> {
220 if self.genre == UNKNOWN_GENRE {
221 return None;
222 }
223 Some(&self.genre)
224 }
225
226 fn meta_duration(&self) -> Duration {
227 self.duration
228 }
229}
230
231impl Indexable for &Track {
232 fn meta_file(&self) -> Option<&str> {
233 self.file()
234 }
235 fn meta_title(&self) -> Option<&str> {
236 self.title()
237 }
238 fn meta_album(&self) -> Option<&str> {
239 self.album()
240 }
241 fn meta_artist(&self) -> Option<&str> {
242 self.artist()
243 }
244 fn meta_genre(&self) -> Option<&str> {
245 self.genre()
246 }
247 fn meta_duration(&self) -> Duration {
248 self.duration()
249 }
250}
251
252impl Indexable for &TrackDB {
253 fn meta_file(&self) -> Option<&str> {
254 if self.file == UNKNOWN_FILE {
255 return None;
256 }
257 Some(&self.file)
258 }
259 fn meta_title(&self) -> Option<&str> {
260 if self.title == UNKNOWN_TITLE {
261 return None;
262 }
263 Some(&self.title)
264 }
265 fn meta_album(&self) -> Option<&str> {
266 if self.album == UNKNOWN_ALBUM {
267 return None;
268 }
269 Some(&self.album)
270 }
271 fn meta_artist(&self) -> Option<&str> {
272 if self.artist == UNKNOWN_ARTIST {
273 return None;
274 }
275 Some(&self.artist)
276 }
277 fn meta_genre(&self) -> Option<&str> {
278 if self.genre == UNKNOWN_GENRE {
279 return None;
280 }
281 Some(&self.genre)
282 }
283
284 fn meta_duration(&self) -> Duration {
285 self.duration
286 }
287}