libopenraw 0.1.2

Rust API bindings for libopenraw
Documentation
/*
 * libopenraw-rs
 *
 * Copyright (C) 2021-2022 Hubert Figuière
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

pub mod error;
pub mod ifd;
pub mod rawfile;
pub mod thumbnail;
pub mod utils;

use std::ffi::CStr;
use std::ffi::OsString;

use libopenraw_sys as ffi;

pub type Result<T> = std::result::Result<T, error::Error>;
pub use error::Error;
pub use ifd::Ifd;
pub use rawfile::{RawFile, RawFileType};
pub use thumbnail::Thumbnail;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
/// Data types
pub enum DataType {
    /// None
    None,
    /// 8bits RGB pixmap
    Pixmap8Rgb,
    /// 8bits RGB pixmap
    Pixmap16Rgb,
    /// JPEG
    Jpeg,
    /// TIFF
    Tiff,
    /// PNG
    Png,
    /// RAW (decompressed)
    Raw,
    /// RAW compressed. Can be any compression.
    CompressedRaw,
    /// Unknown
    Unknown,
}

impl From<ffi::or_data_type> for DataType {
    fn from(t: ffi::or_data_type) -> Self {
        use ffi::or_data_type::*;
        match t {
            OR_DATA_TYPE_NONE => Self::None,
            OR_DATA_TYPE_PIXMAP_8RGB => Self::Pixmap8Rgb,
            OR_DATA_TYPE_PIXMAP_16RGB => Self::Pixmap16Rgb,
            OR_DATA_TYPE_JPEG => Self::Jpeg,
            OR_DATA_TYPE_TIFF => Self::Tiff,
            OR_DATA_TYPE_PNG => Self::Png,
            OR_DATA_TYPE_RAW => Self::Raw,
            OR_DATA_TYPE_COMPRESSED_RAW => Self::CompressedRaw,
            OR_DATA_TYPE_UNKNOWN => Self::Unknown,
        }
    }
}

impl From<DataType> for ffi::or_data_type {
    fn from(t: DataType) -> Self {
        use ffi::or_data_type::*;
        match t {
            DataType::None => OR_DATA_TYPE_NONE,
            DataType::Pixmap8Rgb => OR_DATA_TYPE_PIXMAP_8RGB,
            DataType::Pixmap16Rgb => OR_DATA_TYPE_PIXMAP_16RGB,
            DataType::Jpeg => OR_DATA_TYPE_JPEG,
            DataType::Tiff => OR_DATA_TYPE_TIFF,
            DataType::Png => OR_DATA_TYPE_PNG,
            DataType::Raw => OR_DATA_TYPE_RAW,
            DataType::CompressedRaw => OR_DATA_TYPE_COMPRESSED_RAW,
            DataType::Unknown => OR_DATA_TYPE_UNKNOWN,
        }
    }
}

/// Extract thumbnail from `filename` with `preferred_size`,
/// eventually mutating `thumbnail`.
///
/// Return the extracted thumbnail.
pub fn extract_thumbnail<P: AsRef<std::path::Path>>(
    filename: P,
    preferred_size: u32,
    thumbnail: Option<Thumbnail>,
) -> Result<Thumbnail> {
    let mut thumbnail = thumbnail.unwrap_or_else(Thumbnail::new);
    let cstr = crate::utils::path_to_filename(filename);
    let err =
        unsafe { ffi::or_get_extract_thumbnail(cstr.as_ptr(), preferred_size, &mut thumbnail.0) };
    if err == ffi::or_error::OR_ERROR_NONE {
        Ok(thumbnail)
    } else {
        Err(err.into())
    }
}

lazy_static::lazy_static! {
    /// Static content from the library.
    static ref EXTENSIONS: Vec<OsString> = {
        let mut v = vec![];
        let extensions = unsafe { ffi::or_get_file_extensions() };
        let mut current = extensions;
        if !current.is_null() {
            // Safety: we checked the pointer is NOT null
            while unsafe { !(*current).is_null() } {
                let s = unsafe { CStr::from_ptr(*current) };
                v.push(OsString::from(&*s.to_string_lossy()));
                current = unsafe { current.offset(1) };
            }
        }
        v
    };
}

/// Get the file extensions that libopenraw recognizes.
/// Return a slice of [`OsString`], since the expectation is to
/// compare with the result of [`Path::extension()`].
///
/// Example to list all the extensions:
/// ```
/// use libopenraw::file_extensions;
///
/// let extensions = file_extensions();
/// for ext in extensions {
///   println!("Extension: {:?}", ext);
/// }
/// ```
pub fn file_extensions() -> &'static [OsString] {
    &EXTENSIONS
}

#[cfg(test)]
mod test {
    use super::file_extensions;

    #[test]
    fn test_extensions() {
        let extensions = file_extensions();
        assert_eq!(extensions.len(), 17);
    }
}