pub mod dl;
pub mod ffi;
pub mod folder;

use extension::dl::Dl;

use libc::c_void;

use std::ffi::CStr;
use std::mem;

aggregate_errors!(
    ExtensionResolveError {
        MissingSymbolError(MissingSymbolError),
    }
);

unit_errors!(
    MissingSymbolError = "Missing symbol",
);

pub struct Extension {
    _dl: Dl,
    // BUG: This is unsafe. These should be methods so they cannot be retained
    //      after self has been dropped.
    pub folder_initialize: ffi::FolderInitialize,
    pub folder_step: ffi::FolderStep,
    pub folder_finalize: ffi::FolderFinalize,
    pub folder_free: ffi::FolderFree,
}

impl Extension {
    pub fn resolve(dl: Dl) -> Result<Self, ExtensionResolveError> {
        unsafe {
            let folder_initialize = mem::transmute(resolve(&dl, "leek_extension_folder_initialize\0")?);
            let folder_step       = mem::transmute(resolve(&dl, "leek_extension_folder_step\0")?);
            let folder_finalize   = mem::transmute(resolve(&dl, "leek_extension_folder_finalize\0")?);
            let folder_free       = mem::transmute(resolve(&dl, "leek_extension_folder_free\0")?);
            Ok(Extension{_dl: dl, folder_initialize, folder_step,
                         folder_finalize, folder_free})
        }
    }
}

unsafe fn resolve(dl: &Dl, symbol: &str) -> Result<*mut c_void, ExtensionResolveError> {
    let symbol = CStr::from_bytes_with_nul_unchecked(symbol.as_bytes());
    match dl.resolve(symbol) {
        None => Err(ExtensionResolveError::MissingSymbolError(MissingSymbolError)),
        Some(value) => Ok(value.get()),
    }
}
