Skip to main content

chilen_backend/playback/
state.rs

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