Accessing external state in api handlers

This commit is contained in:
Antoine Gersant 2016-08-20 00:32:57 -07:00
parent f067a1c3d1
commit 9c69353f69
5 changed files with 113 additions and 36 deletions

View file

@ -1,6 +1,9 @@
use core::str::Utf8Error; use core::str::Utf8Error;
use core::ops::DerefMut;
use std::path::PathBuf; use std::path::PathBuf;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
use iron::prelude::*; use iron::prelude::*;
use iron::status; use iron::status;
@ -15,15 +18,28 @@ impl From<CollectionError> for IronError {
fn from(err: CollectionError) -> IronError { fn from(err: CollectionError) -> IronError {
match err { match err {
CollectionError::Io(e) => IronError::new(e, status::NotFound), CollectionError::Io(e) => IronError::new(e, status::NotFound),
CollectionError::PathDecoding => IronError::new(err, status::InternalServerError) CollectionError::PathDecoding => IronError::new(err, status::InternalServerError),
CollectionError::ConflictingMount(_) => IronError::new(err, status::BadRequest),
} }
} }
} }
pub fn get_api_handler() -> Mount { pub fn get_api_handler(collection: Arc<Mutex<collection::Collection>>) -> Mount {
let mut mount = Mount::new(); let mut mount = Mount::new();
mount.mount("/browse/", self::browse) {
.mount("/flatten/", self::flatten); let collection = collection.clone();
mount.mount("/browse/", move |request: &mut Request| {
let mut acquired_collection = collection.deref().lock().unwrap();
self::browse(request, acquired_collection.deref_mut())
} );
}
{
let collection = collection.clone();
mount.mount("/flatten/", move |request: &mut Request| {
let mut acquired_collection = collection.deref().lock().unwrap();
self::flatten(request, acquired_collection.deref_mut())
} );
}
mount mount
} }
@ -33,13 +49,13 @@ fn path_from_request(request: &Request) -> Result<PathBuf, Utf8Error> {
Ok(PathBuf::from(decoded_path.deref())) Ok(PathBuf::from(decoded_path.deref()))
} }
fn browse(request: &mut Request) -> IronResult<Response> { fn browse(request: &mut Request, collection: &mut collection::Collection) -> IronResult<Response> {
let path = path_from_request(request); let path = path_from_request(request);
if path.is_err() { if path.is_err() {
return Ok(Response::with(status::BadRequest)); return Ok(Response::with(status::BadRequest));
} }
let path = path.unwrap(); let path = path.unwrap();
let browse_result = try!(collection::browse(&path)); let browse_result = try!(collection.browse(&path));
let result_json = json::encode(&browse_result); let result_json = json::encode(&browse_result);
if result_json.is_err() { if result_json.is_err() {
@ -51,11 +67,11 @@ fn browse(request: &mut Request) -> IronResult<Response> {
Ok(Response::with((status::Ok, result_json))) Ok(Response::with((status::Ok, result_json)))
} }
fn flatten(request: &mut Request) -> IronResult<Response> { fn flatten(request: &mut Request, collection: &mut collection::Collection) -> IronResult<Response> {
let path = path_from_request(request); let path = path_from_request(request);
if path.is_err() { if path.is_err() {
return Ok(Response::with((status::BadRequest))); return Ok(Response::with((status::BadRequest)));
} }
collection::flatten(&path.unwrap()); collection.flatten(&path.unwrap());
Ok(Response::with((status::Ok, "TODO Flatten data here"))) Ok(Response::with((status::Ok, "TODO Flatten data here")))
} }

View file

@ -7,6 +7,7 @@ pub enum CollectionError
{ {
PathDecoding, PathDecoding,
Io(io::Error), Io(io::Error),
ConflictingMount(String),
} }
impl From<io::Error> for CollectionError { impl From<io::Error> for CollectionError {
@ -20,6 +21,7 @@ impl error::Error for CollectionError {
match *self { match *self {
CollectionError::Io(ref err) => err.description(), CollectionError::Io(ref err) => err.description(),
CollectionError::PathDecoding => "Error while decoding a Path as a UTF-8 string", CollectionError::PathDecoding => "Error while decoding a Path as a UTF-8 string",
CollectionError::ConflictingMount(_) => "Attempting to mount multiple directories under the same name",
} }
} }
@ -27,6 +29,7 @@ impl error::Error for CollectionError {
match *self { match *self {
CollectionError::Io(ref err) => Some(err), CollectionError::Io(ref err) => Some(err),
CollectionError::PathDecoding => None, CollectionError::PathDecoding => None,
CollectionError::ConflictingMount(_) => None,
} }
} }
} }
@ -36,6 +39,7 @@ impl fmt::Display for CollectionError {
match *self { match *self {
CollectionError::Io(ref err) => write!(f, "IO error: {}", err), CollectionError::Io(ref err) => write!(f, "IO error: {}", err),
CollectionError::PathDecoding => write!(f, "Path decoding error"), CollectionError::PathDecoding => write!(f, "Path decoding error"),
CollectionError::ConflictingMount(ref name) => write!(f, "Mount point {} already has a target directory", name),
} }
} }
} }

View file

@ -4,6 +4,7 @@ use std::path::Path;
pub use self::error::CollectionError; pub use self::error::CollectionError;
mod error; mod error;
mod vfs;
#[derive(Debug, RustcEncodable)] #[derive(Debug, RustcEncodable)]
pub struct Song { pub struct Song {
@ -21,36 +22,50 @@ pub enum CollectionFile {
Song(Song), Song(Song),
} }
pub fn browse(path: &Path) -> Result<Vec<CollectionFile>, CollectionError> { pub struct Collection {
vfs: vfs::Vfs,
}
let full_path = "samplemusic/".to_string() + path.to_str().unwrap(); // TMP use mount directories impl Collection {
println!("Browsing: {}", full_path); pub fn new() -> Collection {
Collection{
let mut out = vec![]; vfs: vfs::Vfs::new(),
for file in try!(fs::read_dir(full_path)) {
let file = try!(file);
let file_meta = try!(file.metadata());
let file_path = file.path().to_owned();
if file_meta.is_file() {
let path_string = try!(file_path.to_str().ok_or(CollectionError::PathDecoding));
let collection_file = CollectionFile::Song(Song {
path: path_string.to_string(),
});
out.push(collection_file);
} else if file_meta.is_dir() {
let path_string = try!(file_path.to_str().ok_or(CollectionError::PathDecoding));
let collection_file = CollectionFile::Directory(Directory {
path: path_string.to_string(),
});
out.push(collection_file);
} }
} }
Ok(out)
} }
pub fn flatten(path: &Path) -> Vec<CollectionFile> { impl Collection {
println!("Flatten {:?}", path); pub fn browse(&self, path: &Path) -> Result<Vec<CollectionFile>, CollectionError> {
let out = vec![];
out let full_path = "samplemusic/".to_string() + path.to_str().unwrap(); // TMP use mount directories
println!("Browsing: {}", full_path);
let mut out = vec![];
for file in try!(fs::read_dir(full_path)) {
let file = try!(file);
let file_meta = try!(file.metadata());
let file_path = file.path().to_owned();
if file_meta.is_file() {
let path_string = try!(file_path.to_str().ok_or(CollectionError::PathDecoding));
let collection_file = CollectionFile::Song(Song {
path: path_string.to_string(),
});
out.push(collection_file);
} else if file_meta.is_dir() {
let path_string = try!(file_path.to_str().ok_or(CollectionError::PathDecoding));
let collection_file = CollectionFile::Directory(Directory {
path: path_string.to_string(),
});
out.push(collection_file);
}
}
Ok(out)
}
pub fn flatten(&self, path: &Path) -> Vec<CollectionFile> {
println!("Flatten {:?}", path);
let out = vec![];
out
}
} }

35
src/collection/vfs.rs Normal file
View file

@ -0,0 +1,35 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::path::Path;
use collection::error::CollectionError;
pub struct Vfs {
mount_points: HashMap<String, PathBuf>,
}
impl Vfs {
pub fn new() -> Vfs {
Vfs {
mount_points: HashMap::new(),
}
}
pub fn mount(&mut self, name: &str, real_path: &Path) -> Result<(), CollectionError>
{
let name = name.to_string();
if self.mount_points.contains_key(&name) {
return Err(CollectionError::ConflictingMount(name));
}
self.mount_points.insert(name, real_path.to_path_buf());
Ok(())
}
pub fn real_to_virtual(&self, real_path: &Path) -> Result<(), CollectionError> {
Ok(())
}
pub fn virtual_to_real(&self, virtual_path: &Path) -> Result<(), CollectionError> {
Ok(())
}
}

View file

@ -5,6 +5,9 @@ extern crate rustc_serialize;
extern crate staticfile; extern crate staticfile;
extern crate url; extern crate url;
use std::sync::Arc;
use std::sync::Mutex;
use iron::prelude::*; use iron::prelude::*;
use mount::Mount; use mount::Mount;
use staticfile::Static; use staticfile::Static;
@ -13,8 +16,12 @@ mod api;
mod collection; mod collection;
fn main() { fn main() {
let collection = collection::Collection::new();
let collection = Arc::new(Mutex::new(collection));
let mut mount = Mount::new(); let mut mount = Mount::new();
let api_handler = api::get_api_handler(); let api_handler = api::get_api_handler( collection );
mount.mount("/static/", Static::new("samplemusic/")) mount.mount("/static/", Static::new("samplemusic/"))
.mount("/api/", api_handler); .mount("/api/", api_handler);