use super::{parse_data_uri, parse_headers};
use crate::client::HttpClient;
use crate::data::{DataType, HasDataPath};
use crate::error::{ApiError, Error, ErrorKind, Result, ResultExt};
use crate::Body;
use chrono::{DateTime, TimeZone, Utc};
use reqwest::StatusCode;
use std::io::{self, Read};
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)
}
}
impl FileData {
pub fn into_bytes(mut self) -> io::Result<Vec<u8>> {
let mut bytes = Vec::with_capacity(self.size as usize);
self.read_to_end(&mut bytes)?;
Ok(bytes)
}
pub fn into_string(mut self) -> io::Result<String> {
let mut text = String::with_capacity(self.size as usize);
self.read_to_string(&mut text)?;
Ok(text)
}
}
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 mut res = self
.client
.put(url)
.body(body)
.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::NOT_FOUND => Err(ErrorKind::NotFound(self.to_url().unwrap()).into()),
status => Err(ApiError::from_json_or_status(&res_json, status).into()),
}
}
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::NOT_FOUND => Err(Error::from(ErrorKind::NotFound(self.to_url().unwrap()))),
status => Err(ApiError::from(status.to_string()).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::NOT_FOUND => Err(ErrorKind::NotFound(self.to_url().unwrap()).into()),
status => Err(ApiError::from_json_or_status(&res_json, status).into()),
}
}
}