1use std::{
2 fs::{File, read},
3 io::Write,
4 path::PathBuf,
5 sync::{Arc, LazyLock, RwLock},
6 thread,
7 time::Duration,
8};
9
10use log::{error, trace, warn};
11use rmp_serde::{Deserializer, Serializer};
12use serde::{Deserialize, Serialize};
13
14use rand::seq::SliceRandom;
15
16use crate::{
17 Error,
18 music_lib::{Album, Artist, CACHE_DIR, Genre, Playlist, Track, find_tracks_by_hashes},
19 playback::{LoopState, PlaybackState, PlayerVolume, ShuffleState},
20};
21
22#[cfg(feature = "mpris")]
23use crate::playback::mpris;
24
25#[derive(Debug, Clone, PartialEq)]
26pub enum Event {
27 StateInitialized(PlayerState),
28 PositionChanged(usize),
29 PlayerPositionChanged(Duration),
30 PlayerVolumeChanged(PlayerVolume),
31 PlaybackStateChanged(PlaybackState),
32 TracksChanged {
34 position: usize,
35 tracks: Vec<Arc<Track>>,
36 shuffled_indices: Vec<usize>,
37 },
38 ShuffledTrackIndicesChanged(Vec<usize>),
39 QueueSourceChanged(QueueSource),
40 ShuffleStateChanged(ShuffleState),
41 LoopStateChanged(LoopState),
42}
43
44impl Event {
45 fn send(self) {
46 crate::send_event(crate::Event::Playback(self));
47 }
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub enum Queue {
52 Playlist(Arc<Playlist>),
53 Album(Arc<Album>),
54 Artist(Arc<Artist>),
55 Genre(Arc<Genre>),
56 AllTracks(Vec<Arc<Track>>),
57 Custom {
58 label: String,
59 tracks: Vec<Arc<Track>>,
60 },
61}
62
63impl From<Arc<Playlist>> for Queue {
64 fn from(value: Arc<Playlist>) -> Self {
65 Self::Playlist(value)
66 }
67}
68
69impl From<Arc<Album>> for Queue {
70 fn from(value: Arc<Album>) -> Self {
71 Self::Album(value)
72 }
73}
74
75impl From<Arc<Artist>> for Queue {
76 fn from(value: Arc<Artist>) -> Self {
77 Self::Artist(value)
78 }
79}
80
81impl From<Arc<Genre>> for Queue {
82 fn from(value: Arc<Genre>) -> Self {
83 Self::Genre(value)
84 }
85}
86
87impl Queue {
88 pub fn source(&self) -> QueueSource {
89 match self {
90 Self::Playlist(playlist) => QueueSource::Playlist {
91 name: playlist.name.clone().into_string(),
92 },
93 Self::Album(album) => QueueSource::Album {
94 title: album.title.clone().into_string(),
95 },
96 Self::Artist(artist) => QueueSource::Artist {
97 name: artist.name.clone().into_string(),
98 },
99 Self::Genre(genre) => QueueSource::Genre {
100 name: genre.name.clone().into_string(),
101 },
102 Self::AllTracks(_) => QueueSource::AllTracks,
103 Self::Custom { label, tracks: _ } => QueueSource::Custom {
104 label: label.clone(),
105 },
106 }
107 }
108
109 pub fn tracks(self) -> Vec<Arc<Track>> {
110 match self {
111 Self::Playlist(playlist) => playlist.tracks.to_vec(),
112 Self::Album(album) => album.tracks.to_vec(),
113 Self::Artist(artist) => artist.tracks.to_vec(),
114 Self::Genre(genre) => genre.tracks.to_vec(),
115 Self::AllTracks(tracks) => tracks,
116 Self::Custom { label: _, tracks } => tracks,
117 }
118 }
119}
120
121#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
122pub enum QueueSource {
123 #[default]
124 None,
125 AllTracks,
126 Custom {
127 label: String,
128 },
129 Playlist {
130 name: String,
131 },
132 Album {
133 title: String,
134 },
135 Artist {
136 name: String,
137 },
138 Genre {
139 name: String,
140 },
141}
142
143impl QueueSource {
144 pub fn identity(&self) -> Option<&str> {
145 match self {
146 Self::None => None,
147 Self::AllTracks => Some("All tracks"),
148 Self::Custom { label } => Some(label),
149 Self::Playlist { name } => Some(name),
150 Self::Album { title } => Some(title),
151 Self::Artist { name } => Some(name),
152 Self::Genre { name } => Some(name),
153 }
154 }
155}
156
157#[derive(Debug, Clone, Default, PartialEq)]
159pub struct PlayerState {
160 pub position: usize,
165 pub player_position: Duration,
166 pub player_volume: PlayerVolume,
167 pub playback_state: PlaybackState,
168 pub tracks: Vec<Arc<Track>>,
169 pub shuffled_track_indices: Vec<usize>,
170 pub queue_source: QueueSource,
171 pub shuffle_state: ShuffleState,
172 pub loop_state: LoopState,
173}
174
175impl TryFrom<PlayerStateRaw> for PlayerState {
176 type Error = Error;
177 fn try_from(value: PlayerStateRaw) -> Result<Self, Self::Error> {
178 let (tracks, shuffled_track_indices) = {
179 let result = find_tracks_by_hashes(value.track_hashes)?;
180 if !result.unmatched.is_empty() {
181 warn!("{} missing tracks in the queue", result.unmatched.len());
182 let indices = result.matched.iter().enumerate().map(|(i, _)| i).collect();
183 (result.matched, indices)
184 } else {
185 let mut indices_deduped = value.shuffled_track_indices;
186 indices_deduped.dedup();
187 if result.matched.len() == indices_deduped.len()
188 && indices_deduped.iter().max().unwrap_or(&0) < &result.matched.len()
189 {
190 (result.matched, indices_deduped)
191 } else {
192 let indices = result.matched.iter().enumerate().map(|(i, _)| i).collect();
193 (result.matched, indices)
194 }
195 }
196 };
197 let playback_state = if tracks.is_empty() {
198 PlaybackState::Stopped
199 } else {
200 PlaybackState::Paused
201 };
202 let position = value.position.min(tracks.len().saturating_sub(1));
203 Ok(Self {
204 position,
205 player_position: value.player_position,
206 player_volume: value.player_volume,
207 playback_state,
208 tracks,
209 shuffled_track_indices,
210 queue_source: value.queue_source,
211 shuffle_state: value.shuffle_state,
212 loop_state: value.loop_state,
213 })
214 }
215}
216
217impl PlayerState {
218 pub fn queue_empty(&self) -> bool {
219 self.tracks.is_empty()
220 }
221
222 pub fn current(&self) -> Option<&Arc<Track>> {
223 match self.shuffle_state {
224 ShuffleState::Off => {
225 return self.tracks.get(self.position);
226 }
227 ShuffleState::On => {
228 if let Some(index) = self.shuffled_track_indices.get(self.position) {
229 return self.tracks.get(*index);
230 }
231 }
232 }
233 None
234 }
235
236 pub fn real_track_index(&self, index: usize) -> Option<usize> {
244 match self.shuffle_state {
245 ShuffleState::Off => {
246 if index >= self.tracks.len() {
247 None
248 } else {
249 Some(index)
250 }
251 }
252 ShuffleState::On => self.shuffled_track_indices.get(index).copied(),
253 }
254 }
255
256 pub fn can_seek(&self) -> bool {
257 self.playback_state != PlaybackState::Stopped
258 }
259
260 pub fn can_play(&self) -> bool {
261 self.position < self.tracks.len() && !self.is_playing()
262 }
263
264 pub fn can_pause(&self) -> bool {
265 self.playback_state == PlaybackState::Playing
266 }
267
268 pub fn can_toggle_playing(&self) -> bool {
269 match self.playback_state {
270 PlaybackState::Playing => self.can_pause(),
271 PlaybackState::Paused | PlaybackState::Stopped => self.can_play(),
272 }
273 }
274
275 pub fn can_go_next(&self) -> bool {
276 match self.loop_state {
277 LoopState::Off => !self.tracks.is_empty() && self.position < self.tracks.len() - 1,
278 _ => !self.tracks.is_empty(),
279 }
280 }
281
282 pub fn can_go_previous(&self) -> bool {
283 match self.loop_state {
284 LoopState::Off => !self.tracks.is_empty() && self.position > 0,
285 _ => !self.tracks.is_empty(),
286 }
287 }
288
289 pub fn is_playing(&self) -> bool {
290 self.playback_state == PlaybackState::Playing
291 }
292
293 pub fn is_paused(&self) -> bool {
294 self.playback_state == PlaybackState::Paused
295 }
296
297 pub fn stopped(&self) -> bool {
298 self.playback_state == PlaybackState::Stopped
299 }
300
301 pub fn shuffle_enabled(&self) -> bool {
302 self.shuffle_state.enabled()
303 }
304
305 pub fn handle_event(&mut self, event: Event) {
306 match event {
307 Event::StateInitialized(player_state) => *self = player_state,
308 Event::PositionChanged(pos) => self.position = pos,
309 Event::PlayerPositionChanged(player_position) => self.player_position = player_position,
310 Event::PlayerVolumeChanged(player_volume) => self.player_volume = player_volume,
311 Event::PlaybackStateChanged(playback_state) => self.playback_state = playback_state,
312 Event::ShuffleStateChanged(shuffle_state) => self.shuffle_state = shuffle_state,
313 Event::LoopStateChanged(loop_state) => self.loop_state = loop_state,
314 Event::TracksChanged {
315 position,
316 tracks,
317 shuffled_indices,
318 } => {
319 self.position = position;
320 self.tracks = tracks;
321 self.shuffled_track_indices = shuffled_indices;
322 }
323 Event::QueueSourceChanged(queue_source) => self.queue_source = queue_source,
324 Event::ShuffledTrackIndicesChanged(indices) => self.shuffled_track_indices = indices,
325 }
326 }
327
328 pub(crate) fn on_playback_state_changed(&self) {
329 trace!("Playback state changed: {}", self.playback_state);
330
331 Event::PlaybackStateChanged(self.playback_state).send();
332
333 #[cfg(feature = "mpris")]
334 {
335 use mpris_server::Property;
336 mpris::update_properties(vec![
337 Property::PlaybackStatus(mpris::playback_state_2_mpris(&self.playback_state)),
338 Property::CanPlay(self.can_play()),
339 Property::CanPause(self.can_pause()),
340 Property::CanSeek(self.can_seek()),
341 ]);
342 }
343 }
344
345 pub(crate) fn set_queue(&mut self, queue: Queue) {
346 self.position = 0;
347 self.queue_source = queue.source();
348 self.tracks = queue.tracks();
349 self.set_playback_state(PlaybackState::Stopped);
350 if self.shuffle_state.enabled() {
351 self.shuffle();
352 }
353 Event::QueueSourceChanged(self.queue_source.clone()).send();
354 Event::TracksChanged {
355 position: self.position,
356 tracks: self.tracks.clone(),
357 shuffled_indices: self.shuffled_track_indices.clone(),
358 }
359 .send();
360 self.on_track_changed();
361 }
362
363 pub(crate) fn play_new_queue(&mut self, queue: impl Into<Queue>, index: Option<usize>) {
364 trace!("Setting a new queue");
365 let queue = queue.into();
366 let shuffle_enabled = self.shuffle_state.enabled();
367 if shuffle_enabled {
368 self.shuffle_state = ShuffleState::Off;
370 }
371 self.position = index.unwrap_or_default();
372 self.queue_source = queue.source();
373 Event::QueueSourceChanged(self.queue_source.clone()).send();
374 self.tracks = queue.tracks();
375 if shuffle_enabled {
376 self.shuffle_state = ShuffleState::On;
377 if index.is_some() {
378 self.shuffle();
379 } else {
380 self.full_shuffle();
381 }
382 }
383 Event::TracksChanged {
384 position: self.position,
385 tracks: self.tracks.clone(),
386 shuffled_indices: self.shuffled_track_indices.clone(),
387 }
388 .send();
389 self.on_track_changed();
390 self.set_player_position(Duration::default());
391 }
392
393 pub(crate) fn append_tracks(&mut self, tracks: &mut Vec<Arc<Track>>) {
394 let start = self.shuffled_track_indices.len();
395 let count = tracks.len();
396 self.tracks.append(tracks);
397 self.shuffled_track_indices.extend(start..start + count);
398
399 Event::TracksChanged {
400 position: self.position,
401 tracks: self.tracks.clone(),
402 shuffled_indices: self.shuffled_track_indices.clone(),
403 }
404 .send();
405 #[cfg(feature = "mpris")]
406 {
407 use mpris_server::{Metadata, Property};
408
409 mpris::update_properties(vec![
410 match self.current() {
411 Some(track) => Property::Metadata(mpris::get_meta(track, self.position)),
412 None => Property::Metadata(Metadata::new()),
413 },
414 Property::CanGoPrevious(self.can_go_previous()),
415 Property::CanGoNext(self.can_go_next()),
416 Property::CanPlay(self.can_play()),
417 Property::CanPause(self.can_pause()),
418 ]);
419 }
420 }
421
422 pub(crate) fn remove_tracks(&mut self, mut indices: Vec<usize>) -> Result<(), Error> {
423 indices.dedup();
424 let position_shift = indices.iter().filter(|i| **i < self.position).count();
425 let mut to_remove = vec![false; self.tracks.len()];
426 for i in &indices {
427 if let Some(val) = to_remove.get_mut(*i) {
428 *val = true;
429 } else {
430 error!("Cannot remove tracks from the queue: Track index {i} is out of bounds");
431 return Err(Error::NoTrackAtIndex(*i));
432 }
433 }
434 let prev_track = self.current().cloned();
435 if self.shuffle_enabled() {
436 let mut remove_real = vec![false; self.tracks.len()];
437 for &queue_index in &indices {
438 let real_index = self.shuffled_track_indices[queue_index];
439 remove_real[real_index] = true;
440 }
441
442 let mut remap = vec![0; self.tracks.len()];
443 let mut new_index = 0;
444 for old_index in 0..self.tracks.len() {
445 if !remove_real[old_index] {
446 remap[old_index] = new_index;
447 new_index += 1;
448 }
449 }
450
451 self.shuffled_track_indices = std::mem::take(&mut self.shuffled_track_indices)
452 .into_iter()
453 .enumerate()
454 .filter_map(|(queue_index, old_real_index)| {
455 (!to_remove[queue_index]).then_some(remap[old_real_index])
456 })
457 .collect();
458
459 self.tracks = std::mem::take(&mut self.tracks)
460 .into_iter()
461 .enumerate()
462 .filter_map(|(real_index, track)| (!remove_real[real_index]).then_some(track))
463 .collect();
464 } else {
465 self.tracks = std::mem::take(&mut self.tracks)
466 .into_iter()
467 .enumerate()
468 .filter_map(|(i, track)| (!to_remove[i]).then_some(track))
469 .collect();
470 }
471 self.position = self.position.saturating_sub(position_shift);
472 if prev_track != self.current().cloned() {
473 self.set_player_position(Duration::default());
474 self.on_track_changed();
475 }
476
477 Event::TracksChanged {
478 position: self.position,
479 tracks: self.tracks.clone(),
480 shuffled_indices: self.shuffled_track_indices.clone(),
481 }
482 .send();
483 Ok(())
484 }
485
486 fn full_shuffle(&mut self) {
488 if self.tracks.is_empty() {
489 use log::warn;
490 warn!("Refusing to shuffle an empty queue");
491 return;
492 }
493
494 self.shuffled_track_indices = self.tracks.iter().enumerate().map(|(i, _)| i).collect();
495 let mut rng = rand::rng();
496 self.shuffled_track_indices.shuffle(&mut rng);
497
498 Event::ShuffledTrackIndicesChanged(self.shuffled_track_indices.clone()).send();
499 }
500
501 pub(crate) fn shuffle(&mut self) {
506 if self.tracks.is_empty() {
507 use log::warn;
508
509 warn!("Refusing to shuffle an empty queue");
510 return;
511 }
512
513 self.shuffled_track_indices = self.tracks.iter().enumerate().map(|(i, _)| i).collect();
514 let len = self.shuffled_track_indices.len();
515 let pos = self.position;
516
517 self.shuffled_track_indices.swap(pos, 0);
518 let mut rng = rand::rng();
519 self.shuffled_track_indices[1..len].shuffle(&mut rng);
520 self.position = 0;
521
522 Event::PositionChanged(self.position).send();
523 Event::ShuffledTrackIndicesChanged(self.shuffled_track_indices.clone()).send();
524 }
525
526 pub(crate) fn set_shuffle_state(&mut self, shuffle_state: ShuffleState) {
527 if !shuffle_state.enabled()
528 && let Some(track) = self.current()
529 {
530 match self.tracks.iter().position(|t| t == track) {
531 Some(pos) => {
532 self.position = pos;
533 Event::PositionChanged(self.position).send();
534 }
535 None => {
536 log::warn!(
537 "Could not find the previous track in the queue, this should never happen"
538 );
539 thread::spawn(crate::playback::stop);
540 }
541 }
542 } else {
543 self.shuffle();
544 }
545 self.shuffle_state = shuffle_state;
546 Event::ShuffleStateChanged(self.shuffle_state).send();
547 #[cfg(feature = "mpris")]
548 {
549 use mpris_server::Property;
550
551 let properties = vec![
552 Property::Shuffle(self.shuffle_state.enabled()),
553 Property::CanGoPrevious(self.can_go_previous()),
554 Property::CanGoNext(self.can_go_next()),
555 ];
556 mpris::update_properties(properties);
557 }
558 }
559
560 pub(crate) fn increment_player_position(&mut self, duration: Duration) {
561 self.player_position += duration;
562 Event::PlayerPositionChanged(self.player_position).send();
563 #[cfg(feature = "mpris")]
564 {
565 use mpris_server::{Metadata, Property};
566
567 let meta = match self.current() {
568 Some(track) => mpris::get_meta(track, self.position),
569 None => Metadata::new(),
570 };
571 mpris::update_properties(vec![Property::Metadata(meta)]);
572 mpris::set_position(self.player_position);
573 }
574 }
575
576 pub(crate) fn set_player_position(&mut self, player_position: Duration) {
577 self.player_position = player_position;
578 Event::PlayerPositionChanged(self.player_position).send();
579 #[cfg(feature = "mpris")]
580 {
581 use mpris_server::{Metadata, Property};
582
583 let meta = match self.current() {
584 Some(track) => mpris::get_meta(track, self.position),
585 None => Metadata::new(),
586 };
587 mpris::update_properties(vec![Property::Metadata(meta)]);
588 mpris::set_position(player_position);
589 }
590 }
591
592 pub(crate) fn set_player_volume(&mut self, player_volume: PlayerVolume) {
593 self.player_volume = player_volume;
594 Event::PlayerVolumeChanged(self.player_volume).send();
595 #[cfg(feature = "mpris")]
596 {
597 use mpris_server::Property;
598
599 let properties = vec![Property::Volume(self.player_volume.get())];
600 mpris::update_properties(properties);
601 }
602 }
603
604 pub(crate) fn set_loop_state(&mut self, loop_state: LoopState) {
605 self.loop_state = loop_state;
606 Event::LoopStateChanged(self.loop_state).send();
607 #[cfg(feature = "mpris")]
608 {
609 use mpris_server::Property;
610
611 let properties = vec![
612 Property::LoopStatus(mpris::loop_state_2_mpris(&self.loop_state)),
613 Property::CanGoPrevious(self.can_go_previous()),
614 Property::CanGoNext(self.can_go_next()),
615 ];
616 mpris::update_properties(properties);
617 }
618 }
619
620 pub(crate) fn set_playback_state(&mut self, playback_state: PlaybackState) {
621 self.playback_state = playback_state;
622 if self.playback_state == PlaybackState::Stopped {
623 self.set_player_position(Duration::default());
624 }
625 self.on_playback_state_changed();
626 }
627
628 pub(crate) fn play_track(&mut self, index: usize) -> Option<&Arc<Track>> {
629 if index < self.tracks.len() {
630 self.position = index;
631 self.on_track_changed();
632 self.set_player_position(Duration::default());
633 self.current()
634 } else {
635 None
636 }
637 }
638
639 pub(crate) fn next_track(&mut self) -> Option<&Arc<Track>> {
640 match self.loop_state {
641 LoopState::Off => {
642 if !self.tracks.is_empty() && self.position < self.tracks.len() - 1 {
643 self.position += 1;
644 self.on_track_changed();
645 return self.current();
646 }
647 None
648 }
649 LoopState::Track => {
650 self.on_track_changed();
651 self.current()
652 }
653 LoopState::Playlist => {
654 if self.tracks.is_empty() {
655 None
656 } else if !self.tracks.is_empty() && self.position < self.tracks.len() - 1 {
657 self.position += 1;
658 self.on_track_changed();
659 self.current()
660 } else {
661 self.position = 0;
662 self.on_track_changed();
663 self.current()
664 }
665 }
666 }
667 }
668
669 pub(crate) fn previous_track(&mut self) -> Option<&Arc<Track>> {
670 match self.loop_state {
671 LoopState::Off => {
672 if self.position > 0 && !self.tracks.is_empty() {
673 self.position -= 1;
674 self.on_track_changed();
675 self.current()
676 } else {
677 None
678 }
679 }
680 LoopState::Track => {
681 self.on_track_changed();
682 self.current()
683 }
684 LoopState::Playlist => {
685 if self.tracks.is_empty() {
686 None
687 } else if self.position > 0 {
688 self.position -= 1;
689 self.on_track_changed();
690 self.current()
691 } else if !self.tracks.is_empty() {
692 self.position = self.tracks.len() - 1;
693 self.on_track_changed();
694 self.current()
695 } else {
696 None
697 }
698 }
699 }
700 }
701
702 fn on_track_changed(&self) {
703 Event::PositionChanged(self.position).send();
704
705 #[cfg(feature = "mpris")]
706 {
707 use mpris_server::{Metadata, Property};
708
709 let properties = vec![
710 match self.current() {
711 Some(track) => Property::Metadata(mpris::get_meta(track, self.position)),
712 None => Property::Metadata(Metadata::new()),
713 },
714 Property::CanGoPrevious(self.can_go_previous()),
715 Property::CanGoNext(self.can_go_next()),
716 ];
717 mpris::update_properties(properties);
718 mpris::set_position(self.player_position);
719 }
720 }
721
722 #[cfg(feature = "mpris")]
723 pub(crate) fn get_mpris_properties(&self) -> Vec<mpris_server::Property> {
724 use mpris_server::{Metadata, Property};
725
726 vec![
727 Property::PlaybackStatus(mpris::playback_state_2_mpris(&self.playback_state)),
728 Property::LoopStatus(mpris::loop_state_2_mpris(&self.loop_state)),
729 Property::Shuffle(self.shuffle_state.enabled()),
730 Property::Volume(self.player_volume.get()),
731 Property::CanGoNext(self.can_go_next()),
732 Property::CanGoPrevious(self.can_go_previous()),
733 Property::CanPlay(self.can_play()),
734 Property::CanPause(self.can_pause()),
735 Property::CanSeek(self.can_seek()),
736 match self.current() {
737 Some(track) => Property::Metadata(mpris::get_meta(track, self.position)),
738 None => Property::Metadata(Metadata::new()),
739 },
740 ]
741 }
742}
743
744#[derive(Debug, Clone, Serialize, Deserialize)]
745struct PlayerStateRaw {
746 position: usize,
747 player_position: Duration,
748 player_volume: PlayerVolume,
749 track_hashes: Vec<u64>,
750 shuffled_track_indices: Vec<usize>,
751 queue_source: QueueSource,
752 shuffle_state: ShuffleState,
753 loop_state: LoopState,
754}
755
756impl From<PlayerState> for PlayerStateRaw {
757 fn from(value: PlayerState) -> Self {
758 let track_hashes = Track::hash_tracks(value.tracks);
759 Self {
760 position: value.position,
761 player_position: value.player_position,
762 player_volume: value.player_volume,
763 track_hashes,
764 shuffled_track_indices: value.shuffled_track_indices,
765 queue_source: value.queue_source,
766 shuffle_state: value.shuffle_state,
767 loop_state: value.loop_state,
768 }
769 }
770}
771
772static STATE_FILE: LazyLock<PathBuf> = LazyLock::new(|| {
773 let mut data = CACHE_DIR.read().unwrap().clone().unwrap();
774 data.push("player_state");
775 data
776});
777
778pub(crate) static PLAYER_STATE: LazyLock<Arc<RwLock<Option<PlayerState>>>> =
779 LazyLock::new(|| Arc::new(RwLock::new(None)));
780
781pub(crate) fn unwrap_state_ref(maybe_state: Option<&PlayerState>) -> Result<&PlayerState, Error> {
782 match maybe_state {
783 Some(state) => Ok(state),
784 None => Err(Error::StateNotInitialized),
785 }
786}
787
788pub(crate) fn unwrap_state_mut(
789 maybe_state: Option<&mut PlayerState>,
790) -> Result<&mut PlayerState, Error> {
791 match maybe_state {
792 Some(state) => Ok(state),
793 None => Err(Error::StateNotInitialized),
794 }
795}
796
797pub(crate) fn save_state(state: PlayerState) -> Result<(), String> {
798 let state_raw: PlayerStateRaw = state.into();
799 let state_file = STATE_FILE.clone();
800
801 let mut data = Vec::new();
802 if let Err(e) = state_raw.serialize(&mut Serializer::new(&mut data)) {
803 error!("Could not serialize the player state: {e}");
804 return Err(e.to_string());
805 }
806
807 let mut file = match File::create(state_file) {
808 Ok(file) => file,
809 Err(e) => {
810 error!("Could not open the player state cache in write-only mode: {e}");
811 return Err(e.to_string());
812 }
813 };
814
815 match file.write_all(&data) {
816 Ok(_) => {
817 trace!("Saved player state to cache");
818 Ok(())
819 }
820 Err(e) => {
821 error!("Could not write to the player state cache: {e}");
822 Err(e.to_string())
823 }
824 }
825}
826
827pub(crate) fn background_save_state(state: PlayerState) {
828 thread::spawn(|| {
829 if let Err(e) = save_state(state) {
830 error!("Could not save player state to cache: {e}");
831 }
832 });
833}
834
835pub(crate) fn restore_state_from_cache() -> Result<PlayerState, String> {
836 let state_file = STATE_FILE.clone();
837
838 trace!("Restoring player state from {state_file:?}");
839
840 let state_exists = match state_file.try_exists() {
841 Ok(exists) => exists,
842 Err(e) => {
843 error!("Could not check if the player state file exists: {e}");
844 return Err(e.to_string());
845 }
846 };
847
848 if state_exists {
849 let data = match read(state_file) {
850 Ok(data) => data,
851 Err(e) => {
852 error!("Could not read the player state cache: {e}");
853 return Err(e.to_string());
854 }
855 };
856
857 let state_raw = match PlayerStateRaw::deserialize(&mut Deserializer::from_read_ref(&data)) {
858 Ok(data) => data,
859 Err(e) => {
860 error!("Could not decode the contents of the player state file: {e}");
861 return Err(e.to_string());
862 }
863 };
864
865 match PlayerState::try_from(state_raw) {
866 Ok(state) => Ok(state),
867 Err(e) => {
868 error!("Could not restore player state from cache: {e}");
869 Err(e.to_string())
870 }
871 }
872 } else {
873 Ok(PlayerState::default())
874 }
875}