Modernized error handling
- Use error-chain instead of writing tons of boilerplate - Switched try!() macros to '?'
This commit is contained in:
parent
15505f8991
commit
ec8a8da81e
14 changed files with 249 additions and 317 deletions
57
Cargo.lock
generated
57
Cargo.lock
generated
|
@ -4,6 +4,7 @@ version = "0.2.0"
|
|||
dependencies = [
|
||||
"ape 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_dirs 1.1.1 (git+https://github.com/agersant/app-dirs-rs)",
|
||||
"error-chain 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"id3 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -56,6 +57,29 @@ dependencies = [
|
|||
"xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.7.0"
|
||||
|
@ -88,6 +112,11 @@ name = "byteorder"
|
|||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.0.0"
|
||||
|
@ -112,6 +141,15 @@ dependencies = [
|
|||
"url 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbghelp-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deque"
|
||||
version = "0.3.1"
|
||||
|
@ -199,6 +237,14 @@ dependencies = [
|
|||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "0.2.14"
|
||||
|
@ -788,6 +834,11 @@ dependencies = [
|
|||
"url 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.21"
|
||||
|
@ -1050,14 +1101,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum ape 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b419c2e36e91776200588f91e24c970d16d34236369136ca819f12dd903c5691"
|
||||
"checksum app_dirs 1.1.1 (git+https://github.com/agersant/app-dirs-rs)" = "<none>"
|
||||
"checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80"
|
||||
"checksum backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3602e8d8c43336088a8505fa55cae2b3884a9be29440863a11528a42f46f6bb7"
|
||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bodyparser 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07b171b407e583dc8f01011a713f20575a81ac60acecf3b8153012709aeb1fd6"
|
||||
"checksum buf_redux 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "861b9d19b9f5cb40647242d10d0cb0a13de0a96d5ff8c8a01ea324fa3956eb7d"
|
||||
"checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304"
|
||||
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
|
||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||
"checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d"
|
||||
"checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8"
|
||||
"checksum cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e3d6405328b6edb412158b3b7710e2634e23f3614b9bb1c412df7952489a626"
|
||||
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
|
||||
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
||||
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
|
||||
"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
|
||||
|
@ -1069,6 +1124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
|
||||
"checksum enum_primitive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f79eff5be92a4d7d5bddf7daa7d650717ea71628634efe6ca7bcda85b2183c23"
|
||||
"checksum error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc"
|
||||
"checksum error-chain 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd681735364a04cd5d69f01a4f6768e70473941f8d86d8c224faf6955a75799"
|
||||
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
|
||||
"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
|
||||
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
|
||||
|
@ -1136,6 +1192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum route-recognizer 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4f0a750d020adb1978f5964ea7bca830585899b09da7cbb3f04961fc2400122d"
|
||||
"checksum router 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b94397bfa5b772b4375be4da12560a7c1c1e74b2e35c46ed312958aad56df726"
|
||||
"checksum rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1430d286cadb237c17c885e25447c982c97113926bb579f4379c0eca8d9586dc"
|
||||
"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818"
|
||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||
"checksum scoped_threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef399c8893e8cb7aa9696e895427fab3a6bf265977bb96e126f24ddd2cda85a"
|
||||
|
|
|
@ -9,6 +9,7 @@ ui = []
|
|||
[dependencies]
|
||||
ape = "0.1.2"
|
||||
app_dirs = { git = "https://github.com/agersant/app-dirs-rs" }
|
||||
error-chain = "0.7.1"
|
||||
getopts = "0.2.14"
|
||||
hyper = "0.9.10"
|
||||
id3 = "0.1.11"
|
||||
|
|
50
src/api.rs
50
src/api.rs
|
@ -1,4 +1,3 @@
|
|||
use core::str::Utf8Error;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::*;
|
||||
|
@ -15,7 +14,7 @@ use rustc_serialize::json;
|
|||
use url::percent_encoding::percent_decode;
|
||||
|
||||
use collection::*;
|
||||
use error::*;
|
||||
use errors::*;
|
||||
use thumbnails::*;
|
||||
use utils::*;
|
||||
|
||||
|
@ -37,27 +36,6 @@ impl Version {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<PError> for IronError {
|
||||
fn from(err: PError) -> IronError {
|
||||
match err {
|
||||
PError::Io(e) => IronError::new(e, status::NotFound),
|
||||
PError::CannotClearExistingIndex => IronError::new(err, status::InternalServerError),
|
||||
PError::PathDecoding => IronError::new(err, status::InternalServerError),
|
||||
PError::ConfigDirectoryError => IronError::new(err, status::InternalServerError),
|
||||
PError::CacheDirectoryError => IronError::new(err, status::InternalServerError),
|
||||
PError::PathNotInVfs => IronError::new(err, status::NotFound),
|
||||
PError::CannotServeDirectory => IronError::new(err, status::BadRequest),
|
||||
PError::UnsupportedFileType => IronError::new(err, status::BadRequest),
|
||||
PError::AlbumArtSearchError => IronError::new(err, status::InternalServerError),
|
||||
PError::ImageProcessingError => IronError::new(err, status::InternalServerError),
|
||||
PError::UnsupportedMetadataFormat => IronError::new(err, status::InternalServerError),
|
||||
PError::MetadataDecodingError => IronError::new(err, status::InternalServerError),
|
||||
PError::Unauthorized => IronError::new(err, status::Unauthorized),
|
||||
PError::IncorrectCredentials => IronError::new(err, status::BadRequest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_api_handler(collection: Arc<Collection>) -> Mount {
|
||||
let mut api_handler = Mount::new();
|
||||
|
||||
|
@ -97,20 +75,18 @@ pub fn get_api_handler(collection: Arc<Collection>) -> Mount {
|
|||
api_handler
|
||||
}
|
||||
|
||||
fn path_from_request(request: &Request) -> Result<PathBuf, Utf8Error> {
|
||||
fn path_from_request(request: &Request) -> Result<PathBuf> {
|
||||
let path_string = request.url.path().join("\\");
|
||||
let decoded_path = try!(percent_decode(path_string.as_bytes()).decode_utf8());
|
||||
let decoded_path = percent_decode(path_string.as_bytes()).decode_utf8()?;
|
||||
Ok(PathBuf::from(decoded_path.deref()))
|
||||
}
|
||||
|
||||
struct AuthRequirement;
|
||||
impl BeforeMiddleware for AuthRequirement {
|
||||
fn before(&self, req: &mut Request) -> IronResult<()> {
|
||||
let auth_cookie = req.get_cookie("username");
|
||||
if auth_cookie.is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(IronError::new(PError::Unauthorized, status::Unauthorized))
|
||||
match req.get_cookie("username") {
|
||||
Some(_) => Ok(()),
|
||||
None => Err(Error::from(ErrorKind::AuthenticationRequired).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,11 +103,11 @@ fn auth(request: &mut Request, collection: &Collection) -> IronResult<Response>
|
|||
let input = request.get_ref::<params::Params>().unwrap();
|
||||
let username = match input.find(&["username"]) {
|
||||
Some(¶ms::Value::String(ref username)) => username,
|
||||
_ => return Err(IronError::from(PError::IncorrectCredentials)),
|
||||
_ => return Err(Error::from(ErrorKind::MissingUsername).into()),
|
||||
};
|
||||
let password = match input.find(&["password"]) {
|
||||
Some(¶ms::Value::String(ref password)) => password,
|
||||
_ => return Err(IronError::from(PError::IncorrectCredentials)),
|
||||
_ => return Err(Error::from(ErrorKind::MissingPassword).into()),
|
||||
};
|
||||
if collection.auth(username.as_str(), password.as_str()) {
|
||||
let mut response = Response::with((status::Ok, ""));
|
||||
|
@ -140,7 +116,7 @@ fn auth(request: &mut Request, collection: &Collection) -> IronResult<Response>
|
|||
response.set_cookie(username_cookie);
|
||||
Ok(response)
|
||||
} else {
|
||||
Err(IronError::from(PError::IncorrectCredentials))
|
||||
Err(Error::from(ErrorKind::IncorrectCredentials).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +126,7 @@ fn browse(request: &mut Request, collection: &Collection) -> IronResult<Response
|
|||
Err(e) => return Err(IronError::new(e, status::BadRequest)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
let browse_result = try!(collection.browse(&path));
|
||||
let browse_result = collection.browse(&path)?;
|
||||
|
||||
let result_json = json::encode(&browse_result);
|
||||
let result_json = match result_json {
|
||||
|
@ -167,7 +143,7 @@ fn flatten(request: &mut Request, collection: &Collection) -> IronResult<Respons
|
|||
Err(e) => return Err(IronError::new(e, status::BadRequest)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
let flatten_result = try!(collection.flatten(&path));
|
||||
let flatten_result = collection.flatten(&path)?;
|
||||
|
||||
let result_json = json::encode(&flatten_result);
|
||||
let result_json = match result_json {
|
||||
|
@ -204,7 +180,7 @@ fn serve(request: &mut Request, collection: &Collection) -> IronResult<Response>
|
|||
};
|
||||
|
||||
if !metadata.is_file() {
|
||||
return Err(IronError::from(PError::CannotServeDirectory));
|
||||
return Err(Error::from(ErrorKind::CannotServeDirectory).into());
|
||||
}
|
||||
|
||||
if is_song(real_path.as_path()) {
|
||||
|
@ -215,7 +191,7 @@ fn serve(request: &mut Request, collection: &Collection) -> IronResult<Response>
|
|||
return art(request, real_path.as_path());
|
||||
}
|
||||
|
||||
Err(IronError::from(PError::UnsupportedFileType))
|
||||
Err(Error::from(ErrorKind::UnsupportedFileType).into())
|
||||
}
|
||||
|
||||
fn art(_: &mut Request, real_path: &Path) -> IronResult<Response> {
|
||||
|
|
|
@ -4,9 +4,9 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use config::Config;
|
||||
use errors::*;
|
||||
use index::*;
|
||||
use vfs::*;
|
||||
use error::*;
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -39,7 +39,7 @@ impl Collection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_config(&mut self, config: &Config) -> Result<(), PError> {
|
||||
pub fn load_config(&mut self, config: &Config) -> Result<()> {
|
||||
self.users = config.users.to_vec();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -48,15 +48,15 @@ impl Collection {
|
|||
self.users.iter().any(|u| u.name == username && u.password == password)
|
||||
}
|
||||
|
||||
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>, PError> {
|
||||
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>> {
|
||||
self.index.deref().browse(virtual_path)
|
||||
}
|
||||
|
||||
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>, PError> {
|
||||
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>> {
|
||||
self.index.deref().flatten(virtual_path)
|
||||
}
|
||||
|
||||
pub fn locate(&self, virtual_path: &Path) -> Result<PathBuf, PError> {
|
||||
pub fn locate(&self, virtual_path: &Path) -> Result<PathBuf> {
|
||||
self.vfs.virtual_to_real(virtual_path)
|
||||
}
|
||||
}
|
||||
|
|
158
src/config.rs
158
src/config.rs
|
@ -1,12 +1,12 @@
|
|||
use regex;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::path;
|
||||
use toml;
|
||||
|
||||
use collection::User;
|
||||
use ddns::DDNSConfig;
|
||||
use errors::*;
|
||||
use index::IndexConfig;
|
||||
use utils;
|
||||
use vfs::VfsConfig;
|
||||
|
@ -27,34 +27,6 @@ const CONFIG_DDNS_HOST: &'static str = "host";
|
|||
const CONFIG_DDNS_USERNAME: &'static str = "username";
|
||||
const CONFIG_DDNS_PASSWORD: &'static str = "password";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigError {
|
||||
IoError(io::Error),
|
||||
CacheDirectoryError,
|
||||
ConfigDirectoryError,
|
||||
TOMLParseError,
|
||||
RegexError(regex::Error),
|
||||
SecretParseError,
|
||||
SleepDurationParseError,
|
||||
AlbumArtPatternParseError,
|
||||
UsersParseError,
|
||||
MountDirsParseError,
|
||||
DDNSParseError,
|
||||
ConflictingMounts,
|
||||
}
|
||||
|
||||
impl From<io::Error> for ConfigError {
|
||||
fn from(err: io::Error) -> ConfigError {
|
||||
ConfigError::IoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<regex::Error> for ConfigError {
|
||||
fn from(err: regex::Error) -> ConfigError {
|
||||
ConfigError::RegexError(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub secret: String,
|
||||
pub vfs: VfsConfig,
|
||||
|
@ -64,26 +36,23 @@ pub struct Config {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn parse(custom_path: Option<path::PathBuf>) -> Result<Config, ConfigError> {
|
||||
pub fn parse(custom_path: Option<path::PathBuf>) -> Result<Config> {
|
||||
|
||||
let config_path = match custom_path {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
let mut root = match utils::get_config_root() {
|
||||
Ok(r) => r,
|
||||
Err(_) => return Err(ConfigError::ConfigDirectoryError),
|
||||
};
|
||||
let mut root = utils::get_config_root()?;
|
||||
root.push(DEFAULT_CONFIG_FILE_NAME);
|
||||
root
|
||||
}
|
||||
};
|
||||
println!("Loading config from: {}", config_path.to_string_lossy());
|
||||
|
||||
let mut config_file = try!(fs::File::open(config_path));
|
||||
let mut config_file = fs::File::open(config_path)?;
|
||||
let mut config_file_content = String::new();
|
||||
try!(config_file.read_to_string(&mut config_file_content));
|
||||
config_file.read_to_string(&mut config_file_content)?;
|
||||
let parsed_config = toml::Parser::new(config_file_content.as_str()).parse();
|
||||
let parsed_config = try!(parsed_config.ok_or(ConfigError::TOMLParseError));
|
||||
let parsed_config = parsed_config.ok_or("Could not parse config as valid TOML")?;
|
||||
|
||||
let mut config = Config {
|
||||
secret: String::new(),
|
||||
|
@ -93,57 +62,55 @@ impl Config {
|
|||
ddns: None,
|
||||
};
|
||||
|
||||
try!(config.parse_secret(&parsed_config));
|
||||
try!(config.parse_index_sleep_duration(&parsed_config));
|
||||
try!(config.parse_mount_points(&parsed_config));
|
||||
try!(config.parse_users(&parsed_config));
|
||||
try!(config.parse_album_art_pattern(&parsed_config));
|
||||
try!(config.parse_ddns(&parsed_config));
|
||||
config.parse_secret(&parsed_config)?;
|
||||
config.parse_index_sleep_duration(&parsed_config)?;
|
||||
config.parse_mount_points(&parsed_config)?;
|
||||
config.parse_users(&parsed_config)?;
|
||||
config.parse_album_art_pattern(&parsed_config)?;
|
||||
config.parse_ddns(&parsed_config)?;
|
||||
|
||||
let mut index_path = match utils::get_cache_root() {
|
||||
Err(_) => return Err(ConfigError::CacheDirectoryError),
|
||||
Ok(p) => p,
|
||||
};
|
||||
let mut index_path = utils::get_cache_root()?;
|
||||
index_path.push(INDEX_FILE_NAME);
|
||||
config.index.path = index_path;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn parse_secret(&mut self, source: &toml::Table) -> Result<(), ConfigError> {
|
||||
let secret = try!(source.get(CONFIG_SECRET).ok_or(ConfigError::SecretParseError));
|
||||
let secret = try!(secret.as_str().ok_or(ConfigError::SecretParseError));
|
||||
self.secret = secret.to_owned();
|
||||
fn parse_secret(&mut self, source: &toml::Table) -> Result<()> {
|
||||
self.secret = source.get(CONFIG_SECRET)
|
||||
.and_then(|s| s.as_str())
|
||||
.map(|s| s.to_owned())
|
||||
.ok_or("Could not parse config secret")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_index_sleep_duration(&mut self, source: &toml::Table) -> Result<(), ConfigError> {
|
||||
fn parse_index_sleep_duration(&mut self, source: &toml::Table) -> Result<()> {
|
||||
let sleep_duration = match source.get(CONFIG_INDEX_SLEEP_DURATION) {
|
||||
Some(s) => s,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let sleep_duration = match sleep_duration {
|
||||
&toml::Value::Integer(s) => s as u64,
|
||||
_ => return Err(ConfigError::SleepDurationParseError),
|
||||
_ => bail!("Could not parse index sleep duration"),
|
||||
};
|
||||
self.index.sleep_duration = sleep_duration;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_album_art_pattern(&mut self, source: &toml::Table) -> Result<(), ConfigError> {
|
||||
fn parse_album_art_pattern(&mut self, source: &toml::Table) -> Result<()> {
|
||||
let pattern = match source.get(CONFIG_ALBUM_ART_PATTERN) {
|
||||
Some(s) => s,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let pattern = match pattern {
|
||||
&toml::Value::String(ref s) => s,
|
||||
_ => return Err(ConfigError::AlbumArtPatternParseError),
|
||||
_ => bail!("Could not parse album art pattern"),
|
||||
};
|
||||
self.index.album_art_pattern = Some(try!(regex::Regex::new(pattern)));
|
||||
self.index.album_art_pattern = Some(regex::Regex::new(pattern)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_users(&mut self, source: &toml::Table) -> Result<(), ConfigError> {
|
||||
fn parse_users(&mut self, source: &toml::Table) -> Result<()> {
|
||||
let users = match source.get(CONFIG_USERS) {
|
||||
Some(s) => s,
|
||||
None => return Ok(()),
|
||||
|
@ -151,28 +118,16 @@ impl Config {
|
|||
|
||||
let users = match users {
|
||||
&toml::Value::Array(ref a) => a,
|
||||
_ => return Err(ConfigError::UsersParseError),
|
||||
_ => bail!("Could not parse users array"),
|
||||
};
|
||||
|
||||
for user in users {
|
||||
let name = match user.lookup(CONFIG_USER_NAME) {
|
||||
None => return Err(ConfigError::UsersParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
let name = match name.as_str() {
|
||||
None => return Err(ConfigError::UsersParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
|
||||
let password = match user.lookup(CONFIG_USER_PASSWORD) {
|
||||
None => return Err(ConfigError::UsersParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
let password = match password.as_str() {
|
||||
None => return Err(ConfigError::UsersParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
|
||||
let name = user.lookup(CONFIG_USER_NAME)
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or("Could not parse username")?;
|
||||
let password = user.lookup(CONFIG_USER_PASSWORD)
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or("Could not parse user password")?;
|
||||
let user = User::new(name.to_owned(), password.to_owned());
|
||||
self.users.push(user);
|
||||
}
|
||||
|
@ -180,7 +135,7 @@ impl Config {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_mount_points(&mut self, source: &toml::Table) -> Result<(), ConfigError> {
|
||||
fn parse_mount_points(&mut self, source: &toml::Table) -> Result<()> {
|
||||
let mount_dirs = match source.get(CONFIG_MOUNT_DIRS) {
|
||||
Some(s) => s,
|
||||
None => return Ok(()),
|
||||
|
@ -188,31 +143,19 @@ impl Config {
|
|||
|
||||
let mount_dirs = match mount_dirs {
|
||||
&toml::Value::Array(ref a) => a,
|
||||
_ => return Err(ConfigError::MountDirsParseError),
|
||||
_ => bail!("Could not parse mount directories array"),
|
||||
};
|
||||
|
||||
for dir in mount_dirs {
|
||||
let name = match dir.lookup(CONFIG_MOUNT_DIR_NAME) {
|
||||
None => return Err(ConfigError::MountDirsParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
let name = match name.as_str() {
|
||||
None => return Err(ConfigError::MountDirsParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
|
||||
let source = match dir.lookup(CONFIG_MOUNT_DIR_SOURCE) {
|
||||
None => return Err(ConfigError::MountDirsParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
let source = match source.as_str() {
|
||||
None => return Err(ConfigError::MountDirsParseError),
|
||||
Some(n) => n,
|
||||
};
|
||||
let name = dir.lookup(CONFIG_MOUNT_DIR_NAME)
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or("Could not parse mount directory name")?;
|
||||
let source = dir.lookup(CONFIG_MOUNT_DIR_SOURCE)
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or("Could not parse mount directory source")?;
|
||||
let source = clean_path_string(source);
|
||||
|
||||
if self.vfs.mount_points.contains_key(name) {
|
||||
return Err(ConfigError::ConflictingMounts);
|
||||
bail!("Conflicting mount directories");
|
||||
}
|
||||
self.vfs.mount_points.insert(name.to_owned(), source);
|
||||
}
|
||||
|
@ -220,25 +163,24 @@ impl Config {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_ddns(&mut self, source: &toml::Table) -> Result<(), ConfigError> {
|
||||
fn parse_ddns(&mut self, source: &toml::Table) -> Result<()> {
|
||||
let ddns = match source.get(CONFIG_DDNS) {
|
||||
Some(s) => s,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let ddns = match ddns {
|
||||
&toml::Value::Table(ref a) => a,
|
||||
_ => return Err(ConfigError::DDNSParseError),
|
||||
_ => bail!("Could not parse DDNS settings table"),
|
||||
};
|
||||
|
||||
let host = try!(ddns.get(CONFIG_DDNS_HOST).ok_or(ConfigError::DDNSParseError)).as_str();
|
||||
let username = try!(ddns.get(CONFIG_DDNS_USERNAME).ok_or(ConfigError::DDNSParseError))
|
||||
.as_str();
|
||||
let password = try!(ddns.get(CONFIG_DDNS_PASSWORD).ok_or(ConfigError::DDNSParseError))
|
||||
.as_str();
|
||||
|
||||
let host = try!(host.ok_or(ConfigError::DDNSParseError));
|
||||
let username = try!(username.ok_or(ConfigError::DDNSParseError));
|
||||
let password = try!(password.ok_or(ConfigError::DDNSParseError));
|
||||
let host =
|
||||
ddns.get(CONFIG_DDNS_HOST).and_then(|n| n.as_str()).ok_or("Could not parse DDNS host")?;
|
||||
let username = ddns.get(CONFIG_DDNS_USERNAME)
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or("Could not parse DDNS username")?;
|
||||
let password = ddns.get(CONFIG_DDNS_PASSWORD)
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or("Could not parse DDNS password")?;
|
||||
|
||||
self.ddns = Some(DDNSConfig {
|
||||
host: host.to_owned(),
|
||||
|
|
|
@ -37,9 +37,9 @@ const DDNS_UPDATE_URL: &'static str = "http://ydns.io/api/v1/update/";
|
|||
|
||||
fn get_my_ip() -> Result<String, DDNSError> {
|
||||
let client = Client::new();
|
||||
let mut res = try!(client.get(MY_IP_API_URL).send());
|
||||
let mut res = client.get(MY_IP_API_URL).send()?;
|
||||
let mut buf = String::new();
|
||||
try!(res.read_to_string(&mut buf));
|
||||
res.read_to_string(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ fn update_my_ip(ip: &String, config: &DDNSConfig) -> Result<(), DDNSError> {
|
|||
password: Some(config.password.to_owned()),
|
||||
});
|
||||
|
||||
let res = try!(client.get(full_url.as_str()).header(auth_header).send());
|
||||
let res = client.get(full_url.as_str()).header(auth_header).send()?;
|
||||
match res.status {
|
||||
hyper::status::StatusCode::Ok => Ok(()),
|
||||
s => Err(DDNSError::UpdateError(s)),
|
||||
|
|
111
src/error.rs
111
src/error.rs
|
@ -1,111 +0,0 @@
|
|||
use ape;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use id3;
|
||||
use image;
|
||||
use lewton;
|
||||
use metaflac;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PError {
|
||||
CannotClearExistingIndex,
|
||||
PathDecoding,
|
||||
Io(io::Error),
|
||||
CacheDirectoryError,
|
||||
ConfigDirectoryError,
|
||||
PathNotInVfs,
|
||||
CannotServeDirectory,
|
||||
UnsupportedFileType,
|
||||
AlbumArtSearchError,
|
||||
ImageProcessingError,
|
||||
UnsupportedMetadataFormat,
|
||||
MetadataDecodingError,
|
||||
Unauthorized,
|
||||
IncorrectCredentials,
|
||||
}
|
||||
|
||||
impl From<ape::Error> for PError {
|
||||
fn from(_: ape::Error) -> PError {
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for PError {
|
||||
fn from(err: io::Error) -> PError {
|
||||
PError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<id3::Error> for PError {
|
||||
fn from(_: id3::Error) -> PError {
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<image::ImageError> for PError {
|
||||
fn from(_: image::ImageError) -> PError {
|
||||
PError::ImageProcessingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lewton::VorbisError> for PError {
|
||||
fn from(_: lewton::VorbisError) -> PError {
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<metaflac::Error> for PError {
|
||||
fn from(_: metaflac::Error) -> PError {
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for PError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
PError::Io(ref err) => err.description(),
|
||||
PError::CannotClearExistingIndex => "Error while removing existing index",
|
||||
PError::PathDecoding => "Error while decoding a Path as a UTF-8 string",
|
||||
PError::CacheDirectoryError => "Could not access the cache directory",
|
||||
PError::ConfigDirectoryError => "Could not access the config directory",
|
||||
PError::PathNotInVfs => "Requested path does not index a mount point",
|
||||
PError::CannotServeDirectory => "Only individual files can be served",
|
||||
PError::UnsupportedFileType => "Unrecognized extension",
|
||||
PError::AlbumArtSearchError => "Error while looking for album art",
|
||||
PError::ImageProcessingError => "Error while processing image",
|
||||
PError::UnsupportedMetadataFormat => "Unsupported metadata format",
|
||||
PError::MetadataDecodingError => "Error while reading song metadata",
|
||||
PError::Unauthorized => "Authentication required",
|
||||
PError::IncorrectCredentials => "Incorrect username/password combination",
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
PError::Io(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
PError::Io(ref err) => write!(f, "IO error: {}", err),
|
||||
PError::CannotClearExistingIndex => write!(f, "Error while removing existing index"),
|
||||
PError::PathDecoding => write!(f, "Path decoding error"),
|
||||
PError::CacheDirectoryError => write!(f, "Could not access the cache directory"),
|
||||
PError::ConfigDirectoryError => write!(f, "Could not access the config directory"),
|
||||
PError::PathNotInVfs => write!(f, "Requested path does not index a mount point"),
|
||||
PError::CannotServeDirectory => write!(f, "Only individual files can be served"),
|
||||
PError::UnsupportedFileType => write!(f, "Unrecognized extension"),
|
||||
PError::AlbumArtSearchError => write!(f, "Error while looking for album art"),
|
||||
PError::ImageProcessingError => write!(f, "Error while processing image"),
|
||||
PError::UnsupportedMetadataFormat => write!(f, "Unsupported metadata format"),
|
||||
PError::MetadataDecodingError => write!(f, "Error while reading song metadata"),
|
||||
PError::Unauthorized => write!(f, "Authentication required"),
|
||||
PError::IncorrectCredentials => write!(f, "Incorrect username/password combination"),
|
||||
}
|
||||
}
|
||||
}
|
50
src/errors.rs
Normal file
50
src/errors.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use ape;
|
||||
use core;
|
||||
use id3;
|
||||
use image;
|
||||
use hyper;
|
||||
use iron::IronError;
|
||||
use iron::status::Status;
|
||||
use lewton;
|
||||
use metaflac;
|
||||
use regex;
|
||||
use std;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Ape(ape::Error);
|
||||
Encoding(core::str::Utf8Error);
|
||||
Flac(metaflac::Error);
|
||||
Hyper(hyper::Error);
|
||||
Id3(id3::Error);
|
||||
Image(image::ImageError);
|
||||
Io(std::io::Error);
|
||||
Regex(regex::Error);
|
||||
Vorbis(lewton::VorbisError);
|
||||
}
|
||||
|
||||
errors {
|
||||
AuthenticationRequired {}
|
||||
MissingUsername {}
|
||||
MissingPassword {}
|
||||
IncorrectCredentials {}
|
||||
CannotServeDirectory {}
|
||||
UnsupportedFileType {}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for IronError {
|
||||
fn from(err: Error) -> IronError {
|
||||
match err {
|
||||
e @ Error(ErrorKind::AuthenticationRequired, _) => {
|
||||
IronError::new(e, Status::Unauthorized)
|
||||
}
|
||||
e @ Error(ErrorKind::MissingUsername, _) => IronError::new(e, Status::BadRequest),
|
||||
e @ Error(ErrorKind::MissingPassword, _) => IronError::new(e, Status::BadRequest),
|
||||
e @ Error(ErrorKind::IncorrectCredentials, _) => IronError::new(e, Status::BadRequest),
|
||||
e @ Error(ErrorKind::CannotServeDirectory, _) => IronError::new(e, Status::BadRequest),
|
||||
e @ Error(ErrorKind::UnsupportedFileType, _) => IronError::new(e, Status::BadRequest),
|
||||
e => IronError::new(e, Status::InternalServerError),
|
||||
}
|
||||
}
|
||||
}
|
14
src/index.rs
14
src/index.rs
|
@ -8,7 +8,7 @@ use std::sync::Arc;
|
|||
use std::thread;
|
||||
use std::time;
|
||||
|
||||
use error::*;
|
||||
use errors::*;
|
||||
use metadata;
|
||||
use vfs::Vfs;
|
||||
|
||||
|
@ -184,7 +184,7 @@ impl<'db> Drop for IndexBuilder<'db> {
|
|||
}
|
||||
|
||||
impl Index {
|
||||
pub fn new(vfs: Arc<Vfs>, config: &IndexConfig) -> Result<Index, PError> {
|
||||
pub fn new(vfs: Arc<Vfs>, config: &IndexConfig) -> Result<Index> {
|
||||
|
||||
let path = &config.path;
|
||||
|
||||
|
@ -536,7 +536,7 @@ impl Index {
|
|||
self.select_songs(&mut select).into_iter().map(|s| CollectionFile::Song(s)).collect()
|
||||
}
|
||||
|
||||
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>, PError> {
|
||||
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>> {
|
||||
|
||||
let mut output = Vec::new();
|
||||
|
||||
|
@ -553,9 +553,9 @@ impl Index {
|
|||
output.push(CollectionFile::Directory(directory));
|
||||
}
|
||||
|
||||
// Browse sub-directory
|
||||
// Browse sub-directory
|
||||
} else {
|
||||
let real_path = try!(self.vfs.virtual_to_real(virtual_path));
|
||||
let real_path = self.vfs.virtual_to_real(virtual_path)?;
|
||||
let directories = self.browse_directories(real_path.as_path());
|
||||
let songs = self.browse_songs(real_path.as_path());
|
||||
output.extend(directories);
|
||||
|
@ -565,9 +565,9 @@ impl Index {
|
|||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>, PError> {
|
||||
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>> {
|
||||
let db = self.connect();
|
||||
let real_path = try!(self.vfs.virtual_to_real(virtual_path));
|
||||
let real_path = self.vfs.virtual_to_real(virtual_path)?;
|
||||
let path_string = real_path.to_string_lossy().into_owned() + "%";
|
||||
let mut select =
|
||||
db.prepare("SELECT path, disc_number, track_number, title, year, album_artist, \
|
||||
|
|
27
src/main.rs
27
src/main.rs
|
@ -1,6 +1,10 @@
|
|||
#![recursion_limit = "128"]
|
||||
|
||||
extern crate ape;
|
||||
extern crate app_dirs;
|
||||
extern crate core;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate getopts;
|
||||
extern crate hyper;
|
||||
extern crate id3;
|
||||
|
@ -30,6 +34,7 @@ extern crate shell32;
|
|||
#[cfg(windows)]
|
||||
extern crate user32;
|
||||
|
||||
use errors::*;
|
||||
use getopts::Options;
|
||||
use iron::prelude::*;
|
||||
use mount::Mount;
|
||||
|
@ -41,7 +46,7 @@ mod api;
|
|||
mod collection;
|
||||
mod config;
|
||||
mod ddns;
|
||||
mod error;
|
||||
mod errors;
|
||||
mod index;
|
||||
mod metadata;
|
||||
mod ui;
|
||||
|
@ -50,6 +55,20 @@ mod thumbnails;
|
|||
mod vfs;
|
||||
|
||||
fn main() {
|
||||
if let Err(ref e) = run() {
|
||||
println!("Error: {}", e);
|
||||
|
||||
for e in e.iter().skip(1) {
|
||||
println!("caused by: {}", e);
|
||||
}
|
||||
if let Some(backtrace) = e.backtrace() {
|
||||
println!("backtrace: {:?}", backtrace);
|
||||
}
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
|
||||
// Parse CLI options
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
@ -63,7 +82,7 @@ fn main() {
|
|||
let config_file_path = config_file_name.map(|n| Path::new(n.as_str()).to_path_buf());
|
||||
|
||||
// Parse config
|
||||
let config = config::Config::parse(config_file_path).unwrap();
|
||||
let config = config::Config::parse(config_file_path)?;
|
||||
|
||||
// Init VFS
|
||||
let vfs = Arc::new(vfs::Vfs::new(config.vfs.clone()));
|
||||
|
@ -95,7 +114,7 @@ fn main() {
|
|||
let mut mount = Mount::new();
|
||||
mount.mount("/api/", api_chain);
|
||||
mount.mount("/", Static::new(Path::new("web")));
|
||||
let mut server = Iron::new(mount).http(("0.0.0.0", 5050)).unwrap();
|
||||
let mut server = Iron::new(mount).http(("0.0.0.0", 5050))?;
|
||||
|
||||
// Start DDNS updates
|
||||
match config.ddns {
|
||||
|
@ -113,4 +132,6 @@ fn main() {
|
|||
|
||||
println!("Shutting down server");
|
||||
server.close().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use regex::Regex;
|
|||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use error::PError;
|
||||
use errors::*;
|
||||
use utils;
|
||||
use utils::AudioFormat;
|
||||
|
||||
|
@ -22,18 +22,18 @@ pub struct SongTags {
|
|||
pub year: Option<i32>,
|
||||
}
|
||||
|
||||
pub fn read(path: &Path) -> Result<SongTags, PError> {
|
||||
pub fn read(path: &Path) -> Result<SongTags> {
|
||||
match utils::get_audio_format(path) {
|
||||
Some(AudioFormat::FLAC) => read_flac(path),
|
||||
Some(AudioFormat::MP3) => read_id3(path),
|
||||
Some(AudioFormat::MPC) => read_ape(path),
|
||||
Some(AudioFormat::OGG) => read_vorbis(path),
|
||||
_ => Err(PError::UnsupportedMetadataFormat),
|
||||
_ => bail!("Unsupported file format for reading metadata"),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_id3(path: &Path) -> Result<SongTags, PError> {
|
||||
let tag = try!(id3::Tag::read_from_path(path));
|
||||
fn read_id3(path: &Path) -> Result<SongTags> {
|
||||
let tag = id3::Tag::read_from_path(path)?;
|
||||
|
||||
let artist = tag.artist().map(|s| s.to_string());
|
||||
let album_artist = tag.album_artist().map(|s| s.to_string());
|
||||
|
@ -85,8 +85,8 @@ fn read_ape_x_of_y(item: &ape::Item) -> Option<u32> {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_ape(path: &Path) -> Result<SongTags, PError> {
|
||||
let tag = try!(ape::read(path));
|
||||
fn read_ape(path: &Path) -> Result<SongTags> {
|
||||
let tag = ape::read(path)?;
|
||||
let artist = tag.item("Artist").and_then(read_ape_string);
|
||||
let album = tag.item("Album").and_then(read_ape_string);
|
||||
let album_artist = tag.item("Album artist").and_then(read_ape_string);
|
||||
|
@ -105,10 +105,10 @@ fn read_ape(path: &Path) -> Result<SongTags, PError> {
|
|||
})
|
||||
}
|
||||
|
||||
fn read_vorbis(path: &Path) -> Result<SongTags, PError> {
|
||||
fn read_vorbis(path: &Path) -> Result<SongTags> {
|
||||
|
||||
let file = try!(fs::File::open(path));
|
||||
let source = try!(OggStreamReader::new(PacketReader::new(file)));
|
||||
let file = fs::File::open(path)?;
|
||||
let source = OggStreamReader::new(PacketReader::new(file))?;
|
||||
|
||||
let mut tags = SongTags {
|
||||
artist: None,
|
||||
|
@ -136,9 +136,9 @@ fn read_vorbis(path: &Path) -> Result<SongTags, PError> {
|
|||
Ok(tags)
|
||||
}
|
||||
|
||||
fn read_flac(path: &Path) -> Result<SongTags, PError> {
|
||||
let tag = try!(metaflac::Tag::read_from_path(path));
|
||||
let vorbis = try!(tag.vorbis_comments().ok_or(PError::MetadataDecodingError));
|
||||
fn read_flac(path: &Path) -> Result<SongTags> {
|
||||
let tag = metaflac::Tag::read_from_path(path)?;
|
||||
let vorbis = tag.vorbis_comments().ok_or("Missing Vorbis comments")?;
|
||||
let disc_number = vorbis.get("DISCNUMBER").and_then(|d| d[0].parse::<u32>().ok());
|
||||
let year = vorbis.get("DATE").and_then(|d| d[0].parse::<i32>().ok());
|
||||
Ok(SongTags {
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::fs::DirBuilder;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::path::*;
|
||||
|
||||
use error::*;
|
||||
use errors::*;
|
||||
use utils;
|
||||
|
||||
const THUMBNAILS_PATH: &'static str = "thumbnails";
|
||||
|
@ -22,16 +22,16 @@ fn hash(path: &Path, dimension: u32) -> u64 {
|
|||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn get_thumbnail(real_path: &Path, max_dimension: u32) -> Result<PathBuf, PError> {
|
||||
pub fn get_thumbnail(real_path: &Path, max_dimension: u32) -> Result<PathBuf> {
|
||||
|
||||
let mut out_path = try!(utils::get_cache_root());
|
||||
let mut out_path = utils::get_cache_root()?;
|
||||
out_path.push(THUMBNAILS_PATH);
|
||||
|
||||
let mut dir_builder = DirBuilder::new();
|
||||
dir_builder.recursive(true);
|
||||
try!(dir_builder.create(out_path.as_path()));
|
||||
dir_builder.create(out_path.as_path())?;
|
||||
|
||||
let source_image = try!(image::open(real_path));
|
||||
let source_image = image::open(real_path)?;
|
||||
let (source_width, source_height) = source_image.dimensions();
|
||||
let cropped_dimension = cmp::max(source_width, source_height);
|
||||
let out_dimension = cmp::min(max_dimension, cropped_dimension);
|
||||
|
@ -50,13 +50,13 @@ pub fn get_thumbnail(real_path: &Path, max_dimension: u32) -> Result<PathBuf, PE
|
|||
out_dimension,
|
||||
out_dimension,
|
||||
FilterType::Lanczos3);
|
||||
try!(out_image.save(out_path.as_path()));
|
||||
out_image.save(out_path.as_path())?;
|
||||
} else {
|
||||
let out_image = resize(&source_image,
|
||||
out_dimension,
|
||||
out_dimension,
|
||||
FilterType::Lanczos3);
|
||||
try!(out_image.save(out_path.as_path()));
|
||||
out_image.save(out_path.as_path())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
22
src/utils.rs
22
src/utils.rs
|
@ -2,28 +2,24 @@ use app_dirs::{AppDataType, data_root};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::fs;
|
||||
|
||||
use error::PError;
|
||||
use errors::*;
|
||||
|
||||
pub fn get_config_root() -> Result<PathBuf, PError> {
|
||||
pub fn get_config_root() -> Result<PathBuf> {
|
||||
if let Ok(mut root) = data_root(AppDataType::SharedConfig) {
|
||||
root.push("Polaris");
|
||||
return match fs::create_dir_all(&root) {
|
||||
Ok(()) => Ok(root),
|
||||
Err(_) => Err(PError::CacheDirectoryError),
|
||||
};
|
||||
fs::create_dir_all(&root)?;
|
||||
return Ok(root);
|
||||
}
|
||||
Err(PError::ConfigDirectoryError)
|
||||
bail!("Could not retrieve config directory root");
|
||||
}
|
||||
|
||||
pub fn get_cache_root() -> Result<PathBuf, PError> {
|
||||
pub fn get_cache_root() -> Result<PathBuf> {
|
||||
if let Ok(mut root) = data_root(AppDataType::SharedData) {
|
||||
root.push("Polaris");
|
||||
return match fs::create_dir_all(&root) {
|
||||
Ok(()) => Ok(root),
|
||||
Err(_) => Err(PError::CacheDirectoryError),
|
||||
};
|
||||
fs::create_dir_all(&root)?;
|
||||
return Ok(root);
|
||||
}
|
||||
Err(PError::CacheDirectoryError)
|
||||
bail!("Could not retrieve cache directory root");
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
|
10
src/vfs.rs
10
src/vfs.rs
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
|
||||
use error::*;
|
||||
use errors::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VfsConfig {
|
||||
|
@ -24,7 +24,7 @@ impl Vfs {
|
|||
Vfs { mount_points: config.mount_points }
|
||||
}
|
||||
|
||||
pub fn real_to_virtual(&self, real_path: &Path) -> Result<PathBuf, PError> {
|
||||
pub fn real_to_virtual(&self, real_path: &Path) -> Result<PathBuf> {
|
||||
for (name, target) in &self.mount_points {
|
||||
match real_path.strip_prefix(target) {
|
||||
Ok(p) => {
|
||||
|
@ -34,10 +34,10 @@ impl Vfs {
|
|||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
Err(PError::PathNotInVfs)
|
||||
bail!("Real path has no match in VFS")
|
||||
}
|
||||
|
||||
pub fn virtual_to_real(&self, virtual_path: &Path) -> Result<PathBuf, PError> {
|
||||
pub fn virtual_to_real(&self, virtual_path: &Path) -> Result<PathBuf> {
|
||||
for (name, target) in &self.mount_points {
|
||||
let mount_path = Path::new(&name);
|
||||
match virtual_path.strip_prefix(mount_path) {
|
||||
|
@ -51,7 +51,7 @@ impl Vfs {
|
|||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
Err(PError::PathNotInVfs)
|
||||
bail!("Virtual path has no match in VFS")
|
||||
}
|
||||
|
||||
pub fn get_mount_points(&self) -> &HashMap<String, PathBuf> {
|
||||
|
|
Loading…
Add table
Reference in a new issue