2
0
Fork 0
mirror of https://github.com/agersant/polaris synced 2025-02-23 00:58:28 +00:00

Formatting

This commit is contained in:
Antoine Gersant 2016-12-21 07:05:16 +01:00
parent 6044fbb029
commit f9a27895b4
14 changed files with 1457 additions and 1454 deletions

2
.rustfmt.toml Normal file
View file

@ -0,0 +1,2 @@
write_mode = "Overwrite"
hard_tabs = true

View file

@ -23,197 +23,197 @@ const CURRENT_MINOR_VERSION: i32 = 1;
#[derive(RustcEncodable)]
struct Version {
major: i32,
minor: i32,
major: i32,
minor: i32,
}
impl Version {
fn new(major: i32, minor: i32) -> Version {
Version {
major: major,
minor: minor,
}
}
fn new(major: i32, minor: i32) -> Version {
Version {
major: major,
minor: minor,
}
}
}
pub fn get_api_handler(collection: Arc<Collection>) -> Mount {
let mut api_handler = Mount::new();
let mut api_handler = Mount::new();
{
let collection = collection.clone();
api_handler.mount("/version/", self::version);
api_handler.mount("/auth/",
move |request: &mut Request| self::auth(request, collection.deref()));
}
{
let collection = collection.clone();
api_handler.mount("/version/", self::version);
api_handler.mount("/auth/",
move |request: &mut Request| self::auth(request, collection.deref()));
}
{
let mut auth_api_mount = Mount::new();
{
let collection = collection.clone();
auth_api_mount.mount("/browse/", move |request: &mut Request| {
self::browse(request, collection.deref())
});
}
{
let collection = collection.clone();
auth_api_mount.mount("/flatten/", move |request: &mut Request| {
self::flatten(request, collection.deref())
});
}
{
let collection = collection.clone();
auth_api_mount.mount("/random/", move |request: &mut Request| {
self::random(request, collection.deref())
});
}
{
let collection = collection.clone();
auth_api_mount.mount("/serve/", move |request: &mut Request| {
self::serve(request, collection.deref())
});
}
{
let mut auth_api_mount = Mount::new();
{
let collection = collection.clone();
auth_api_mount.mount("/browse/", move |request: &mut Request| {
self::browse(request, collection.deref())
});
}
{
let collection = collection.clone();
auth_api_mount.mount("/flatten/", move |request: &mut Request| {
self::flatten(request, collection.deref())
});
}
{
let collection = collection.clone();
auth_api_mount.mount("/random/", move |request: &mut Request| {
self::random(request, collection.deref())
});
}
{
let collection = collection.clone();
auth_api_mount.mount("/serve/", move |request: &mut Request| {
self::serve(request, collection.deref())
});
}
let mut auth_api_chain = Chain::new(auth_api_mount);
auth_api_chain.link_before(AuthRequirement);
let mut auth_api_chain = Chain::new(auth_api_mount);
auth_api_chain.link_before(AuthRequirement);
api_handler.mount("/", auth_api_chain);
}
api_handler
api_handler.mount("/", auth_api_chain);
}
api_handler
}
fn path_from_request(request: &Request) -> Result<PathBuf> {
let path_string = request.url.path().join("\\");
let decoded_path = percent_decode(path_string.as_bytes()).decode_utf8()?;
Ok(PathBuf::from(decoded_path.deref()))
let path_string = request.url.path().join("\\");
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<()> {
match req.get_cookie("username") {
Some(_) => Ok(()),
None => Err(Error::from(ErrorKind::AuthenticationRequired).into()),
}
}
fn before(&self, req: &mut Request) -> IronResult<()> {
match req.get_cookie("username") {
Some(_) => Ok(()),
None => Err(Error::from(ErrorKind::AuthenticationRequired).into()),
}
}
}
fn version(_: &mut Request) -> IronResult<Response> {
let current_version = Version::new(CURRENT_MAJOR_VERSION, CURRENT_MINOR_VERSION);
match json::encode(&current_version) {
Ok(result_json) => Ok(Response::with((status::Ok, result_json))),
Err(e) => Err(IronError::new(e, status::InternalServerError)),
}
let current_version = Version::new(CURRENT_MAJOR_VERSION, CURRENT_MINOR_VERSION);
match json::encode(&current_version) {
Ok(result_json) => Ok(Response::with((status::Ok, result_json))),
Err(e) => Err(IronError::new(e, status::InternalServerError)),
}
}
fn auth(request: &mut Request, collection: &Collection) -> IronResult<Response> {
let input = request.get_ref::<params::Params>().unwrap();
let username = match input.find(&["username"]) {
Some(&params::Value::String(ref username)) => username,
_ => return Err(Error::from(ErrorKind::MissingUsername).into()),
};
let password = match input.find(&["password"]) {
Some(&params::Value::String(ref password)) => password,
_ => return Err(Error::from(ErrorKind::MissingPassword).into()),
};
if collection.auth(username.as_str(), password.as_str()) {
let mut response = Response::with((status::Ok, ""));
let mut username_cookie = CookiePair::new("username".to_string(), username.clone());
username_cookie.path = Some("/".to_owned());
response.set_cookie(username_cookie);
Ok(response)
} else {
Err(Error::from(ErrorKind::IncorrectCredentials).into())
}
let input = request.get_ref::<params::Params>().unwrap();
let username = match input.find(&["username"]) {
Some(&params::Value::String(ref username)) => username,
_ => return Err(Error::from(ErrorKind::MissingUsername).into()),
};
let password = match input.find(&["password"]) {
Some(&params::Value::String(ref password)) => password,
_ => return Err(Error::from(ErrorKind::MissingPassword).into()),
};
if collection.auth(username.as_str(), password.as_str()) {
let mut response = Response::with((status::Ok, ""));
let mut username_cookie = CookiePair::new("username".to_string(), username.clone());
username_cookie.path = Some("/".to_owned());
response.set_cookie(username_cookie);
Ok(response)
} else {
Err(Error::from(ErrorKind::IncorrectCredentials).into())
}
}
fn browse(request: &mut Request, collection: &Collection) -> IronResult<Response> {
let path = path_from_request(request);
let path = match path {
Err(e) => return Err(IronError::new(e, status::BadRequest)),
Ok(p) => p,
};
let browse_result = collection.browse(&path)?;
let path = path_from_request(request);
let path = match path {
Err(e) => return Err(IronError::new(e, status::BadRequest)),
Ok(p) => p,
};
let browse_result = collection.browse(&path)?;
let result_json = json::encode(&browse_result);
let result_json = match result_json {
Ok(j) => j,
Err(e) => return Err(IronError::new(e, status::InternalServerError)),
};
let result_json = json::encode(&browse_result);
let result_json = match result_json {
Ok(j) => j,
Err(e) => return Err(IronError::new(e, status::InternalServerError)),
};
Ok(Response::with((status::Ok, result_json)))
Ok(Response::with((status::Ok, result_json)))
}
fn flatten(request: &mut Request, collection: &Collection) -> IronResult<Response> {
let path = path_from_request(request);
let path = match path {
Err(e) => return Err(IronError::new(e, status::BadRequest)),
Ok(p) => p,
};
let flatten_result = collection.flatten(&path)?;
let path = path_from_request(request);
let path = match path {
Err(e) => return Err(IronError::new(e, status::BadRequest)),
Ok(p) => p,
};
let flatten_result = collection.flatten(&path)?;
let result_json = json::encode(&flatten_result);
let result_json = match result_json {
Ok(j) => j,
Err(e) => return Err(IronError::new(e, status::InternalServerError)),
};
let result_json = json::encode(&flatten_result);
let result_json = match result_json {
Ok(j) => j,
Err(e) => return Err(IronError::new(e, status::InternalServerError)),
};
Ok(Response::with((status::Ok, result_json)))
Ok(Response::with((status::Ok, result_json)))
}
fn random(_: &mut Request, collection: &Collection) -> IronResult<Response> {
let random_result = collection.get_random_albums(20)?;
let result_json = json::encode(&random_result);
let result_json = match result_json {
Ok(j) => j,
Err(e) => return Err(IronError::new(e, status::InternalServerError)),
};
Ok(Response::with((status::Ok, result_json)))
let random_result = collection.get_random_albums(20)?;
let result_json = json::encode(&random_result);
let result_json = match result_json {
Ok(j) => j,
Err(e) => return Err(IronError::new(e, status::InternalServerError)),
};
Ok(Response::with((status::Ok, result_json)))
}
fn serve(request: &mut Request, collection: &Collection) -> IronResult<Response> {
let virtual_path = path_from_request(request);
let virtual_path = match virtual_path {
Err(e) => return Err(IronError::new(e, status::BadRequest)),
Ok(p) => p,
};
let virtual_path = path_from_request(request);
let virtual_path = match virtual_path {
Err(e) => return Err(IronError::new(e, status::BadRequest)),
Ok(p) => p,
};
let real_path = collection.locate(virtual_path.as_path());
let real_path = match real_path {
Err(e) => return Err(IronError::new(e, status::NotFound)),
Ok(p) => p,
};
let real_path = collection.locate(virtual_path.as_path());
let real_path = match real_path {
Err(e) => return Err(IronError::new(e, status::NotFound)),
Ok(p) => p,
};
let metadata = match fs::metadata(real_path.as_path()) {
Ok(meta) => meta,
Err(e) => {
let status = match e.kind() {
io::ErrorKind::NotFound => status::NotFound,
io::ErrorKind::PermissionDenied => status::Forbidden,
_ => status::InternalServerError,
};
return Err(IronError::new(e, status));
}
};
let metadata = match fs::metadata(real_path.as_path()) {
Ok(meta) => meta,
Err(e) => {
let status = match e.kind() {
io::ErrorKind::NotFound => status::NotFound,
io::ErrorKind::PermissionDenied => status::Forbidden,
_ => status::InternalServerError,
};
return Err(IronError::new(e, status));
}
};
if !metadata.is_file() {
return Err(Error::from(ErrorKind::CannotServeDirectory).into());
}
if !metadata.is_file() {
return Err(Error::from(ErrorKind::CannotServeDirectory).into());
}
if is_song(real_path.as_path()) {
return Ok(Response::with((status::Ok, real_path)));
}
if is_song(real_path.as_path()) {
return Ok(Response::with((status::Ok, real_path)));
}
if is_image(real_path.as_path()) {
return art(request, real_path.as_path());
}
if is_image(real_path.as_path()) {
return art(request, real_path.as_path());
}
Err(Error::from(ErrorKind::UnsupportedFileType).into())
Err(Error::from(ErrorKind::UnsupportedFileType).into())
}
fn art(_: &mut Request, real_path: &Path) -> IronResult<Response> {
let thumb = get_thumbnail(real_path, 400);
match thumb {
Ok(path) => Ok(Response::with((status::Ok, path))),
Err(e) => Err(IronError::from(e)),
}
let thumb = get_thumbnail(real_path, 400);
match thumb {
Ok(path) => Ok(Response::with((status::Ok, path))),
Err(e) => Err(IronError::from(e)),
}
}

View file

@ -11,56 +11,56 @@ use vfs::*;
#[derive(Clone, Debug)]
pub struct User {
name: String,
password: String,
name: String,
password: String,
}
impl User {
pub fn new(name: String, password: String) -> User {
User {
name: name,
password: password,
}
}
pub fn new(name: String, password: String) -> User {
User {
name: name,
password: password,
}
}
}
pub struct Collection {
vfs: Arc<Vfs>,
users: Vec<User>,
index: Arc<Index>,
vfs: Arc<Vfs>,
users: Vec<User>,
index: Arc<Index>,
}
impl Collection {
pub fn new(vfs: Arc<Vfs>, index: Arc<Index>) -> Collection {
Collection {
vfs: vfs,
users: Vec::new(),
index: index,
}
}
pub fn new(vfs: Arc<Vfs>, index: Arc<Index>) -> Collection {
Collection {
vfs: vfs,
users: Vec::new(),
index: index,
}
}
pub fn load_config(&mut self, config: &Config) -> Result<()> {
self.users = config.users.to_vec();
Ok(())
}
pub fn load_config(&mut self, config: &Config) -> Result<()> {
self.users = config.users.to_vec();
Ok(())
}
pub fn auth(&self, username: &str, password: &str) -> bool {
self.users.iter().any(|u| u.name == username && u.password == password)
}
pub fn auth(&self, username: &str, password: &str) -> bool {
self.users.iter().any(|u| u.name == username && u.password == password)
}
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>> {
self.index.deref().browse(virtual_path)
}
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>> {
self.index.deref().flatten(virtual_path)
}
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>> {
self.index.deref().flatten(virtual_path)
}
pub fn get_random_albums(&self, count: u32) -> Result<Vec<Directory>> {
self.index.deref().get_random_albums(count)
}
pub fn get_random_albums(&self, count: u32) -> Result<Vec<Directory>> {
self.index.deref().get_random_albums(count)
}
pub fn locate(&self, virtual_path: &Path) -> Result<PathBuf> {
self.vfs.virtual_to_real(virtual_path)
}
pub fn locate(&self, virtual_path: &Path) -> Result<PathBuf> {
self.vfs.virtual_to_real(virtual_path)
}
}

View file

@ -28,195 +28,195 @@ const CONFIG_DDNS_USERNAME: &'static str = "username";
const CONFIG_DDNS_PASSWORD: &'static str = "password";
pub struct Config {
pub secret: String,
pub vfs: VfsConfig,
pub users: Vec<User>,
pub index: IndexConfig,
pub ddns: Option<DDNSConfig>,
pub secret: String,
pub vfs: VfsConfig,
pub users: Vec<User>,
pub index: IndexConfig,
pub ddns: Option<DDNSConfig>,
}
impl Config {
pub fn parse(custom_path: Option<path::PathBuf>) -> Result<Config> {
pub fn parse(custom_path: Option<path::PathBuf>) -> Result<Config> {
let config_path = match custom_path {
Some(p) => p,
None => {
let mut root = utils::get_config_root()?;
root.push(DEFAULT_CONFIG_FILE_NAME);
root
}
};
println!("Config file path: {}", config_path.to_string_lossy());
let config_path = match custom_path {
Some(p) => p,
None => {
let mut root = utils::get_config_root()?;
root.push(DEFAULT_CONFIG_FILE_NAME);
root
}
};
println!("Config file path: {}", config_path.to_string_lossy());
let mut config_file = fs::File::open(config_path)?;
let mut config_file_content = String::new();
config_file.read_to_string(&mut config_file_content)?;
let parsed_config = toml::Parser::new(config_file_content.as_str()).parse();
let parsed_config = parsed_config.ok_or("Could not parse config as valid TOML")?;
let mut config_file = fs::File::open(config_path)?;
let mut config_file_content = String::new();
config_file.read_to_string(&mut config_file_content)?;
let parsed_config = toml::Parser::new(config_file_content.as_str()).parse();
let parsed_config = parsed_config.ok_or("Could not parse config as valid TOML")?;
let mut config = Config {
secret: String::new(),
vfs: VfsConfig::new(),
users: Vec::new(),
index: IndexConfig::new(),
ddns: None,
};
let mut config = Config {
secret: String::new(),
vfs: VfsConfig::new(),
users: Vec::new(),
index: IndexConfig::new(),
ddns: None,
};
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)?;
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 = utils::get_cache_root()?;
index_path.push(INDEX_FILE_NAME);
config.index.path = index_path;
let mut index_path = utils::get_cache_root()?;
index_path.push(INDEX_FILE_NAME);
config.index.path = index_path;
Ok(config)
}
Ok(config)
}
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_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<()> {
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,
_ => bail!("Could not parse index sleep duration"),
};
self.index.sleep_duration = sleep_duration;
Ok(())
}
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,
_ => 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<()> {
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,
_ => bail!("Could not parse album art pattern"),
};
self.index.album_art_pattern = Some(regex::Regex::new(pattern)?);
Ok(())
}
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,
_ => bail!("Could not parse album art pattern"),
};
self.index.album_art_pattern = Some(regex::Regex::new(pattern)?);
Ok(())
}
fn parse_users(&mut self, source: &toml::Table) -> Result<()> {
let users = match source.get(CONFIG_USERS) {
Some(s) => s,
None => return Ok(()),
};
fn parse_users(&mut self, source: &toml::Table) -> Result<()> {
let users = match source.get(CONFIG_USERS) {
Some(s) => s,
None => return Ok(()),
};
let users = match users {
&toml::Value::Array(ref a) => a,
_ => bail!("Could not parse users array"),
};
let users = match users {
&toml::Value::Array(ref a) => a,
_ => bail!("Could not parse users array"),
};
for user in users {
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);
}
for user in users {
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);
}
Ok(())
}
Ok(())
}
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(()),
};
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(()),
};
let mount_dirs = match mount_dirs {
&toml::Value::Array(ref a) => a,
_ => bail!("Could not parse mount directories array"),
};
let mount_dirs = match mount_dirs {
&toml::Value::Array(ref a) => a,
_ => bail!("Could not parse mount directories array"),
};
for dir in mount_dirs {
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) {
bail!("Conflicting mount directories");
}
self.vfs.mount_points.insert(name.to_owned(), source);
}
for dir in mount_dirs {
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) {
bail!("Conflicting mount directories");
}
self.vfs.mount_points.insert(name.to_owned(), source);
}
Ok(())
}
Ok(())
}
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,
_ => bail!("Could not parse DDNS settings table"),
};
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,
_ => bail!("Could not parse DDNS settings table"),
};
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")?;
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(),
username: username.to_owned(),
password: password.to_owned(),
});
Ok(())
}
self.ddns = Some(DDNSConfig {
host: host.to_owned(),
username: username.to_owned(),
password: password.to_owned(),
});
Ok(())
}
}
fn clean_path_string(path_string: &str) -> path::PathBuf {
let separator_regex = regex::Regex::new(r"\\|/").unwrap();
let mut correct_separator = String::new();
correct_separator.push(path::MAIN_SEPARATOR);
let path_string = separator_regex.replace_all(path_string, correct_separator.as_str());
path::PathBuf::from(path_string)
let separator_regex = regex::Regex::new(r"\\|/").unwrap();
let mut correct_separator = String::new();
correct_separator.push(path::MAIN_SEPARATOR);
let path_string = separator_regex.replace_all(path_string, correct_separator.as_str());
path::PathBuf::from(path_string)
}
#[test]
fn test_clean_path_string() {
let mut correct_path = path::PathBuf::new();
if cfg!(target_os = "windows") {
correct_path.push("C:\\");
} else {
correct_path.push("/usr");
}
correct_path.push("some");
correct_path.push("path");
if cfg!(target_os = "windows") {
assert_eq!(correct_path, clean_path_string(r#"C:/some/path"#));
assert_eq!(correct_path, clean_path_string(r#"C:\some\path"#));
assert_eq!(correct_path, clean_path_string(r#"C:\some\path\"#));
} else {
assert_eq!(correct_path, clean_path_string(r#"/usr/some/path"#));
assert_eq!(correct_path, clean_path_string(r#"/usr\some\path"#));
assert_eq!(correct_path, clean_path_string(r#"/usr\some\path\"#));
}
let mut correct_path = path::PathBuf::new();
if cfg!(target_os = "windows") {
correct_path.push("C:\\");
} else {
correct_path.push("/usr");
}
correct_path.push("some");
correct_path.push("path");
if cfg!(target_os = "windows") {
assert_eq!(correct_path, clean_path_string(r#"C:/some/path"#));
assert_eq!(correct_path, clean_path_string(r#"C:\some\path"#));
assert_eq!(correct_path, clean_path_string(r#"C:\some\path\"#));
} else {
assert_eq!(correct_path, clean_path_string(r#"/usr/some/path"#));
assert_eq!(correct_path, clean_path_string(r#"/usr\some\path"#));
assert_eq!(correct_path, clean_path_string(r#"/usr\some\path\"#));
}
}

View file

@ -8,69 +8,69 @@ use std::time;
#[derive(Debug, Clone)]
pub struct DDNSConfig {
pub host: String,
pub username: String,
pub password: String,
pub host: String,
pub username: String,
pub password: String,
}
#[derive(Debug)]
enum DDNSError {
IoError(io::Error),
HyperError(hyper::Error),
UpdateError(hyper::status::StatusCode),
IoError(io::Error),
HyperError(hyper::Error),
UpdateError(hyper::status::StatusCode),
}
impl From<io::Error> for DDNSError {
fn from(err: io::Error) -> DDNSError {
DDNSError::IoError(err)
}
fn from(err: io::Error) -> DDNSError {
DDNSError::IoError(err)
}
}
impl From<hyper::Error> for DDNSError {
fn from(err: hyper::Error) -> DDNSError {
DDNSError::HyperError(err)
}
fn from(err: hyper::Error) -> DDNSError {
DDNSError::HyperError(err)
}
}
const MY_IP_API_URL: &'static str = "http://api.ipify.org";
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 = client.get(MY_IP_API_URL).send()?;
let mut buf = String::new();
res.read_to_string(&mut buf)?;
Ok(buf)
let client = Client::new();
let mut res = client.get(MY_IP_API_URL).send()?;
let mut buf = String::new();
res.read_to_string(&mut buf)?;
Ok(buf)
}
fn update_my_ip(ip: &String, config: &DDNSConfig) -> Result<(), DDNSError> {
let client = Client::new();
let url = DDNS_UPDATE_URL;
let host = &config.host;
let full_url = format!("{}?host={}&ip={}", url, host, ip);
let auth_header = Authorization(Basic {
username: config.username.clone(),
password: Some(config.password.to_owned()),
});
let client = Client::new();
let url = DDNS_UPDATE_URL;
let host = &config.host;
let full_url = format!("{}?host={}&ip={}", url, host, ip);
let auth_header = Authorization(Basic {
username: config.username.clone(),
password: Some(config.password.to_owned()),
});
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)),
}
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)),
}
}
pub fn run(config: DDNSConfig) {
loop {
let my_ip_res = get_my_ip();
if let Ok(my_ip) = my_ip_res {
match update_my_ip(&my_ip, &config) {
Err(e) => println!("Dynamic DNS Error: {:?}", e),
Ok(_) => (),
};
} else {
println!("Dynamic DNS Error: could not retrieve our own IP address");
}
thread::sleep(time::Duration::from_secs(60 * 30));
}
loop {
let my_ip_res = get_my_ip();
if let Ok(my_ip) = my_ip_res {
match update_my_ip(&my_ip, &config) {
Err(e) => println!("Dynamic DNS Error: {:?}", e),
Ok(_) => (),
};
} else {
println!("Dynamic DNS Error: could not retrieve our own IP address");
}
thread::sleep(time::Duration::from_secs(60 * 30));
}
}

View file

@ -36,17 +36,17 @@ error_chain! {
}
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),
}
}
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),
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -55,83 +55,83 @@ mod thumbnails;
mod vfs;
fn main() {
if let Err(ref e) = run() {
println!("Error: {}", e);
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);
}
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();
let mut options = Options::new();
options.optopt("c", "config", "set the configuration file", "FILE");
let matches = match options.parse(&args[1..]) {
Ok(m) => m,
Err(f) => panic!(f.to_string()),
};
let config_file_name = matches.opt_str("c");
let config_file_path = config_file_name.map(|n| Path::new(n.as_str()).to_path_buf());
// Parse CLI options
let args: Vec<String> = std::env::args().collect();
let mut options = Options::new();
options.optopt("c", "config", "set the configuration file", "FILE");
let matches = match options.parse(&args[1..]) {
Ok(m) => m,
Err(f) => panic!(f.to_string()),
};
let config_file_name = matches.opt_str("c");
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)?;
// Parse config
let config = config::Config::parse(config_file_path)?;
// Init VFS
let vfs = Arc::new(vfs::Vfs::new(config.vfs.clone()));
// Init VFS
let vfs = Arc::new(vfs::Vfs::new(config.vfs.clone()));
// Init index
println!("Starting up index");
let index = Arc::new(index::Index::new(vfs.clone(), &config.index)?);
let index_ref = index.clone();
std::thread::spawn(move || index_ref.run());
// Init index
println!("Starting up index");
let index = Arc::new(index::Index::new(vfs.clone(), &config.index)?);
let index_ref = index.clone();
std::thread::spawn(move || index_ref.run());
// Start server
println!("Starting up server");
let mut api_chain;
{
let api_handler;
{
let mut collection = collection::Collection::new(vfs, index);
collection.load_config(&config)?;
let collection = Arc::new(collection);
api_handler = api::get_api_handler(collection);
}
api_chain = Chain::new(api_handler);
// Start server
println!("Starting up server");
let mut api_chain;
{
let api_handler;
{
let mut collection = collection::Collection::new(vfs, index);
collection.load_config(&config)?;
let collection = Arc::new(collection);
api_handler = api::get_api_handler(collection);
}
api_chain = Chain::new(api_handler);
let auth_secret = config.secret.to_owned();
let cookie_middleware = oven::new(auth_secret.into_bytes());
api_chain.link(cookie_middleware);
}
let auth_secret = config.secret.to_owned();
let cookie_middleware = oven::new(auth_secret.into_bytes());
api_chain.link(cookie_middleware);
}
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))?;
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))?;
// Start DDNS updates
match config.ddns {
Some(ref ddns_config) => {
let ddns_config = ddns_config.clone();
std::thread::spawn(|| {
ddns::run(ddns_config);
});
}
None => (),
};
// Start DDNS updates
match config.ddns {
Some(ref ddns_config) => {
let ddns_config = ddns_config.clone();
std::thread::spawn(|| {
ddns::run(ddns_config);
});
}
None => (),
};
// Run UI
ui::run();
// Run UI
ui::run();
println!("Shutting down server");
server.close()?;
println!("Shutting down server");
server.close()?;
Ok(())
Ok(())
}

View file

@ -13,157 +13,157 @@ use utils::AudioFormat;
#[derive(Debug, PartialEq)]
pub struct SongTags {
pub disc_number: Option<u32>,
pub track_number: Option<u32>,
pub title: Option<String>,
pub artist: Option<String>,
pub album_artist: Option<String>,
pub album: Option<String>,
pub year: Option<i32>,
pub disc_number: Option<u32>,
pub track_number: Option<u32>,
pub title: Option<String>,
pub artist: Option<String>,
pub album_artist: Option<String>,
pub album: Option<String>,
pub year: Option<i32>,
}
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),
_ => bail!("Unsupported file format for reading metadata"),
}
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),
_ => bail!("Unsupported file format for reading metadata"),
}
}
fn read_id3(path: &Path) -> Result<SongTags> {
let tag = id3::Tag::read_from_path(path)?;
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());
let album = tag.album().map(|s| s.to_string());
let title = tag.title().map(|s| s.to_string());
let disc_number = tag.disc();
let track_number = tag.track();
let year = tag.year()
.map(|y| y as i32)
.or(tag.date_released().and_then(|d| d.year))
.or(tag.date_recorded().and_then(|d| d.year));
let artist = tag.artist().map(|s| s.to_string());
let album_artist = tag.album_artist().map(|s| s.to_string());
let album = tag.album().map(|s| s.to_string());
let title = tag.title().map(|s| s.to_string());
let disc_number = tag.disc();
let track_number = tag.track();
let year = tag.year()
.map(|y| y as i32)
.or(tag.date_released().and_then(|d| d.year))
.or(tag.date_recorded().and_then(|d| d.year));
Ok(SongTags {
artist: artist,
album_artist: album_artist,
album: album,
title: title,
disc_number: disc_number,
track_number: track_number,
year: year,
})
Ok(SongTags {
artist: artist,
album_artist: album_artist,
album: album,
title: title,
disc_number: disc_number,
track_number: track_number,
year: year,
})
}
fn read_ape_string(item: &ape::Item) -> Option<String> {
match item.value {
ape::ItemValue::Text(ref s) => Some(s.clone()),
_ => None,
}
match item.value {
ape::ItemValue::Text(ref s) => Some(s.clone()),
_ => None,
}
}
fn read_ape_i32(item: &ape::Item) -> Option<i32> {
match item.value {
ape::ItemValue::Text(ref s) => s.parse::<i32>().ok(),
_ => None,
}
match item.value {
ape::ItemValue::Text(ref s) => s.parse::<i32>().ok(),
_ => None,
}
}
fn read_ape_x_of_y(item: &ape::Item) -> Option<u32> {
match item.value {
ape::ItemValue::Text(ref s) => {
let format = Regex::new(r#"^\d+"#).unwrap();
if let Some((start, end)) = format.find(s) {
s[start..end].parse().ok()
} else {
None
}
}
_ => None,
}
match item.value {
ape::ItemValue::Text(ref s) => {
let format = Regex::new(r#"^\d+"#).unwrap();
if let Some((start, end)) = format.find(s) {
s[start..end].parse().ok()
} else {
None
}
}
_ => None,
}
}
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);
let title = tag.item("Title").and_then(read_ape_string);
let year = tag.item("Year").and_then(read_ape_i32);
let disc_number = tag.item("Disc").and_then(read_ape_x_of_y);
let track_number = tag.item("Track").and_then(read_ape_x_of_y);
Ok(SongTags {
artist: artist,
album_artist: album_artist,
album: album,
title: title,
disc_number: disc_number,
track_number: track_number,
year: year,
})
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);
let title = tag.item("Title").and_then(read_ape_string);
let year = tag.item("Year").and_then(read_ape_i32);
let disc_number = tag.item("Disc").and_then(read_ape_x_of_y);
let track_number = tag.item("Track").and_then(read_ape_x_of_y);
Ok(SongTags {
artist: artist,
album_artist: album_artist,
album: album,
title: title,
disc_number: disc_number,
track_number: track_number,
year: year,
})
}
fn read_vorbis(path: &Path) -> Result<SongTags> {
let file = fs::File::open(path)?;
let source = OggStreamReader::new(PacketReader::new(file))?;
let file = fs::File::open(path)?;
let source = OggStreamReader::new(PacketReader::new(file))?;
let mut tags = SongTags {
artist: None,
album_artist: None,
album: None,
title: None,
disc_number: None,
track_number: None,
year: None,
};
let mut tags = SongTags {
artist: None,
album_artist: None,
album: None,
title: None,
disc_number: None,
track_number: None,
year: None,
};
for (key, value) in source.comment_hdr.comment_list {
match key.as_str() {
"TITLE" => tags.title = Some(value),
"ALBUM" => tags.album = Some(value),
"ARTIST" => tags.artist = Some(value),
"ALBUMARTIST" => tags.album_artist = Some(value),
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
"DATE" => tags.year = value.parse::<i32>().ok(),
_ => (),
}
}
for (key, value) in source.comment_hdr.comment_list {
match key.as_str() {
"TITLE" => tags.title = Some(value),
"ALBUM" => tags.album = Some(value),
"ARTIST" => tags.artist = Some(value),
"ALBUMARTIST" => tags.album_artist = Some(value),
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
"DATE" => tags.year = value.parse::<i32>().ok(),
_ => (),
}
}
Ok(tags)
Ok(tags)
}
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 {
artist: vorbis.artist().map(|v| v[0].clone()),
album_artist: vorbis.album_artist().map(|v| v[0].clone()),
album: vorbis.album().map(|v| v[0].clone()),
title: vorbis.title().map(|v| v[0].clone()),
disc_number: disc_number,
track_number: vorbis.track(),
year: year,
})
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 {
artist: vorbis.artist().map(|v| v[0].clone()),
album_artist: vorbis.album_artist().map(|v| v[0].clone()),
album: vorbis.album().map(|v| v[0].clone()),
title: vorbis.title().map(|v| v[0].clone()),
disc_number: disc_number,
track_number: vorbis.track(),
year: year,
})
}
#[test]
fn test_read_metadata() {
let sample_tags = SongTags {
disc_number: Some(3),
track_number: Some(1),
title: Some("TEST TITLE".into()),
artist: Some("TEST ARTIST".into()),
album_artist: Some("TEST ALBUM ARTIST".into()),
album: Some("TEST ALBUM".into()),
year: Some(2016),
};
assert_eq!(read(Path::new("test/sample.mp3")).unwrap(), sample_tags);
assert_eq!(read(Path::new("test/sample.ogg")).unwrap(), sample_tags);
assert_eq!(read(Path::new("test/sample.flac")).unwrap(), sample_tags);
let sample_tags = SongTags {
disc_number: Some(3),
track_number: Some(1),
title: Some("TEST TITLE".into()),
artist: Some("TEST ARTIST".into()),
album_artist: Some("TEST ALBUM ARTIST".into()),
album: Some("TEST ALBUM".into()),
year: Some(2016),
};
assert_eq!(read(Path::new("test/sample.mp3")).unwrap(), sample_tags);
assert_eq!(read(Path::new("test/sample.ogg")).unwrap(), sample_tags);
assert_eq!(read(Path::new("test/sample.flac")).unwrap(), sample_tags);
}

View file

@ -15,50 +15,50 @@ use utils;
const THUMBNAILS_PATH: &'static str = "thumbnails";
fn hash(path: &Path, dimension: u32) -> u64 {
let path_string = path.to_string_lossy();
let hash_input = format!("{}:{}", path_string, dimension.to_string());
let mut hasher = DefaultHasher::new();
hash_input.hash(&mut hasher);
hasher.finish()
let path_string = path.to_string_lossy();
let hash_input = format!("{}:{}", path_string, dimension.to_string());
let mut hasher = DefaultHasher::new();
hash_input.hash(&mut hasher);
hasher.finish()
}
pub fn get_thumbnail(real_path: &Path, max_dimension: u32) -> Result<PathBuf> {
let mut out_path = utils::get_cache_root()?;
out_path.push(THUMBNAILS_PATH);
let mut out_path = utils::get_cache_root()?;
out_path.push(THUMBNAILS_PATH);
let mut dir_builder = DirBuilder::new();
dir_builder.recursive(true);
dir_builder.create(out_path.as_path())?;
let mut dir_builder = DirBuilder::new();
dir_builder.recursive(true);
dir_builder.create(out_path.as_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);
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);
let hash = hash(real_path, out_dimension);
out_path.push(format!("{}.png", hash.to_string()));
let hash = hash(real_path, out_dimension);
out_path.push(format!("{}.png", hash.to_string()));
if !out_path.exists() {
let source_aspect_ratio: f32 = source_width as f32 / source_height as f32;
if source_aspect_ratio < 0.8 || source_aspect_ratio > 1.2 {
let mut cropped_image = ImageBuffer::new(cropped_dimension, cropped_dimension);
cropped_image.copy_from(&source_image,
(cropped_dimension - source_width) / 2,
(cropped_dimension - source_height) / 2);
let out_image = resize(&cropped_image,
out_dimension,
out_dimension,
FilterType::Lanczos3);
out_image.save(out_path.as_path())?;
} else {
let out_image = resize(&source_image,
out_dimension,
out_dimension,
FilterType::Lanczos3);
out_image.save(out_path.as_path())?;
}
}
if !out_path.exists() {
let source_aspect_ratio: f32 = source_width as f32 / source_height as f32;
if source_aspect_ratio < 0.8 || source_aspect_ratio > 1.2 {
let mut cropped_image = ImageBuffer::new(cropped_dimension, cropped_dimension);
cropped_image.copy_from(&source_image,
(cropped_dimension - source_width) / 2,
(cropped_dimension - source_height) / 2);
let out_image = resize(&cropped_image,
out_dimension,
out_dimension,
FilterType::Lanczos3);
out_image.save(out_path.as_path())?;
} else {
let out_image = resize(&source_image,
out_dimension,
out_dimension,
FilterType::Lanczos3);
out_image.save(out_path.as_path())?;
}
}
Ok(out_path)
Ok(out_path)
}

View file

@ -2,8 +2,8 @@ use std::time;
use std::thread;
pub fn run() {
println!("Starting up UI (headless)");
loop {
thread::sleep(time::Duration::from_secs(10));
}
println!("Starting up UI (headless)");
loop {
thread::sleep(time::Duration::from_secs(10));
}
}

View file

@ -13,220 +13,220 @@ const MESSAGE_NOTIFICATION_ICON: u32 = winapi::WM_USER + 1;
const MESSAGE_NOTIFICATION_ICON_QUIT: u32 = winapi::WM_USER + 2;
pub trait ToWin {
type Out;
fn to_win(&self) -> Self::Out;
type Out;
fn to_win(&self) -> Self::Out;
}
impl<'a> ToWin for &'a str {
type Out = Vec<u16>;
type Out = Vec<u16>;
fn to_win(&self) -> Self::Out {
OsStr::new(self)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
fn to_win(&self) -> Self::Out {
OsStr::new(self)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
}
impl ToWin for uuid::Uuid {
type Out = winapi::GUID;
type Out = winapi::GUID;
fn to_win(&self) -> Self::Out {
let bytes = self.as_bytes();
let end = [bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
bytes[15]];
fn to_win(&self) -> Self::Out {
let bytes = self.as_bytes();
let end = [bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
bytes[15]];
winapi::GUID {
Data1: ((bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 |
(bytes[3] as u32)),
Data2: ((bytes[4] as u16) << 8 | (bytes[5] as u16)),
Data3: ((bytes[6] as u16) << 8 | (bytes[7] as u16)),
Data4: end,
}
}
winapi::GUID {
Data1: ((bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 |
(bytes[3] as u32)),
Data2: ((bytes[4] as u16) << 8 | (bytes[5] as u16)),
Data3: ((bytes[6] as u16) << 8 | (bytes[7] as u16)),
Data4: end,
}
}
}
pub trait Constructible {
type Out;
fn new() -> Self::Out;
type Out;
fn new() -> Self::Out;
}
impl Constructible for winapi::NOTIFYICONDATAW {
type Out = winapi::NOTIFYICONDATAW;
type Out = winapi::NOTIFYICONDATAW;
fn new() -> Self::Out {
winapi::NOTIFYICONDATAW {
cbSize: std::mem::size_of::<winapi::NOTIFYICONDATAW>() as u32,
hWnd: std::ptr::null_mut(),
uFlags: 0,
guidItem: uuid::Uuid::nil().to_win(),
hIcon: std::ptr::null_mut(),
uID: 0,
uCallbackMessage: 0,
szTip: [0; 128],
dwState: 0,
dwStateMask: 0,
szInfo: [0; 256],
uTimeout: winapi::NOTIFYICON_VERSION_4,
szInfoTitle: [0; 64],
dwInfoFlags: 0,
hBalloonIcon: std::ptr::null_mut(),
}
}
fn new() -> Self::Out {
winapi::NOTIFYICONDATAW {
cbSize: std::mem::size_of::<winapi::NOTIFYICONDATAW>() as u32,
hWnd: std::ptr::null_mut(),
uFlags: 0,
guidItem: uuid::Uuid::nil().to_win(),
hIcon: std::ptr::null_mut(),
uID: 0,
uCallbackMessage: 0,
szTip: [0; 128],
dwState: 0,
dwStateMask: 0,
szInfo: [0; 256],
uTimeout: winapi::NOTIFYICON_VERSION_4,
szInfoTitle: [0; 64],
dwInfoFlags: 0,
hBalloonIcon: std::ptr::null_mut(),
}
}
}
fn create_window() -> Option<winapi::HWND> {
let class_name = "Polaris-class".to_win();
let window_name = "Polaris-window".to_win();
let class_name = "Polaris-class".to_win();
let window_name = "Polaris-window".to_win();
unsafe {
let module_handle = kernel32::GetModuleHandleW(std::ptr::null());
let wnd = winapi::WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
hInstance: module_handle,
hIcon: std::ptr::null_mut(),
hCursor: std::ptr::null_mut(),
lpszClassName: class_name.as_ptr(),
hbrBackground: winapi::COLOR_WINDOW as winapi::HBRUSH,
lpszMenuName: std::ptr::null_mut(),
cbClsExtra: 0,
cbWndExtra: 0,
};
unsafe {
let module_handle = kernel32::GetModuleHandleW(std::ptr::null());
let wnd = winapi::WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
hInstance: module_handle,
hIcon: std::ptr::null_mut(),
hCursor: std::ptr::null_mut(),
lpszClassName: class_name.as_ptr(),
hbrBackground: winapi::COLOR_WINDOW as winapi::HBRUSH,
lpszMenuName: std::ptr::null_mut(),
cbClsExtra: 0,
cbWndExtra: 0,
};
let atom = user32::RegisterClassW(&wnd);
if atom == 0 {
return None;
}
let atom = user32::RegisterClassW(&wnd);
if atom == 0 {
return None;
}
let window_handle = user32::CreateWindowExW(0,
atom as winapi::LPCWSTR,
window_name.as_ptr(),
winapi::WS_DISABLED,
0,
0,
0,
0,
user32::GetDesktopWindow(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut());
let window_handle = user32::CreateWindowExW(0,
atom as winapi::LPCWSTR,
window_name.as_ptr(),
winapi::WS_DISABLED,
0,
0,
0,
0,
user32::GetDesktopWindow(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut());
if window_handle.is_null() {
return None;
}
if window_handle.is_null() {
return None;
}
return Some(window_handle);
}
return Some(window_handle);
}
}
fn add_notification_icon(window: winapi::HWND) {
let mut tooltip = [0 as winapi::WCHAR; 128];
for (&x, p) in "Polaris".to_win().iter().zip(tooltip.iter_mut()) {
*p = x;
}
let mut tooltip = [0 as winapi::WCHAR; 128];
for (&x, p) in "Polaris".to_win().iter().zip(tooltip.iter_mut()) {
*p = x;
}
unsafe {
let module = kernel32::GetModuleHandleW(std::ptr::null());
let icon = user32::LoadIconW(module, std::mem::transmute(IDI_POLARIS_TRAY));
let mut flags = winapi::NIF_MESSAGE | winapi::NIF_TIP;
if !icon.is_null() {
flags |= winapi::NIF_ICON;
}
unsafe {
let module = kernel32::GetModuleHandleW(std::ptr::null());
let icon = user32::LoadIconW(module, std::mem::transmute(IDI_POLARIS_TRAY));
let mut flags = winapi::NIF_MESSAGE | winapi::NIF_TIP;
if !icon.is_null() {
flags |= winapi::NIF_ICON;
}
let mut icon_data = winapi::NOTIFYICONDATAW::new();
icon_data.hWnd = window;
icon_data.uID = UID_NOTIFICATION_ICON;
icon_data.uFlags = flags;
icon_data.hIcon = icon;
icon_data.uCallbackMessage = MESSAGE_NOTIFICATION_ICON;
icon_data.szTip = tooltip;
let mut icon_data = winapi::NOTIFYICONDATAW::new();
icon_data.hWnd = window;
icon_data.uID = UID_NOTIFICATION_ICON;
icon_data.uFlags = flags;
icon_data.hIcon = icon;
icon_data.uCallbackMessage = MESSAGE_NOTIFICATION_ICON;
icon_data.szTip = tooltip;
shell32::Shell_NotifyIconW(winapi::NIM_ADD, &mut icon_data);
}
shell32::Shell_NotifyIconW(winapi::NIM_ADD, &mut icon_data);
}
}
fn remove_notification_icon(window: winapi::HWND) {
let mut icon_data = winapi::NOTIFYICONDATAW::new();
icon_data.hWnd = window;
icon_data.uID = UID_NOTIFICATION_ICON;
unsafe {
shell32::Shell_NotifyIconW(winapi::NIM_DELETE, &mut icon_data);
}
let mut icon_data = winapi::NOTIFYICONDATAW::new();
icon_data.hWnd = window;
icon_data.uID = UID_NOTIFICATION_ICON;
unsafe {
shell32::Shell_NotifyIconW(winapi::NIM_DELETE, &mut icon_data);
}
}
fn open_notification_context_menu(window: winapi::HWND) {
println!("Opening notification icon context menu");
let quit_string = "Quit Polaris".to_win();
println!("Opening notification icon context menu");
let quit_string = "Quit Polaris".to_win();
unsafe {
let context_menu = user32::CreatePopupMenu();
if context_menu.is_null() {
return;
}
user32::InsertMenuW(context_menu,
0,
winapi::winuser::MF_STRING,
MESSAGE_NOTIFICATION_ICON_QUIT as u64,
quit_string.as_ptr());
unsafe {
let context_menu = user32::CreatePopupMenu();
if context_menu.is_null() {
return;
}
user32::InsertMenuW(context_menu,
0,
winapi::winuser::MF_STRING,
MESSAGE_NOTIFICATION_ICON_QUIT as u64,
quit_string.as_ptr());
let mut cursor_position = winapi::POINT { x: 0, y: 0 };
user32::GetCursorPos(&mut cursor_position);
let mut cursor_position = winapi::POINT { x: 0, y: 0 };
user32::GetCursorPos(&mut cursor_position);
user32::SetForegroundWindow(window);
let flags = winapi::winuser::TPM_RIGHTALIGN | winapi::winuser::TPM_BOTTOMALIGN |
winapi::winuser::TPM_RIGHTBUTTON;
user32::TrackPopupMenu(context_menu,
flags,
cursor_position.x,
cursor_position.y,
0,
window,
std::ptr::null_mut());
user32::PostMessageW(window, 0, 0, 0);
user32::SetForegroundWindow(window);
let flags = winapi::winuser::TPM_RIGHTALIGN | winapi::winuser::TPM_BOTTOMALIGN |
winapi::winuser::TPM_RIGHTBUTTON;
user32::TrackPopupMenu(context_menu,
flags,
cursor_position.x,
cursor_position.y,
0,
window,
std::ptr::null_mut());
user32::PostMessageW(window, 0, 0, 0);
println!("Closing notification context menu");
user32::DestroyMenu(context_menu);
}
println!("Closing notification context menu");
user32::DestroyMenu(context_menu);
}
}
fn quit(window: winapi::HWND) {
println!("Shutting down UI");
unsafe {
user32::PostMessageW(window, winapi::winuser::WM_CLOSE, 0, 0);
}
println!("Shutting down UI");
unsafe {
user32::PostMessageW(window, winapi::winuser::WM_CLOSE, 0, 0);
}
}
pub fn run() {
println!("Starting up UI (Windows)");
println!("Starting up UI (Windows)");
create_window().expect("Could not initialize window");
create_window().expect("Could not initialize window");
let mut message = winapi::MSG {
hwnd: std::ptr::null_mut(),
message: 0,
wParam: 0,
lParam: 0,
time: 0,
pt: winapi::POINT { x: 0, y: 0 },
};
let mut message = winapi::MSG {
hwnd: std::ptr::null_mut(),
message: 0,
wParam: 0,
lParam: 0,
time: 0,
pt: winapi::POINT { x: 0, y: 0 },
};
loop {
let status: i32;
unsafe {
status = user32::GetMessageW(&mut message, std::ptr::null_mut(), 0, 0);
if status == -1 {
panic!("GetMessageW error: {}", kernel32::GetLastError());
}
if status == 0 {
break;
}
user32::TranslateMessage(&message);
user32::DispatchMessageW(&message);
}
}
loop {
let status: i32;
unsafe {
status = user32::GetMessageW(&mut message, std::ptr::null_mut(), 0, 0);
if status == -1 {
panic!("GetMessageW error: {}", kernel32::GetLastError());
}
if status == 0 {
break;
}
user32::TranslateMessage(&message);
user32::DispatchMessageW(&message);
}
}
}
pub unsafe extern "system" fn window_proc(window: winapi::HWND,
@ -234,37 +234,37 @@ pub unsafe extern "system" fn window_proc(window: winapi::HWND,
w_param: winapi::WPARAM,
l_param: winapi::LPARAM)
-> winapi::LRESULT {
match msg {
match msg {
winapi::winuser::WM_CREATE => {
add_notification_icon(window);
}
winapi::winuser::WM_CREATE => {
add_notification_icon(window);
}
MESSAGE_NOTIFICATION_ICON => {
match winapi::LOWORD(l_param as winapi::DWORD) as u32 {
winapi::winuser::WM_RBUTTONUP => {
open_notification_context_menu(window);
}
_ => (),
}
}
MESSAGE_NOTIFICATION_ICON => {
match winapi::LOWORD(l_param as winapi::DWORD) as u32 {
winapi::winuser::WM_RBUTTONUP => {
open_notification_context_menu(window);
}
_ => (),
}
}
winapi::winuser::WM_COMMAND => {
match winapi::LOWORD(w_param as winapi::DWORD) as u32 {
MESSAGE_NOTIFICATION_ICON_QUIT => {
quit(window);
}
_ => (),
}
}
winapi::winuser::WM_COMMAND => {
match winapi::LOWORD(w_param as winapi::DWORD) as u32 {
MESSAGE_NOTIFICATION_ICON_QUIT => {
quit(window);
}
_ => (),
}
}
winapi::winuser::WM_DESTROY => {
remove_notification_icon(window);
user32::PostQuitMessage(0);
}
winapi::winuser::WM_DESTROY => {
remove_notification_icon(window);
user32::PostQuitMessage(0);
}
_ => (),
};
_ => (),
};
return user32::DefWindowProcW(window, msg, w_param, l_param);
return user32::DefWindowProcW(window, msg, w_param, l_param);
}

View file

@ -5,90 +5,90 @@ use std::fs;
use errors::*;
pub fn get_config_root() -> Result<PathBuf> {
if let Ok(mut root) = data_root(AppDataType::SharedConfig) {
root.push("Polaris");
fs::create_dir_all(&root)?;
return Ok(root);
}
bail!("Could not retrieve config directory root");
if let Ok(mut root) = data_root(AppDataType::SharedConfig) {
root.push("Polaris");
fs::create_dir_all(&root)?;
return Ok(root);
}
bail!("Could not retrieve config directory root");
}
pub fn get_cache_root() -> Result<PathBuf> {
if let Ok(mut root) = data_root(AppDataType::SharedData) {
root.push("Polaris");
fs::create_dir_all(&root)?;
return Ok(root);
}
bail!("Could not retrieve cache directory root");
if let Ok(mut root) = data_root(AppDataType::SharedData) {
root.push("Polaris");
fs::create_dir_all(&root)?;
return Ok(root);
}
bail!("Could not retrieve cache directory root");
}
#[derive(Debug, PartialEq)]
pub enum AudioFormat {
FLAC,
MP3,
MP4,
MPC,
OGG,
FLAC,
MP3,
MP4,
MPC,
OGG,
}
pub fn get_audio_format(path: &Path) -> Option<AudioFormat> {
let extension = match path.extension() {
Some(e) => e,
_ => return None,
};
let extension = match extension.to_str() {
Some(e) => e,
_ => return None,
};
match extension.to_lowercase().as_str() {
"flac" => Some(AudioFormat::FLAC),
"mp3" => Some(AudioFormat::MP3),
"m4a" => Some(AudioFormat::MP4),
"mpc" => Some(AudioFormat::MPC),
"ogg" => Some(AudioFormat::OGG),
_ => None,
}
let extension = match path.extension() {
Some(e) => e,
_ => return None,
};
let extension = match extension.to_str() {
Some(e) => e,
_ => return None,
};
match extension.to_lowercase().as_str() {
"flac" => Some(AudioFormat::FLAC),
"mp3" => Some(AudioFormat::MP3),
"m4a" => Some(AudioFormat::MP4),
"mpc" => Some(AudioFormat::MPC),
"ogg" => Some(AudioFormat::OGG),
_ => None,
}
}
#[test]
fn test_get_audio_format() {
assert_eq!(get_audio_format(Path::new("animals/🐷/my🐖file.jpg")),
None);
assert_eq!(get_audio_format(Path::new("animals/🐷/my🐖file.flac")),
Some(AudioFormat::FLAC));
assert_eq!(get_audio_format(Path::new("animals/🐷/my🐖file.jpg")),
None);
assert_eq!(get_audio_format(Path::new("animals/🐷/my🐖file.flac")),
Some(AudioFormat::FLAC));
}
pub fn is_song(path: &Path) -> bool {
get_audio_format(path).is_some()
get_audio_format(path).is_some()
}
#[test]
fn test_is_song() {
assert!(is_song(Path::new("animals/🐷/my🐖file.mp3")));
assert!(!is_song(Path::new("animals/🐷/my🐖file.jpg")));
assert!(is_song(Path::new("animals/🐷/my🐖file.mp3")));
assert!(!is_song(Path::new("animals/🐷/my🐖file.jpg")));
}
pub fn is_image(path: &Path) -> bool {
let extension = match path.extension() {
Some(e) => e,
_ => return false,
};
let extension = match extension.to_str() {
Some(e) => e,
_ => return false,
};
match extension.to_lowercase().as_str() {
"png" => true,
"gif" => true,
"jpg" => true,
"jpeg" => true,
"bmp" => true,
_ => false,
}
let extension = match path.extension() {
Some(e) => e,
_ => return false,
};
let extension = match extension.to_str() {
Some(e) => e,
_ => return false,
};
match extension.to_lowercase().as_str() {
"png" => true,
"gif" => true,
"jpg" => true,
"jpeg" => true,
"bmp" => true,
_ => false,
}
}
#[test]
fn test_is_image() {
assert!(!is_image(Path::new("animals/🐷/my🐖file.mp3")));
assert!(is_image(Path::new("animals/🐷/my🐖file.jpg")));
assert!(!is_image(Path::new("animals/🐷/my🐖file.mp3")));
assert!(is_image(Path::new("animals/🐷/my🐖file.jpg")));
}

View file

@ -6,105 +6,105 @@ use errors::*;
#[derive(Debug, Clone)]
pub struct VfsConfig {
pub mount_points: HashMap<String, PathBuf>,
pub mount_points: HashMap<String, PathBuf>,
}
impl VfsConfig {
pub fn new() -> VfsConfig {
VfsConfig { mount_points: HashMap::new() }
}
pub fn new() -> VfsConfig {
VfsConfig { mount_points: HashMap::new() }
}
}
pub struct Vfs {
mount_points: HashMap<String, PathBuf>,
mount_points: HashMap<String, PathBuf>,
}
impl Vfs {
pub fn new(config: VfsConfig) -> Vfs {
Vfs { mount_points: config.mount_points }
}
pub fn new(config: VfsConfig) -> Vfs {
Vfs { mount_points: config.mount_points }
}
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) => {
let mount_path = Path::new(&name);
return Ok(mount_path.join(p));
}
Err(_) => (),
}
}
bail!("Real path has no match in VFS")
}
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) => {
let mount_path = Path::new(&name);
return Ok(mount_path.join(p));
}
Err(_) => (),
}
}
bail!("Real path has no match in VFS")
}
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) {
Ok(p) => {
return if p.components().count() == 0 {
Ok(target.clone())
} else {
Ok(target.join(p))
}
}
Err(_) => (),
}
}
bail!("Virtual path has no match in VFS")
}
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) {
Ok(p) => {
return if p.components().count() == 0 {
Ok(target.clone())
} else {
Ok(target.join(p))
}
}
Err(_) => (),
}
}
bail!("Virtual path has no match in VFS")
}
pub fn get_mount_points(&self) -> &HashMap<String, PathBuf> {
return &self.mount_points;
}
pub fn get_mount_points(&self) -> &HashMap<String, PathBuf> {
return &self.mount_points;
}
}
#[test]
fn test_virtual_to_real() {
let mut config = VfsConfig::new();
config.mount_points.insert("root".to_owned(), Path::new("test_dir").to_path_buf());
let vfs = Vfs::new(config);
let mut config = VfsConfig::new();
config.mount_points.insert("root".to_owned(), Path::new("test_dir").to_path_buf());
let vfs = Vfs::new(config);
let mut correct_path = PathBuf::new();
correct_path.push("test_dir");
correct_path.push("somewhere");
correct_path.push("something.png");
let mut correct_path = PathBuf::new();
correct_path.push("test_dir");
correct_path.push("somewhere");
correct_path.push("something.png");
let mut virtual_path = PathBuf::new();
virtual_path.push("root");
virtual_path.push("somewhere");
virtual_path.push("something.png");
let mut virtual_path = PathBuf::new();
virtual_path.push("root");
virtual_path.push("somewhere");
virtual_path.push("something.png");
let found_path = vfs.virtual_to_real(virtual_path.as_path()).unwrap();
assert!(found_path.to_str() == correct_path.to_str());
let found_path = vfs.virtual_to_real(virtual_path.as_path()).unwrap();
assert!(found_path.to_str() == correct_path.to_str());
}
#[test]
fn test_virtual_to_real_no_trail() {
let mut config = VfsConfig::new();
config.mount_points.insert("root".to_owned(), Path::new("test_dir").to_path_buf());
let vfs = Vfs::new(config);
let correct_path = Path::new("test_dir");
let found_path = vfs.virtual_to_real(Path::new("root")).unwrap();
assert!(found_path.to_str() == correct_path.to_str());
let mut config = VfsConfig::new();
config.mount_points.insert("root".to_owned(), Path::new("test_dir").to_path_buf());
let vfs = Vfs::new(config);
let correct_path = Path::new("test_dir");
let found_path = vfs.virtual_to_real(Path::new("root")).unwrap();
assert!(found_path.to_str() == correct_path.to_str());
}
#[test]
fn test_real_to_virtual() {
let mut config = VfsConfig::new();
config.mount_points.insert("root".to_owned(), Path::new("test_dir").to_path_buf());
let vfs = Vfs::new(config);
let mut config = VfsConfig::new();
config.mount_points.insert("root".to_owned(), Path::new("test_dir").to_path_buf());
let vfs = Vfs::new(config);
let mut correct_path = PathBuf::new();
correct_path.push("root");
correct_path.push("somewhere");
correct_path.push("something.png");
let mut correct_path = PathBuf::new();
correct_path.push("root");
correct_path.push("somewhere");
correct_path.push("something.png");
let mut real_path = PathBuf::new();
real_path.push("test_dir");
real_path.push("somewhere");
real_path.push("something.png");
let mut real_path = PathBuf::new();
real_path.push("test_dir");
real_path.push("somewhere");
real_path.push("something.png");
let found_path = vfs.real_to_virtual(real_path.as_path()).unwrap();
assert!(found_path == correct_path);
let found_path = vfs.real_to_virtual(real_path.as_path()).unwrap();
assert!(found_path == correct_path);
}