use chrono::{DateTime, UTC, TimeZone};
use client::HttpClient;
use reqwest::StatusCode;
use data::{HasDataPath, DataType};
use std::io::{self, Read};
use Body;
use error::{self, Error, ErrorKind, Result, ResultExt, ApiError};
use super::{parse_headers, parse_data_uri};
pub struct FileData {
pub size: u64,
pub last_modified: DateTime<UTC>,
data: Box<Read>,
}
impl Read for FileData {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.data.read(buf)
}
}
pub struct DataFile {
path: String,
client: HttpClient,
}
impl HasDataPath for DataFile {
#[doc(hidden)]
fn new(client: HttpClient, path: &str) -> Self {
DataFile {
client: client,
path: parse_data_uri(path).to_string(),
}
}
#[doc(hidden)]
fn path(&self) -> &str {
&self.path
}
#[doc(hidden)]
fn client(&self) -> &HttpClient {
&self.client
}
}
impl DataFile {
pub fn put<B>(&self, body: B) -> Result<()>
where B: Into<Body>
{
let url = self.to_url()?;
let req = self.client.put(url).body(body);
let mut res = req.send()
.chain_err(|| ErrorKind::Http(format!("writing file '{}'", self.to_data_uri())))?;
let mut res_json = String::new();
res.read_to_string(&mut res_json)
.chain_err(|| ErrorKind::Io(format!("writing file '{}'", self.to_data_uri())))?;
match *res.status() {
status if status.is_success() => Ok(()),
StatusCode::NotFound => Err(ErrorKind::NotFound(self.to_url().unwrap()).into()),
status => {
let api_error = ApiError {
message: status.to_string(),
stacktrace: None,
};
Err(Error::from(ErrorKind::Api(api_error))).chain_err(|| error::decode(&res_json))
}
}
}
pub fn get(&self) -> Result<FileData> {
let url = self.to_url()?;
let req = self.client.get(url);
let res = req.send()
.chain_err(|| ErrorKind::Http(format!("downloading file '{}'", self.to_data_uri())))?;
match *res.status() {
StatusCode::Ok => {
let metadata = parse_headers(res.headers())?;
match metadata.data_type {
DataType::File => (),
DataType::Dir => {
return Err(ErrorKind::UnexpectedDataType("file", "directory".to_string())
.into());
}
}
Ok(FileData {
size: metadata.content_length.unwrap_or(0),
last_modified: metadata.last_modified
.unwrap_or_else(|| UTC.ymd(2015, 3, 14).and_hms(8, 0, 0)),
data: Box::new(res),
})
}
StatusCode::NotFound => Err(Error::from(ErrorKind::NotFound(self.to_url().unwrap()))),
status => {
Err(ErrorKind::Api(ApiError {
message: status.to_string(),
stacktrace: None,
})
.into())
}
}
}
pub fn delete(&self) -> Result<()> {
let url = self.to_url()?;
let req = self.client.delete(url);
let mut res = req.send()
.chain_err(|| ErrorKind::Http(format!("deleting file '{}'", self.to_data_uri())))?;
let mut res_json = String::new();
res.read_to_string(&mut res_json)
.chain_err(|| ErrorKind::Io(format!("deleting file '{}'", self.to_data_uri())))?;
match *res.status() {
status if status.is_success() => Ok(()),
StatusCode::NotFound => Err(ErrorKind::NotFound(self.to_url().unwrap()).into()),
status => {
let api_error = ApiError {
message: status.to_string(),
stacktrace: None,
};
Err(Error::from(ErrorKind::Api(api_error))).chain_err(|| error::decode(&res_json))
}
}
}
}