2018-02-10 10:26:26 +00:00
|
|
|
use std::fs;
|
2017-01-29 15:36:39 +00:00
|
|
|
use std::fs::File;
|
2021-01-23 21:37:41 +00:00
|
|
|
use std::io::{self, Error, ErrorKind, Read, Write};
|
|
|
|
use std::path::{Path, PathBuf};
|
2017-01-29 15:36:39 +00:00
|
|
|
|
2019-10-08 09:31:18 +00:00
|
|
|
use crate::authentication::Credentials;
|
|
|
|
use crate::spotify_id::FileId;
|
2017-01-29 15:36:39 +00:00
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
/// A cache for volume, credentials and audio files.
|
2017-01-31 08:21:30 +00:00
|
|
|
#[derive(Clone)]
|
2017-01-29 15:36:39 +00:00
|
|
|
pub struct Cache {
|
2021-01-25 01:22:25 +00:00
|
|
|
credentials_location: Option<PathBuf>,
|
|
|
|
volume_location: Option<PathBuf>,
|
|
|
|
audio_location: Option<PathBuf>,
|
2017-01-29 15:36:39 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
impl Cache {
|
|
|
|
pub fn new<P: AsRef<Path>>(
|
|
|
|
system_location: Option<P>,
|
|
|
|
audio_location: Option<P>,
|
|
|
|
) -> io::Result<Self> {
|
|
|
|
if let Some(location) = &system_location {
|
|
|
|
fs::create_dir_all(location)?;
|
2018-02-10 10:26:26 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
if let Some(location) = &audio_location {
|
|
|
|
fs::create_dir_all(location)?;
|
2020-08-02 08:52:09 +00:00
|
|
|
}
|
2017-01-29 15:36:39 +00:00
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
let audio_location = audio_location.map(|p| p.as_ref().to_owned());
|
|
|
|
let volume_location = system_location.as_ref().map(|p| p.as_ref().join("volume"));
|
|
|
|
let credentials_location = system_location
|
|
|
|
.as_ref()
|
|
|
|
.map(|p| p.as_ref().join("credentials.json"));
|
2016-03-16 04:07:04 +00:00
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
let cache = Cache {
|
|
|
|
credentials_location,
|
|
|
|
volume_location,
|
|
|
|
audio_location,
|
|
|
|
};
|
2021-01-23 21:37:41 +00:00
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
Ok(cache)
|
2016-03-16 04:07:04 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 15:36:39 +00:00
|
|
|
pub fn credentials(&self) -> Option<Credentials> {
|
2021-01-25 01:22:25 +00:00
|
|
|
let location = self.credentials_location.as_ref()?;
|
|
|
|
|
|
|
|
// This closure is just convencience to enable the question mark operator
|
|
|
|
let read = || {
|
|
|
|
let mut file = File::open(location)?;
|
|
|
|
let mut contents = String::new();
|
|
|
|
file.read_to_string(&mut contents)?;
|
|
|
|
serde_json::from_str(&contents).map_err(|e| Error::new(ErrorKind::InvalidData, e))
|
|
|
|
};
|
|
|
|
|
|
|
|
match read() {
|
2021-01-23 21:37:41 +00:00
|
|
|
Ok(c) => Some(c),
|
|
|
|
Err(e) => {
|
2021-01-25 01:22:25 +00:00
|
|
|
// If the file did not exist, the file was probably not written
|
|
|
|
// before. Otherwise, log the error.
|
2021-01-23 21:37:41 +00:00
|
|
|
if e.kind() != ErrorKind::NotFound {
|
|
|
|
warn!("Error reading credentials from cache: {}", e);
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2016-03-16 04:07:04 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 15:36:39 +00:00
|
|
|
pub fn save_credentials(&self, cred: &Credentials) {
|
2021-01-25 01:22:25 +00:00
|
|
|
if let Some(location) = &self.credentials_location {
|
|
|
|
let result = File::create(location).and_then(|mut file| {
|
|
|
|
let data = serde_json::to_string(cred)?;
|
|
|
|
write!(file, "{}", data)
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Err(e) = result {
|
|
|
|
warn!("Cannot save credentials to cache: {}", e)
|
|
|
|
}
|
2021-01-23 21:37:41 +00:00
|
|
|
}
|
2016-03-16 04:07:04 +00:00
|
|
|
}
|
2021-01-23 21:37:41 +00:00
|
|
|
|
2021-01-25 09:52:06 +00:00
|
|
|
pub fn volume(&self) -> Option<u16> {
|
2021-01-25 01:22:25 +00:00
|
|
|
let location = self.volume_location.as_ref()?;
|
|
|
|
|
|
|
|
let read = || {
|
|
|
|
let mut file = File::open(location)?;
|
|
|
|
let mut contents = String::new();
|
|
|
|
file.read_to_string(&mut contents)?;
|
|
|
|
contents
|
|
|
|
.parse()
|
|
|
|
.map_err(|e| Error::new(ErrorKind::InvalidData, e))
|
|
|
|
};
|
|
|
|
|
|
|
|
match read() {
|
2021-01-23 21:37:41 +00:00
|
|
|
Ok(v) => Some(v),
|
|
|
|
Err(e) => {
|
|
|
|
if e.kind() != ErrorKind::NotFound {
|
|
|
|
warn!("Error reading volume from cache: {}", e);
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2018-05-17 01:15:17 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 09:52:06 +00:00
|
|
|
pub fn save_volume(&self, volume: u16) {
|
2021-01-25 01:22:25 +00:00
|
|
|
if let Some(ref location) = self.volume_location {
|
|
|
|
let result = File::create(location).and_then(|mut file| write!(file, "{}", volume));
|
|
|
|
if let Err(e) = result {
|
|
|
|
warn!("Cannot save volume to cache: {}", e);
|
|
|
|
}
|
2021-01-23 21:37:41 +00:00
|
|
|
}
|
2018-05-17 01:15:17 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
fn file_path(&self, file: FileId) -> Option<PathBuf> {
|
|
|
|
self.audio_location.as_ref().map(|location| {
|
|
|
|
let name = file.to_base16();
|
|
|
|
let mut path = location.join(&name[0..2]);
|
|
|
|
path.push(&name[2..]);
|
|
|
|
path
|
|
|
|
})
|
2017-01-29 15:36:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn file(&self, file: FileId) -> Option<File> {
|
2021-01-25 01:22:25 +00:00
|
|
|
File::open(self.file_path(file)?)
|
2021-01-23 21:37:41 +00:00
|
|
|
.map_err(|e| {
|
|
|
|
if e.kind() != ErrorKind::NotFound {
|
|
|
|
warn!("Error reading file from cache: {}", e)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.ok()
|
2017-01-29 15:36:39 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 01:22:25 +00:00
|
|
|
pub fn save_file<F: Read>(&self, file: FileId, contents: &mut F) {
|
|
|
|
if let Some(path) = self.file_path(file) {
|
|
|
|
let parent = path.parent().unwrap();
|
|
|
|
let result = fs::create_dir_all(parent)
|
|
|
|
.and_then(|_| File::create(path))
|
|
|
|
.and_then(|mut file| io::copy(contents, &mut file));
|
|
|
|
|
|
|
|
if let Err(e) = result {
|
|
|
|
warn!("Cannot save file to cache: {}", e)
|
|
|
|
}
|
2017-06-30 06:43:11 +00:00
|
|
|
}
|
2017-01-29 15:36:39 +00:00
|
|
|
}
|
|
|
|
}
|