vcard 0.4.11

A pure Rust implementation of vCard based on RFC 6350.
Documentation
use super::super::values::uri::URI;
use super::*;

use std::fmt::Display;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;

use validators::base64::Base64;
use validators::{Validated, ValidatedWrapper};

use base64_stream::ToBase64Reader;
use mime::Mime;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[allow(clippy::upper_case_acronyms)]
enum ImageValueInner {
    Base64(Mime, Base64),
    URI(URI),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ImageValue {
    inner: ImageValueInner,
}

#[derive(Debug)]
pub enum ImageValueError {
    FileMediaTypeCannotBeDefined,
    MediaTypeNotImage,
    IOError(io::Error),
}

impl From<io::Error> for ImageValueError {
    #[inline]
    fn from(err: io::Error) -> Self {
        ImageValueError::IOError(err)
    }
}

impl ImageValue {
    pub fn from_base64(mime_type: Mime, base64: Base64) -> Result<ImageValue, ImageValueError> {
        if mime_type.type_() != mime::IMAGE {
            return Err(ImageValueError::MediaTypeNotImage);
        }

        Ok(ImageValue {
            inner: ImageValueInner::Base64(mime_type, base64),
        })
    }

    pub fn from_uri(uri: URI) -> ImageValue {
        ImageValue {
            inner: ImageValueInner::URI(uri),
        }
    }

    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<ImageValue, ImageValueError> {
        Self::from_file_inner(path, None)
    }

    pub fn from_file_with_mime<P: AsRef<Path>>(
        path: P,
        mime_type: Mime,
    ) -> Result<ImageValue, ImageValueError> {
        Self::from_file_inner(path, Some(mime_type))
    }

    fn from_file_inner<P: AsRef<Path>>(
        path: P,
        mime_type: Option<Mime>,
    ) -> Result<ImageValue, ImageValueError> {
        let path = path.as_ref();

        let mime_type = match mime_type {
            Some(image_type) => image_type,
            None => {
                match path.extension() {
                    Some(ext) => {
                        match ext.to_str() {
                            Some(ext) => {
                                let mime_type = mime_guess::from_ext(ext).first_or_octet_stream();

                                if mime_type.type_() != mime::IMAGE {
                                    return Err(ImageValueError::MediaTypeNotImage);
                                }

                                mime_type
                            }
                            None => {
                                return Err(ImageValueError::FileMediaTypeCannotBeDefined);
                            }
                        }
                    }
                    None => {
                        return Err(ImageValueError::FileMediaTypeCannotBeDefined);
                    }
                }
            }
        };

        let mut reader = ToBase64Reader::new(File::open(path)?);

        let mut base64_raw = Vec::new();

        reader.read_to_end(&mut base64_raw)?;

        let base64 = unsafe { String::from_utf8_unchecked(base64_raw) };

        let base64 = unsafe { Base64::from_string_unchecked(base64) };

        Ok(ImageValue {
            inner: ImageValueInner::Base64(mime_type, base64),
        })
    }
}

impl Value for ImageValue {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        match &self.inner {
            ImageValueInner::Base64(typ, base64) => {
                f.write_str("data:")?;
                f.write_str(typ.as_ref())?;
                f.write_str(";base64,")?;
                f.write_str(base64.get_base64())?;
            }
            ImageValueInner::URI(uri) => {
                f.write_str(uri.get_full_uri())?;
            }
        }

        Ok(())
    }
}

impl Display for ImageValue {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        Value::fmt(self, f)
    }
}

impl Validated for ImageValue {}

impl ValidatedWrapper for ImageValue {
    type Error = &'static str;

    fn from_string(_from_string_input: String) -> Result<Self, Self::Error> {
        unimplemented!();
    }

    fn from_str(_from_str_input: &str) -> Result<Self, Self::Error> {
        unimplemented!();
    }
}