1use crate::fields::{ColumnField, FieldValue, NdeField};
54
55use log::error;
56use parse_display::Display;
57use serde::Serialize;
58
59use std::collections::HashMap;
60
61#[derive(Debug, Display)]
66pub enum Cause {
67 #[display("An error in another crate or module-- cf. source.")]
69 Other,
70 #[display("No filename field found.")]
72 NoFilename,
73}
74
75#[derive(Debug, Display)]
76#[display("{cause} Source (if any): {source} Stack trace (if any): {trace}")]
77pub struct Error {
78 #[display("XNDE error {}.")]
80 cause: Cause,
81 #[display("fields error caused by {:#?}.")]
89 source: Option<Box<dyn std::error::Error>>,
90 #[display("backtrace: {:#?}.")]
93 trace: Option<backtrace::Backtrace>,
94}
95
96impl Error {
97 fn new(cause: Cause) -> Error {
98 Error {
100 cause: cause,
101 source: None,
102 trace: Some(backtrace::Backtrace::new()),
103 }
104 }
105}
106
107impl std::error::Error for Error {
108 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
110 match &self.source {
111 Some(bx) => Some(bx.as_ref()),
117 None => None,
118 }
119 }
120}
121
122pub type Result<T> = std::result::Result<T, Error>;
123
124#[derive(Debug, Eq, Hash, PartialEq)]
128pub enum TrackAttrs {
129 Filename,
130 Artist,
131 Title,
132 Album,
133 Year,
134 Genre,
135 Comment,
136 TrackNo,
137 Length,
138 Type,
139 LastUpd,
140 LastPlay,
141 Rating,
142 Tuid2,
143 PlayCount,
144 Filetime,
145 Filesize,
146 Bitrate,
147 Disc,
148 Albumartist,
149 ReplaygainAlbumGain,
150 ReplaygainTrackGain,
151 Publisher,
152 Composer,
153 Bpm,
154 Discs,
155 Tracks,
156 IsPodcast,
157 PodcastChannel,
158 PodcastPubdate,
159 GracenoteFileId,
160 GracenoteExtData,
161 Lossless,
162 Category,
163 Codec,
164 Director,
165 Producer,
166 Width,
167 Height,
168 MimeType,
169 DateAdded,
170}
171
172pub type ColumnMap = HashMap<i32, TrackAttrs>;
174
175pub fn new_column_map<'a, CI>(cols: CI) -> ColumnMap
177where
178 CI: Iterator<Item = &'a ColumnField>,
179{
180 let mut col_map: HashMap<i32, TrackAttrs> = HashMap::new();
181 for col in cols {
182 let id = col.id();
183 match col.name().as_str() {
184 "filename" => {
185 col_map.insert(id, TrackAttrs::Filename);
186 }
187 "artist" => {
188 col_map.insert(id, TrackAttrs::Artist);
189 }
190 "title" => {
191 col_map.insert(id, TrackAttrs::Title);
192 }
193 "album" => {
194 col_map.insert(id, TrackAttrs::Album);
195 }
196 "year" => {
197 col_map.insert(id, TrackAttrs::Year);
198 }
199 "genre" => {
200 col_map.insert(id, TrackAttrs::Genre);
201 }
202 "comment" => {
203 col_map.insert(id, TrackAttrs::Comment);
204 }
205 "trackno" => {
206 col_map.insert(id, TrackAttrs::TrackNo);
207 }
208 "length" => {
209 col_map.insert(id, TrackAttrs::Length);
210 }
211 "type" => {
212 col_map.insert(id, TrackAttrs::Type);
213 }
214 "lastupd" => {
215 col_map.insert(id, TrackAttrs::LastUpd);
216 }
217 "lastplay" => {
218 col_map.insert(id, TrackAttrs::LastPlay);
219 }
220 "rating" => {
221 col_map.insert(id, TrackAttrs::Rating);
222 }
223 "tuid2" => {
224 col_map.insert(id, TrackAttrs::Tuid2);
225 }
226 "playcount" => {
227 col_map.insert(id, TrackAttrs::PlayCount);
228 }
229 "filetime" => {
230 col_map.insert(id, TrackAttrs::Filetime);
231 }
232 "filesize" => {
233 col_map.insert(id, TrackAttrs::Filesize);
234 }
235 "bitrate" => {
236 col_map.insert(id, TrackAttrs::Bitrate);
237 }
238 "disc" => {
239 col_map.insert(id, TrackAttrs::Disc);
240 }
241 "albumartist" => {
242 col_map.insert(id, TrackAttrs::Albumartist);
243 }
244 "replaygain_album_gain" => {
245 col_map.insert(id, TrackAttrs::ReplaygainAlbumGain);
246 }
247 "replaygain_track_gain" => {
248 col_map.insert(id, TrackAttrs::ReplaygainTrackGain);
249 }
250 "publisher" => {
251 col_map.insert(id, TrackAttrs::Publisher);
252 }
253 "composer" => {
254 col_map.insert(id, TrackAttrs::Composer);
255 }
256 "bpm" => {
257 col_map.insert(id, TrackAttrs::Bpm);
258 }
259 "discs" => {
260 col_map.insert(id, TrackAttrs::Discs);
261 }
262 "tracks" => {
263 col_map.insert(id, TrackAttrs::Tracks);
264 }
265 "ispodcast" => {
266 col_map.insert(id, TrackAttrs::IsPodcast);
267 }
268 "podcastchannel" => {
269 col_map.insert(id, TrackAttrs::PodcastChannel);
270 }
271 "podcastpubdate" => {
272 col_map.insert(id, TrackAttrs::PodcastPubdate);
273 }
274 "GracenoteFileID" => {
275 col_map.insert(id, TrackAttrs::GracenoteFileId);
276 }
277 "GracenoteExtData" => {
278 col_map.insert(id, TrackAttrs::GracenoteExtData);
279 }
280 "lossless" => {
281 col_map.insert(id, TrackAttrs::Lossless);
282 }
283 "category" => {
284 col_map.insert(id, TrackAttrs::Category);
285 }
286 "codec" => {
287 col_map.insert(id, TrackAttrs::Codec);
288 }
289 "director" => {
290 col_map.insert(id, TrackAttrs::Director);
291 }
292 "producer" => {
293 col_map.insert(id, TrackAttrs::Producer);
294 }
295 "width" => {
296 col_map.insert(id, TrackAttrs::Width);
297 }
298 "height" => {
299 col_map.insert(id, TrackAttrs::Height);
300 }
301 "mimetype" => {
302 col_map.insert(id, TrackAttrs::MimeType);
303 }
304 "dateadded" => {
305 col_map.insert(id, TrackAttrs::DateAdded);
306 }
307 _ => (),
308 }
309 }
310 col_map
311}
312
313#[derive(Debug, Serialize)]
315pub struct Track {
316 filename: std::path::PathBuf,
317 artist: Option<String>,
318 title: Option<String>,
319 album: Option<String>,
320 year: Option<i32>,
321 genre: Option<String>,
322 comment: Option<String>,
323 trackno: Option<i32>,
324 length: Option<i32>,
325 ttype: Option<i32>,
326 lastupd: Option<i32>,
327 lastplay: Option<i32>,
328 rating: Option<i32>,
329 tuid2: Option<String>,
330 play_count: Option<i32>,
331 filetime: Option<i32>,
332 filesize: Option<i64>,
333 bitrate: Option<i32>,
334 disc: Option<i32>,
335 albumartist: Option<String>,
336 replaygain_album_gain: Option<String>,
337 replaygain_track_gain: Option<String>,
338 publisher: Option<String>,
339 composer: Option<String>,
340 bpm: Option<i32>,
341 discs: Option<i32>,
342 tracks: Option<i32>,
343 is_podcast: Option<i32>,
344 podcast_channel: Option<String>,
345 podcast_pubdate: Option<i32>,
346 gracenote_file_id: Option<String>,
347 gracenote_ext_data: Option<String>,
348 lossless: Option<i32>,
349 category: Option<String>,
350 codec: Option<String>,
351 director: Option<String>,
352 producer: Option<String>,
353 width: Option<i32>,
354 height: Option<i32>,
355 mimetype: Option<String>,
356 date_added: Option<i32>,
357}
358
359impl Track {
360 pub fn new<'a, FI>(col_map: &ColumnMap, fields: FI) -> Result<Track>
361 where
362 FI: Iterator<Item = &'a Box<dyn NdeField>>,
363 {
364 let mut attrs_map: HashMap<TrackAttrs, crate::fields::FieldValue> = HashMap::new();
366
367 for field in fields {
368 match col_map.get(&field.id()) {
369 Some(attr) => match (attr, field.value()) {
370 (TrackAttrs::Filename, FieldValue::Filename(x)) => {
371 attrs_map.insert(TrackAttrs::Filename, FieldValue::Filename(x));
372 }
373 (TrackAttrs::Artist, FieldValue::String(x)) => {
374 attrs_map.insert(TrackAttrs::Artist, FieldValue::String(x));
375 }
376 (TrackAttrs::Title, FieldValue::String(x)) => {
377 attrs_map.insert(TrackAttrs::Title, FieldValue::String(x));
378 }
379 (TrackAttrs::Album, FieldValue::String(x)) => {
380 attrs_map.insert(TrackAttrs::Album, FieldValue::String(x));
381 }
382 (TrackAttrs::Year, FieldValue::Integer(x)) => {
383 attrs_map.insert(TrackAttrs::Year, FieldValue::Integer(x));
384 }
385 (TrackAttrs::Genre, FieldValue::String(x)) => {
386 attrs_map.insert(TrackAttrs::Genre, FieldValue::String(x));
387 }
388 (TrackAttrs::Comment, FieldValue::String(x)) => {
389 attrs_map.insert(TrackAttrs::Comment, FieldValue::String(x));
390 }
391 (TrackAttrs::TrackNo, FieldValue::Integer(x)) => {
392 attrs_map.insert(TrackAttrs::TrackNo, FieldValue::Integer(x));
393 }
394 (TrackAttrs::Length, FieldValue::Length(x)) => {
395 attrs_map.insert(TrackAttrs::Length, FieldValue::Integer(x));
396 }
397 (TrackAttrs::Type, FieldValue::Integer(x)) => {
398 attrs_map.insert(TrackAttrs::Type, FieldValue::Integer(x));
399 }
400 (TrackAttrs::LastUpd, FieldValue::Datetime(x)) => {
401 attrs_map.insert(TrackAttrs::LastUpd, FieldValue::Datetime(x));
402 }
403 (TrackAttrs::LastPlay, FieldValue::Datetime(x)) => {
404 attrs_map.insert(TrackAttrs::LastPlay, FieldValue::Datetime(x));
405 }
406 (TrackAttrs::Rating, FieldValue::Integer(x)) => {
407 attrs_map.insert(TrackAttrs::Rating, FieldValue::Integer(x));
408 }
409 (TrackAttrs::Tuid2, FieldValue::String(x)) => {
410 attrs_map.insert(TrackAttrs::Tuid2, FieldValue::String(x));
411 }
412 (TrackAttrs::PlayCount, FieldValue::Integer(x)) => {
413 attrs_map.insert(TrackAttrs::PlayCount, FieldValue::Integer(x));
414 }
415 (TrackAttrs::Filetime, FieldValue::Datetime(x)) => {
416 attrs_map.insert(TrackAttrs::Filetime, FieldValue::Integer(x));
417 }
418 (TrackAttrs::Filesize, FieldValue::Int64(x)) => {
419 attrs_map.insert(TrackAttrs::Filesize, FieldValue::Int64(x));
420 }
421 (TrackAttrs::Bitrate, FieldValue::Integer(x)) => {
422 attrs_map.insert(TrackAttrs::Bitrate, FieldValue::Integer(x));
423 }
424 (TrackAttrs::Disc, FieldValue::Integer(x)) => {
425 attrs_map.insert(TrackAttrs::Disc, FieldValue::Integer(x));
426 }
427 (TrackAttrs::Albumartist, FieldValue::String(x)) => {
428 attrs_map.insert(TrackAttrs::Albumartist, FieldValue::String(x));
429 }
430 (TrackAttrs::ReplaygainAlbumGain, FieldValue::String(x)) => {
431 attrs_map.insert(TrackAttrs::ReplaygainAlbumGain, FieldValue::String(x));
432 }
433 (TrackAttrs::ReplaygainTrackGain, FieldValue::String(x)) => {
434 attrs_map.insert(TrackAttrs::ReplaygainTrackGain, FieldValue::String(x));
435 }
436 (TrackAttrs::Publisher, FieldValue::String(x)) => {
437 attrs_map.insert(TrackAttrs::Publisher, FieldValue::String(x));
438 }
439 (TrackAttrs::Composer, FieldValue::String(x)) => {
440 attrs_map.insert(TrackAttrs::Composer, FieldValue::String(x));
441 }
442 (TrackAttrs::Bpm, FieldValue::Integer(x)) => {
443 attrs_map.insert(TrackAttrs::Bpm, FieldValue::Integer(x));
444 }
445 (TrackAttrs::Discs, FieldValue::Integer(x)) => {
446 attrs_map.insert(TrackAttrs::Discs, FieldValue::Integer(x));
447 }
448 (TrackAttrs::Tracks, FieldValue::Integer(x)) => {
449 attrs_map.insert(TrackAttrs::Tracks, FieldValue::Integer(x));
450 }
451 (TrackAttrs::IsPodcast, FieldValue::Integer(x)) => {
452 attrs_map.insert(TrackAttrs::IsPodcast, FieldValue::Integer(x));
453 }
454 (TrackAttrs::PodcastChannel, FieldValue::String(x)) => {
455 attrs_map.insert(TrackAttrs::PodcastChannel, FieldValue::String(x));
456 }
457 (TrackAttrs::PodcastPubdate, FieldValue::Integer(x)) => {
458 attrs_map.insert(TrackAttrs::PodcastPubdate, FieldValue::Integer(x));
459 }
460 (TrackAttrs::GracenoteFileId, FieldValue::String(x)) => {
461 attrs_map.insert(TrackAttrs::GracenoteFileId, FieldValue::String(x));
462 }
463 (TrackAttrs::GracenoteExtData, FieldValue::String(x)) => {
464 attrs_map.insert(TrackAttrs::GracenoteExtData, FieldValue::String(x));
465 }
466 (TrackAttrs::Lossless, FieldValue::Integer(x)) => {
467 attrs_map.insert(TrackAttrs::Lossless, FieldValue::Integer(x));
468 }
469 (TrackAttrs::Category, FieldValue::String(x)) => {
470 attrs_map.insert(TrackAttrs::Category, FieldValue::String(x));
471 }
472 (TrackAttrs::Codec, FieldValue::String(x)) => {
473 attrs_map.insert(TrackAttrs::Codec, FieldValue::String(x));
474 }
475 (TrackAttrs::Director, FieldValue::String(x)) => {
476 attrs_map.insert(TrackAttrs::Director, FieldValue::String(x));
477 }
478 (TrackAttrs::Producer, FieldValue::String(x)) => {
479 attrs_map.insert(TrackAttrs::Producer, FieldValue::String(x));
480 }
481 (TrackAttrs::Width, FieldValue::Integer(x)) => {
482 attrs_map.insert(TrackAttrs::Width, FieldValue::Integer(x));
483 }
484 (TrackAttrs::Height, FieldValue::Integer(x)) => {
485 attrs_map.insert(TrackAttrs::Height, FieldValue::Integer(x));
486 }
487 (TrackAttrs::MimeType, FieldValue::String(x)) => {
488 attrs_map.insert(TrackAttrs::MimeType, FieldValue::String(x));
489 }
490 (TrackAttrs::DateAdded, FieldValue::Datetime(x)) => {
491 attrs_map.insert(TrackAttrs::DateAdded, FieldValue::Datetime(x));
492 }
493 _ => {
494 error!("failed to match: ({:#?}, {:#?})!", attr, field.value());
495 }
496 },
497 None => {
498 error!("failed to match: {}", field.id());
499 }
500 }
501 }
502
503 let filename = match attrs_map.get(&TrackAttrs::Filename) {
506 Some(FieldValue::Filename(x)) => x.clone(),
507 _ => {
508 return Err(Error::new(Cause::NoFilename));
509 }
510 };
511 let artist = match attrs_map.get(&TrackAttrs::Artist) {
514 Some(FieldValue::String(x)) => Some(x.clone()),
515 _ => None,
516 };
517 let title = match attrs_map.get(&TrackAttrs::Title) {
518 Some(FieldValue::String(x)) => Some(x.clone()),
519 _ => None,
520 };
521 let album = match attrs_map.get(&TrackAttrs::Album) {
522 Some(FieldValue::String(x)) => Some(x.clone()),
523 _ => None,
524 };
525 let year = match attrs_map.get(&TrackAttrs::Year) {
526 Some(FieldValue::Integer(x)) => Some(*x),
527 _ => None,
528 };
529 let genre = match attrs_map.get(&TrackAttrs::Genre) {
530 Some(FieldValue::String(x)) => Some(x.clone()),
531 _ => None,
532 };
533 let comment = match attrs_map.get(&TrackAttrs::Comment) {
534 Some(FieldValue::String(x)) => Some(x.clone()),
535 _ => None,
536 };
537 let trackno = match attrs_map.get(&TrackAttrs::TrackNo) {
538 Some(FieldValue::Integer(x)) => Some(*x),
539 _ => None,
540 };
541 let length = match attrs_map.get(&TrackAttrs::Length) {
542 Some(FieldValue::Integer(x)) => Some(*x),
543 _ => None,
544 };
545 let ttype = match attrs_map.get(&TrackAttrs::Type) {
546 Some(FieldValue::Integer(x)) => Some(*x),
547 _ => None,
548 };
549 let lastupd = match attrs_map.get(&TrackAttrs::LastUpd) {
550 Some(FieldValue::Datetime(x)) => Some(*x),
551 _ => None,
552 };
553 let lastplay = match attrs_map.get(&TrackAttrs::LastPlay) {
554 Some(FieldValue::Datetime(x)) => Some(*x),
555 _ => None,
556 };
557 let rating = match attrs_map.get(&TrackAttrs::Rating) {
558 Some(FieldValue::Integer(x)) => Some(*x),
559 _ => None,
560 };
561 let tuid2 = match attrs_map.get(&TrackAttrs::Tuid2) {
562 Some(FieldValue::String(x)) => Some(x.clone()),
563 _ => None,
564 };
565 let play_count = match attrs_map.get(&TrackAttrs::PlayCount) {
566 Some(FieldValue::Integer(x)) => Some(*x),
567 _ => None,
568 };
569
570 let filetime = match attrs_map.get(&TrackAttrs::Filetime) {
571 Some(FieldValue::Integer(x)) => Some(*x),
572 _ => None,
573 };
574 let filesize = match attrs_map.get(&TrackAttrs::Filesize) {
575 Some(FieldValue::Int64(x)) => Some(*x),
576 _ => None,
577 };
578 let bitrate = match attrs_map.get(&TrackAttrs::Bitrate) {
579 Some(FieldValue::Integer(x)) => Some(*x),
580 _ => None,
581 };
582 let disc = match attrs_map.get(&TrackAttrs::Disc) {
583 Some(FieldValue::Integer(x)) => Some(*x),
584 _ => None,
585 };
586 let albumartist = match attrs_map.get(&TrackAttrs::Albumartist) {
587 Some(FieldValue::String(x)) => Some(x.clone()),
588 _ => None,
589 };
590 let replaygain_album_gain = match attrs_map.get(&TrackAttrs::ReplaygainAlbumGain) {
591 Some(FieldValue::String(x)) => Some(x.clone()),
592 _ => None,
593 };
594 let replaygain_track_gain = match attrs_map.get(&TrackAttrs::ReplaygainTrackGain) {
595 Some(FieldValue::String(x)) => Some(x.clone()),
596 _ => None,
597 };
598 let publisher = match attrs_map.get(&TrackAttrs::Publisher) {
599 Some(FieldValue::String(x)) => Some(x.clone()),
600 _ => None,
601 };
602 let composer = match attrs_map.get(&TrackAttrs::Composer) {
603 Some(FieldValue::String(x)) => Some(x.clone()),
604 _ => None,
605 };
606 let bpm = match attrs_map.get(&TrackAttrs::Bpm) {
607 Some(FieldValue::Integer(x)) => Some(*x),
608 _ => None,
609 };
610 let discs = match attrs_map.get(&TrackAttrs::Discs) {
611 Some(FieldValue::Integer(x)) => Some(*x),
612 _ => None,
613 };
614 let tracks = match attrs_map.get(&TrackAttrs::Tracks) {
615 Some(FieldValue::Integer(x)) => Some(*x),
616 _ => None,
617 };
618 let ispodcast = match attrs_map.get(&TrackAttrs::IsPodcast) {
619 Some(FieldValue::Integer(x)) => Some(*x),
620 _ => None,
621 };
622 let podcastchannel = match attrs_map.get(&TrackAttrs::PodcastChannel) {
623 Some(FieldValue::String(x)) => Some(x.clone()),
624 _ => None,
625 };
626 let podcastpubdate = match attrs_map.get(&TrackAttrs::PodcastPubdate) {
627 Some(FieldValue::Datetime(x)) => Some(*x),
628 _ => None,
629 };
630 let gracenote_file_id = match attrs_map.get(&TrackAttrs::GracenoteFileId) {
631 Some(FieldValue::String(x)) => Some(x.clone()),
632 _ => None,
633 };
634 let gracenote_ext_data = match attrs_map.get(&TrackAttrs::GracenoteExtData) {
635 Some(FieldValue::String(x)) => Some(x.clone()),
636 _ => None,
637 };
638 let lossless = match attrs_map.get(&TrackAttrs::Lossless) {
639 Some(FieldValue::Integer(x)) => Some(*x),
640 _ => None,
641 };
642 let category = match attrs_map.get(&TrackAttrs::Category) {
643 Some(FieldValue::String(x)) => Some(x.clone()),
644 _ => None,
645 };
646 let codec = match attrs_map.get(&TrackAttrs::Codec) {
647 Some(FieldValue::String(x)) => Some(x.clone()),
648 _ => None,
649 };
650 let director = match attrs_map.get(&TrackAttrs::Director) {
651 Some(FieldValue::String(x)) => Some(x.clone()),
652 _ => None,
653 };
654 let producer = match attrs_map.get(&TrackAttrs::Producer) {
655 Some(FieldValue::String(x)) => Some(x.clone()),
656 _ => None,
657 };
658 let width = match attrs_map.get(&TrackAttrs::Width) {
659 Some(FieldValue::Integer(x)) => Some(*x),
660 _ => None,
661 };
662 let height = match attrs_map.get(&TrackAttrs::Height) {
663 Some(FieldValue::Integer(x)) => Some(*x),
664 _ => None,
665 };
666 let mimetype = match attrs_map.get(&TrackAttrs::MimeType) {
667 Some(FieldValue::String(x)) => Some(x.clone()),
668 _ => None,
669 };
670 let dateadded = match attrs_map.get(&TrackAttrs::DateAdded) {
671 Some(FieldValue::Datetime(x)) => Some(*x),
672 _ => None,
673 };
674
675 Ok(Track {
676 filename: filename,
677 artist: artist,
678 title: title,
679 album: album,
680 year: year,
681 genre: genre,
682 comment: comment,
683 trackno: trackno,
684 length: length,
685 ttype: ttype,
686 lastupd: lastupd,
687 lastplay: lastplay,
688 rating: rating,
689 tuid2: tuid2,
690 play_count: play_count,
691 filetime: filetime,
692 filesize: filesize,
693 bitrate: bitrate,
694 disc: disc,
695 albumartist: albumartist,
696 replaygain_album_gain: replaygain_album_gain,
697 replaygain_track_gain: replaygain_track_gain,
698 publisher: publisher,
699 composer: composer,
700 bpm: bpm,
701 discs: discs,
702 tracks: tracks,
703 is_podcast: ispodcast,
704 podcast_channel: podcastchannel,
705 podcast_pubdate: podcastpubdate,
706 gracenote_file_id: gracenote_file_id,
707 gracenote_ext_data: gracenote_ext_data,
708 lossless: lossless,
709 category: category,
710 codec: codec,
711 director: director,
712 producer: producer,
713 width: width,
714 height: height,
715 mimetype: mimetype,
716 date_added: dateadded,
717 })
718 }
719}