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::ops::DerefMut;
use std::path::PathBuf;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
use iron::prelude::*;
use iron::status;
@ -15,15 +18,28 @@ impl From<CollectionError> for IronError {
fn from(err: CollectionError) -> IronError {
match err {
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();
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
}
@ -33,13 +49,13 @@ fn path_from_request(request: &Request) -> Result<PathBuf, Utf8Error> {
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);
if path.is_err() {
return Ok(Response::with(status::BadRequest));
}
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);
if result_json.is_err() {
@ -51,11 +67,11 @@ fn browse(request: &mut Request) -> IronResult<Response> {
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);
if path.is_err() {
return Ok(Response::with((status::BadRequest)));
}
collection::flatten(&path.unwrap());
collection.flatten(&path.unwrap());
Ok(Response::with((status::Ok, "TODO Flatten data here")))
}

View file

@ -7,6 +7,7 @@ pub enum CollectionError
{
PathDecoding,
Io(io::Error),
ConflictingMount(String),
}
impl From<io::Error> for CollectionError {
@ -20,6 +21,7 @@ impl error::Error for CollectionError {
match *self {
CollectionError::Io(ref err) => err.description(),
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 {
CollectionError::Io(ref err) => Some(err),
CollectionError::PathDecoding => None,
CollectionError::ConflictingMount(_) => None,
}
}
}
@ -36,6 +39,7 @@ impl fmt::Display for CollectionError {
match *self {
CollectionError::Io(ref err) => write!(f, "IO error: {}", err),
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;
mod error;
mod vfs;
#[derive(Debug, RustcEncodable)]
pub struct Song {
@ -21,36 +22,50 @@ pub enum CollectionFile {
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
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);
impl Collection {
pub fn new() -> Collection {
Collection{
vfs: vfs::Vfs::new(),
}
}
Ok(out)
}
pub fn flatten(path: &Path) -> Vec<CollectionFile> {
println!("Flatten {:?}", path);
let out = vec![];
out
impl Collection {
pub fn browse(&self, path: &Path) -> Result<Vec<CollectionFile>, CollectionError> {
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 url;
use std::sync::Arc;
use std::sync::Mutex;
use iron::prelude::*;
use mount::Mount;
use staticfile::Static;
@ -13,8 +16,12 @@ mod api;
mod collection;
fn main() {
let collection = collection::Collection::new();
let collection = Arc::new(Mutex::new(collection));
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("/api/", api_handler);