diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index c65482bd..bae69419 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -430,9 +430,7 @@ impl AudioFile { .map(move |mut file| { if let Some(cache) = session_.cache() { debug!("File {} complete, saving to cache", file_id); - if let Err(err) = cache.save_file(file_id, &mut file) { - warn!("Cannot save file to cache: {}", err); - } + cache.save_file(file_id, &mut file); } else { debug!("File {} complete", file_id); } diff --git a/core/src/cache.rs b/core/src/cache.rs index 4dcce968..76d49fa9 100644 --- a/core/src/cache.rs +++ b/core/src/cache.rs @@ -7,59 +7,58 @@ use crate::authentication::Credentials; use crate::spotify_id::FileId; use crate::volume::Volume; +/// A cache for volume, credentials and audio files. #[derive(Clone)] pub struct Cache { - audio_root: PathBuf, - system_root: PathBuf, - use_audio_cache: bool, -} - -fn mkdir_existing(path: &Path) -> io::Result<()> { - fs::create_dir(path).or_else(|err| { - if err.kind() == io::ErrorKind::AlreadyExists { - Ok(()) - } else { - Err(err) - } - }) + credentials_location: Option, + volume_location: Option, + audio_location: Option, } impl Cache { - pub fn new( - audio_location: PathBuf, - system_location: PathBuf, - use_audio_cache: bool, - ) -> io::Result { - if use_audio_cache { - mkdir_existing(&audio_location)?; - mkdir_existing(&audio_location.join("files"))?; + pub fn new>( + system_location: Option

, + audio_location: Option

, + ) -> io::Result { + if let Some(location) = &system_location { + fs::create_dir_all(location)?; } - mkdir_existing(&system_location)?; - Ok(Cache { - audio_root: audio_location, - system_root: system_location, - use_audio_cache, - }) - } -} + if let Some(location) = &audio_location { + fs::create_dir_all(location)?; + } -impl Cache { - fn open_credentials_file(&self) -> io::Result { - File::open(self.system_root.join("credentials.json")) - } + 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")); - fn read_credentials(&self) -> io::Result { - let mut file = self.open_credentials_file()?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - serde_json::from_str(&contents).map_err(|e| Error::new(ErrorKind::InvalidData, e)) + let cache = Cache { + credentials_location, + volume_location, + audio_location, + }; + + Ok(cache) } pub fn credentials(&self) -> Option { - match self.read_credentials() { + 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() { Ok(c) => Some(c), Err(e) => { + // If the file did not exist, the file was probably not written + // before. Otherwise, log the error. if e.kind() != ErrorKind::NotFound { warn!("Error reading credentials from cache: {}", e); } @@ -69,32 +68,31 @@ impl Cache { } pub fn save_credentials(&self, cred: &Credentials) { - let result = self - .open_credentials_file() - .and_then(|mut file| write!(file, "{}", serde_json::to_string(cred)?)); - if let Err(e) = result { - warn!("Cannot save credentials to cache: {}", e); + 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) + } } } -} - -// cache volume to system_root/volume -impl Cache { - fn open_volume_file(&self) -> io::Result { - File::open(self.system_root.join("volume")) - } - - fn read_volume(&self) -> io::Result { - let mut file = self.open_volume_file()?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - contents - .parse() - .map_err(|e| Error::new(ErrorKind::InvalidData, e)) - } pub fn volume(&self) -> Option { - match self.read_volume() { + 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() { Ok(v) => Some(v), Err(e) => { if e.kind() != ErrorKind::NotFound { @@ -106,26 +104,25 @@ impl Cache { } pub fn save_volume(&self, volume: Volume) { - let result = self - .open_volume_file() - .and_then(|mut file| write!(file, "{}", volume)); - if let Err(e) = result { - warn!("Cannot save volume to cache: {}", e); + 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); + } } } -} -impl Cache { - fn file_path(&self, file: FileId) -> PathBuf { - let name = file.to_base16(); - self.audio_root - .join("files") - .join(&name[0..2]) - .join(&name[2..]) + fn file_path(&self, file: FileId) -> Option { + 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 + }) } pub fn file(&self, file: FileId) -> Option { - File::open(self.file_path(file)) + File::open(self.file_path(file)?) .map_err(|e| { if e.kind() != ErrorKind::NotFound { warn!("Error reading file from cache: {}", e) @@ -134,30 +131,16 @@ impl Cache { .ok() } - pub fn save_file(&self, file: FileId, contents: &mut F) -> io::Result<()> { - if self.use_audio_cache { - let path = self.file_path(file); - mkdir_existing(path.parent().unwrap())?; + pub fn save_file(&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)); - let mut cache_file = File::create(path).or_else(|_| { - fs::remove_dir_all(&self.audio_root.join("files"))?; - mkdir_existing(&self.audio_root.join("files"))?; - - let path = self.file_path(file); - mkdir_existing(path.parent().unwrap())?; - File::create(path) - })?; - - io::copy(contents, &mut cache_file).or_else(|_| { - fs::remove_dir_all(&self.audio_root.join("files"))?; - mkdir_existing(&self.audio_root.join("files"))?; - - let path = self.file_path(file); - mkdir_existing(path.parent().unwrap())?; - let mut file = File::create(path)?; - io::copy(contents, &mut file) - })?; + if let Err(e) = result { + warn!("Cannot save file to cache: {}", e) + } } - Ok(()) } } diff --git a/src/main.rs b/src/main.rs index 8c029f5a..b22d7d5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use sha1::{Digest, Sha1}; use std::env; use std::io::{self, stderr, Write}; use std::mem; -use std::path::PathBuf; +use std::path::Path; use std::process::exit; use std::str::FromStr; use std::time::Instant; @@ -254,22 +254,32 @@ fn setup(args: &[String]) -> Setup { mapped_volume: !matches.opt_present("mixer-linear-volume"), }; - let use_audio_cache = !matches.opt_present("disable-audio-cache"); + let cache = { + let audio_dir; + let system_dir; + if matches.opt_present("disable-audio-cache") { + audio_dir = None; + system_dir = matches + .opt_str("system-cache") + .or_else(|| matches.opt_str("c")) + .map(|p| p.into()); + } else { + let cache_dir = matches.opt_str("c"); + audio_dir = cache_dir + .as_ref() + .map(|p| AsRef::::as_ref(p).join("files")); + system_dir = matches + .opt_str("system-cache") + .or_else(|| cache_dir) + .map(|p| p.into()); + } - let cache_directory = matches.opt_str("c").unwrap_or(String::from("")); - let system_cache_directory = matches - .opt_str("system-cache") - .unwrap_or(String::from(cache_directory.clone())); - - let cache = match Cache::new( - PathBuf::from(cache_directory), - PathBuf::from(system_cache_directory), - use_audio_cache, - ) { - Ok(cache) => Some(cache), - Err(e) => { - warn!("Cannot create cache: {}", e); - None + match Cache::new(system_dir, audio_dir) { + Ok(cache) => Some(cache), + Err(e) => { + warn!("Cannot create cache: {}", e); + None + } } };