aboutsummaryrefslogtreecommitdiff
path: root/src/state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/state.rs')
-rw-r--r--src/state.rs81
1 files changed, 49 insertions, 32 deletions
diff --git a/src/state.rs b/src/state.rs
index b9a6618..a04412f 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,36 +1,36 @@
-use std::{collections::HashSet, fs};
-
-use derive_more::Display;
+use std::{collections::HashSet, fmt, fs};
+use directories::ProjectDirs;
use expanduser::expanduser;
use serde::{Deserialize, Serialize};
use walkdir::WalkDir;
-use crate::{
- config::{Config, default_state_path},
- learner::Learner,
- trajectory::Trajectory,
-};
+use crate::{config::Config, learner::Learner, trajectory::Trajectory};
+
+fn default_state_path() -> String {
+ let name = "mood.bin";
+ ProjectDirs::from("qualifier", "organisation", "mood")
+ .and_then(|pd| pd.config_dir().join(name).to_str().map(|x| x.to_string()))
+ .unwrap_or(format!("~/.config/mood/{name}"))
+}
#[derive(Serialize, Deserialize, Default)]
pub struct State {
- pub config: Config,
trajectory: Trajectory,
learner: Learner,
albums: HashSet<String>,
}
impl State {
- pub fn next(&mut self) -> Option<(String, f32)> {
- let learning = self.trajectory.step(self.config.skip_window_secs);
+ pub fn next(&mut self, config: &Config) -> Option<(String, f32)> {
+ let learning = self.trajectory.step(config.skip_window_secs);
if let Some(ref learning) = learning {
- println!("Obtained learning: {learning:?}");
- self.learner.learn(&self.config, learning);
+ self.learner.learn(learning);
}
if let Some((new_album, prob)) = self.learner.sample(
self.trajectory.slice(),
&learning.as_ref().into(),
&self.albums,
- self.config.temperature,
+ config,
) {
self.trajectory.log(&new_album);
Some((new_album, prob))
@@ -40,36 +40,39 @@ impl State {
}
}
-#[derive(Display)]
-#[display("+{added}/-{removed}, total {new_total}")]
pub struct UpdateResult {
added: usize,
removed: usize,
new_total: usize,
}
+impl fmt::Display for UpdateResult {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "+{}/-{}, total {}", self.added, self.removed, self.new_total)
+ }
+}
+
impl State {
- pub fn load(path: &str) -> Result<State, String> {
- expanduser(path)
+ pub fn try_load() -> Result<State, String> {
+ expanduser(default_state_path())
.map_err(|e| e.to_string())
- .and_then(|p| fs::read_to_string(p).map_err(|e| e.to_string()))
- .and_then(|contents| ron::from_str(&contents).map_err(|e| e.to_string()))
+ .and_then(|p| fs::read(p).map_err(|e| e.to_string()))
+ .and_then(|contents| bincode::deserialize(&contents).map_err(|e| e.to_string()))
}
pub fn try_save(&self) -> Result<(), String> {
let str_path = default_state_path();
let path = expanduser(&str_path).map_err(|e| e.to_string())?;
- _ = path.parent().map(|parent| std::fs::create_dir_all(parent));
- let serialised = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default())
- .map_err(|e| format!("Failed to serialize state: {}", e))?;
+ _ = path.parent().map(std::fs::create_dir_all);
+ let serialised =
+ bincode::serialize(self).map_err(|e| format!("Failed to serialize state: {}", e))?;
fs::write(&path, serialised)
.map_err(|e| format!("Failed to write state file at {}: {}", str_path, e))?;
Ok(())
}
- pub fn update_albums(&mut self) -> UpdateResult {
- let roots: Vec<_> = self
- .config
+ pub fn update_albums(&mut self, config: &Config) -> UpdateResult {
+ let roots: Vec<_> = config
.music_roots
.iter()
.filter_map(|x| expanduser(x).ok().filter(|p| p.is_dir()))
@@ -77,14 +80,14 @@ impl State {
let mut new_albums: HashSet<String> = HashSet::new();
- for root_path in roots {
+ for root_path in &roots {
for entry in WalkDir::new(&root_path) {
let entry = match entry {
Ok(e) if e.path().is_dir() => e,
_ => continue,
};
- if !self.contains_audio(&entry) {
+ if !Self::contains_audio(&entry, config) {
continue;
}
@@ -113,21 +116,35 @@ impl State {
}
}
- fn contains_audio(&self, entry: &walkdir::DirEntry) -> bool {
+ fn contains_audio(entry: &walkdir::DirEntry, config: &Config) -> bool {
fs::read_dir(entry.path()).ok().is_some_and(|mut entries| {
entries.any(|e| {
e.as_ref()
- .is_ok_and(|entry| self.is_audio_file(&entry.path()))
+ .is_ok_and(|entry| Self::is_audio_file(&entry.path(), config))
})
})
}
- fn is_audio_file(&self, path: &std::path::Path) -> bool {
+ fn is_audio_file(path: &std::path::Path, config: &Config) -> bool {
!path.is_dir()
&& path
.extension()
.and_then(|ext| ext.to_str())
.map(|s| s.to_lowercase())
- .is_some_and(|ext| self.config.audio_exts.contains(&format!(".{ext}")))
+ .is_some_and(|ext| config.audio_exts.contains(&format!(".{ext}")))
+ }
+}
+
+impl State {
+ pub fn dump_albums(&self) -> Result<String, String> {
+ serde_json::to_string_pretty(&self.albums).map_err(|e| e.to_string())
+ }
+
+ pub fn dump_learner(&self) -> Result<String, String> {
+ serde_json::to_string_pretty(&self.learner).map_err(|e| e.to_string())
+ }
+
+ pub fn dump_trajectory(&self) -> Result<String, String> {
+ serde_json::to_string_pretty(&self.trajectory).map_err(|e| e.to_string())
}
}