~hime/aqua

a516bc585603038c820d8192fea1359f0e69511c — Robbie Straw 7 years ago c219e4d
rename processing Result & Error types
M src/bin/aqua_thumbfix.rs => src/bin/aqua_thumbfix.rs +3 -3
@@ 9,7 9,7 @@ extern crate image;

use aqua::models::{Entry, EntryTag, Tag, NewEntry};
use aqua::schema;
use aqua::util::processing::{ProcessingError, ProcessingResult};
use aqua::util::processing;
use clap::{Arg, App};
use diesel::prelude::*;
use diesel::pg::PgConnection;


@@ 41,14 41,14 @@ fn main() {
    process_entries(&content_store[..]);
}

fn establish_connection() -> ProcessingResult<PgConnection> {
fn establish_connection() -> processing::Result<PgConnection> {
    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL not set in `.env` file !!!");

    Ok(PgConnection::establish(&database_url)?)
}

fn process_entries(content_store: &str) -> ProcessingResult<()> {
fn process_entries(content_store: &str) -> processing::Result<()> {
    let conn = establish_connection()?;
    
    let missing_thumb_tag = schema::tags::table

M src/bin/aqua_watch.rs => src/bin/aqua_watch.rs +7 -7
@@ 35,7 35,7 @@ extern crate serde_json;

use aqua::models::{Entry, NewEntry};
use aqua::schema;
use aqua::util::processing::{ProcessingError, ProcessingResult};
use aqua::util::processing;
use clap::{Arg, App};
use diesel::prelude::*;
use diesel::pg::PgConnection;


@@ 101,7 101,7 @@ fn main() {
}

// TODO: check that file doesn't exist before moving it ...
fn handle_new_file(path: PathBuf, content_store: &str) -> ProcessingResult<()> {
fn handle_new_file(path: PathBuf, content_store: &str) -> processing::Result<()> {
    let digest = aqua::util::processing::hash_file(path.as_path())?;
    let mut file = OpenOptions::new()
        .read(true)


@@ 133,11 133,11 @@ fn handle_new_file(path: PathBuf, content_store: &str) -> ProcessingResult<()> {

        Ok(())
    } else {
        Err(ProcessingError::DetectionFailed)
        Err(processing::Error::DetectionFailed)
    }
}

fn establish_connection() -> ProcessingResult<PgConnection> {
fn establish_connection() -> processing::Result<PgConnection> {
    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL not set in `.env` file !!!");



@@ 145,7 145,7 @@ fn establish_connection() -> ProcessingResult<PgConnection> {
}

// create entry in database
fn create_db_entry(digest: &str, mime_ty: &str) -> ProcessingResult<Entry> {
fn create_db_entry(digest: &str, mime_ty: &str) -> processing::Result<Entry> {
    let pg_conn = establish_connection()?;
    let aqua_entry = NewEntry { hash: &digest, mime: Some(&mime_ty) };
    let entry = diesel::insert(&aqua_entry)


@@ 156,7 156,7 @@ fn create_db_entry(digest: &str, mime_ty: &str) -> ProcessingResult<Entry> {
}

// moves the file from `src_path` to the `content_store` based on its digest
fn move_file(src_path: &Path, content_store: &str, digest: &str, file_ext: &str) -> ProcessingResult<()> {
fn move_file(src_path: &Path, content_store: &str, digest: &str, file_ext: &str) -> processing::Result<()> {
    // carve out a bucket based on first byte of SHA256 digest
    // create the bucket if it does not exist
    let file_bucket    = format!("f{}", &digest[0..2]);


@@ 168,7 168,7 @@ fn move_file(src_path: &Path, content_store: &str, digest: &str, file_ext: &str)
        .join(file_filename);

    // TODO: bad error type ... 
    let bucket_dir = dest.parent().ok_or(ProcessingError::ThumbnailFailed)?;
    let bucket_dir = dest.parent().ok_or(processing::Error::ThumbnailFailed)?;
    fs::create_dir_all(bucket_dir)?;

    // move the file 

M src/util/processing/image_detector.rs => src/util/processing/image_detector.rs +2 -3
@@ 2,7 2,6 @@ use image::{self, ImageFormat};
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use super::{ProcessingError, ProcessingResult};

/// ImageMeta stores mappings of common image filetypes to their associated
/// MIME type and typical file extension. This is useful in processing files 


@@ 75,7 74,7 @@ pub fn mime_detect(data: &[u8]) -> Option<ImageMeta> {

// creates a thumbnail in the content store for the specified digest
// this expects an `ImageMeta` structure describing the input.
pub fn process_image(content_store: &str, digest: &str, buf: &[u8]) -> ProcessingResult<()> {
pub fn process_image(content_store: &str, digest: &str, buf: &[u8]) -> super::Result<()> {
    // create in memory thumbnail
    let image = image::load_from_memory(&buf)?;



@@ 89,7 88,7 @@ pub fn process_image(content_store: &str, digest: &str, buf: &[u8]) -> Processin
        .join(thumb_filename);

    // write thumbnail file to disk
    let bucket_dir = dest.parent().ok_or(ProcessingError::ThumbnailFailed)?;
    let bucket_dir = dest.parent().ok_or(super::Error::ThumbnailFailed)?;
    fs::create_dir_all(bucket_dir)?;
    let mut dest_file = File::create(&dest)?;
    thumb.save(&mut dest_file, image::ImageFormat::JPEG)?;

M src/util/processing/mod.rs => src/util/processing/mod.rs +35 -36
@@ 6,7 6,8 @@ use serde_json;
use std::{self, fmt, io};
use std::borrow::Borrow;
use std::convert::From;
use std::error::Error;
use std::error::Error as StdError;
use std::result::Result as StdResult;
use std::fs::File;
use std::io::Read;
use std::path::Path;


@@ 21,7 22,7 @@ pub use self::video_detector::ffmpeg_detect as detect_video;
pub use self::video_detector::process_video as thumb_video;

/// Reads a file from the specified path and returns its SHA256 digest.
pub fn hash_file(path: &Path) -> ProcessingResult<String> {
pub fn hash_file(path: &Path) -> Result<String> {
    let mut buf = vec![];

    info!("path exists? {}",  (path.borrow()).exists());


@@ 40,12 41,10 @@ pub fn hash_file(path: &Path) -> ProcessingResult<String> {
    Ok(digest)
}



pub type ProcessingResult<T> = Result<T, ProcessingError>;
pub type Result<T> = StdResult<T, Error>;

#[derive(Debug)]
pub enum ProcessingError {
pub enum Error {
    DigestFailed,
    DetectionFailed,
    ThumbnailFailed,


@@ 54,40 53,40 @@ pub enum ProcessingError {
    DbQueryErr(diesel::result::Error),
    IoErr(io::Error),
    ImageErr(image::ImageError),
    Misc(Box<Error>),
    Misc(Box<StdError>),
}

impl Error for ProcessingError {
impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            // internal errors
            ProcessingError::DigestFailed      => "Unhandled error while generating SHA256 digest",
            ProcessingError::DetectionFailed   => "The file's type could not be detected",
            ProcessingError::ThumbnailFailed   => "The thumbnail could not be generated",
            Error::DigestFailed      => "Unhandled error while generating SHA256 digest",
            Error::DetectionFailed   => "The file's type could not be detected",
            Error::ThumbnailFailed   => "The thumbnail could not be generated",

            // external errors
            ProcessingError::DbConnErr(ref inner)  => inner.description(),
            ProcessingError::DbQueryErr(ref inner) => inner.description(),
            ProcessingError::IoErr(ref inner)      => inner.description(),
            ProcessingError::ImageErr(ref inner)   => inner.description(),
            ProcessingError::Misc(ref inner)       => inner.description(),
            Error::DbConnErr(ref inner)  => inner.description(),
            Error::DbQueryErr(ref inner) => inner.description(),
            Error::IoErr(ref inner)      => inner.description(),
            Error::ImageErr(ref inner)   => inner.description(),
            Error::Misc(ref inner)       => inner.description(),
        }
    }

    fn cause(&self) -> Option<&Error> {
    fn cause(&self) -> Option<&StdError> {
        match *self {
            ProcessingError::DbConnErr(ref err)  => Some(err),
            ProcessingError::DbQueryErr(ref err) => Some(err),
            ProcessingError::IoErr(ref err)      => Some(err),
            ProcessingError::ImageErr(ref err)   => Some(err),
            ProcessingError::Misc(ref err)       => Some(err.as_ref()),
            Error::DbConnErr(ref err)  => Some(err),
            Error::DbQueryErr(ref err) => Some(err),
            Error::IoErr(ref err)      => Some(err),
            Error::ImageErr(ref err)   => Some(err),
            Error::Misc(ref err)       => Some(err.as_ref()),
            _ => None,
        }
    }
}

impl fmt::Display for ProcessingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
        match *self {
            // TODO: better display impl.
            _ => write!(f, "{}", self.description()),


@@ 95,26 94,26 @@ impl fmt::Display for ProcessingError {
    }
}

impl From<diesel::ConnectionError> for ProcessingError {
    fn from(err: diesel::ConnectionError) -> Self { ProcessingError::DbConnErr(err) }
impl From<diesel::ConnectionError> for Error {
    fn from(err: diesel::ConnectionError) -> Self { Error::DbConnErr(err) }
}

impl From<diesel::result::Error> for ProcessingError {
    fn from(err: diesel::result::Error) -> Self { ProcessingError::DbQueryErr(err) }
impl From<diesel::result::Error> for Error {
    fn from(err: diesel::result::Error) -> Self { Error::DbQueryErr(err) }
}

impl From<image::ImageError> for ProcessingError {
    fn from(err: image::ImageError) -> Self { ProcessingError::ImageErr(err) }
impl From<image::ImageError> for Error {
    fn from(err: image::ImageError) -> Self { Error::ImageErr(err) }
}

impl From<io::Error> for ProcessingError {
    fn from(err: io::Error) -> Self { ProcessingError::IoErr(err) }
impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self { Error::IoErr(err) }
}

impl From<serde_json::Error> for ProcessingError {
    fn from(err: serde_json::Error) -> Self { ProcessingError::Misc(Box::new(err)) }
impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self { Error::Misc(Box::new(err)) }
}

impl From<std::string::FromUtf8Error> for ProcessingError {
    fn from(err: std::string::FromUtf8Error) -> Self { ProcessingError::Misc(Box::new(err)) }
impl From<std::string::FromUtf8Error> for Error {
    fn from(err: std::string::FromUtf8Error) -> Self { Error::Misc(Box::new(err)) }
}

M src/util/processing/video_detector.rs => src/util/processing/video_detector.rs +5 -6
@@ 2,7 2,6 @@ use serde_json;
use std::{fs, process};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use super::ProcessingError;

#[derive(Debug, Serialize, Deserialize)]
pub struct FFProbeResult {


@@ 43,7 42,7 @@ pub struct FFProbeMeta {
/// The container format is then mapped to a common mime & extension which is used
/// by other parts of the `aqua` application suite to determine how an asset should
/// be displayed.
pub fn ffmpeg_detect(path: &Path) -> Result<Option<FFProbeMeta>, ProcessingError> {
pub fn ffmpeg_detect(path: &Path) -> super::Result<Option<FFProbeMeta>> {
    let ffprobe_cmd = process::Command::new("ffprobe")
        .arg("-v").arg("quiet")            // silence debug output
        .arg("-hide_banner")               // don't print ffmpeg configuration


@@ 77,7 76,7 @@ pub fn ffmpeg_detect(path: &Path) -> Result<Option<FFProbeMeta>, ProcessingError
        .filter(|el| el.codec_type == "video")
        .count();

    if number_of_videos <= 0 { return Err(ProcessingError::DetectionFailed) }
    if number_of_videos <= 0 { return Err(super::Error::DetectionFailed) }

    // TODO: how do we correlate format_name w/ stream & stream position?
    // TODO: I believe this should be matching on containers (which is what will be moved


@@ 115,7 114,7 @@ fn is_webm(streams: &[FFProbeStream]) -> bool {
/// The destination of the thumbnail is `content_store/<digest bucket>/<digest>.thumbnail`
/// The bucket is used by taking the first byte (two hexadecimal characters) off the digest
/// and prefixing that with a `t` to designate that it is a thumbnail bucket.
pub fn process_video(content_store: &str, digest: &str, src: &Path) -> super::ProcessingResult<()> {
pub fn process_video(content_store: &str, digest: &str, src: &Path) -> super::Result<()> {
    let thumb_bucket   = format!("t{}", &digest[0..2]);
    let thumb_filename = format!("{}.thumbnail", &digest);



@@ 128,7 127,7 @@ pub fn process_video(content_store: &str, digest: &str, src: &Path) -> super::Pr
    //       JPEG muxer/encoder in my ffmpeg install ...
    //
    // write thumbnail file to disk
    let bucket_dir = dest.parent().ok_or(ProcessingError::ThumbnailFailed)?;
    let bucket_dir = dest.parent().ok_or(super::Error::ThumbnailFailed)?;
    fs::create_dir_all(bucket_dir)?;
    let ffmpeg_cmd = process::Command::new("ffmpeg")
        .arg("-i").arg(src.as_os_str())            // the input file


@@ 146,6 145,6 @@ pub fn process_video(content_store: &str, digest: &str, src: &Path) -> super::Pr

    match dest.is_file() {
        true  => Ok(()),
        false => Err(ProcessingError::ThumbnailFailed),
        false => Err(super::Error::ThumbnailFailed),
    }
}