julid-rs 0.0.1

A library and loadable extension for SQLite that uses it, that provides Joe's ULIDs.
Documentation
use sqlite_loadable::{
    api, define_scalar_function,
    prelude::{
        c_char, c_uint, sqlite3, sqlite3_api_routines, sqlite3_context, sqlite3_value,
        FunctionFlags,
    },
    Result,
};

mod base32;
pub mod julid;

pub use julid::Julid;

//-************************************************************************
// Entrypoint into the loadable extension
//-************************************************************************
#[no_mangle]
pub unsafe extern "C" fn sqlite3_julid_init(
    db: *mut sqlite3,
    _pz_err_msg: *mut *mut c_char,
    p_api: *mut sqlite3_api_routines,
) -> c_uint {
    unsafe { sqlite_loadable::ext::faux_sqlite_extension_init2(p_api) }
    match init_rs(db) {
        Ok(()) => 256, // SQLITE_OK_LOAD_PERMANENTLY
        Err(err) => err.code_extended(),
    }
}

fn init_rs(db: *mut sqlite3) -> Result<()> {
    let flags = FunctionFlags::UTF8 | FunctionFlags::DETERMINISTIC;
    define_scalar_function(db, "julid_new", 0, julid_new, FunctionFlags::INNOCUOUS)?;
    define_scalar_function(db, "julid_timestamp", 1, julid_timestamp, flags)?;
    define_scalar_function(db, "julid_counter", 1, julid_counter, flags)?;
    define_scalar_function(db, "julid_sortable", 1, julid_sortable, flags)?;
    define_scalar_function(db, "julid_string", 1, julid_string, flags)?;

    Ok(())
}

//-************************************************************************
// impls
//-************************************************************************
fn julid_new(context: *mut sqlite3_context, _vals: &[*mut sqlite3_value]) -> Result<()> {
    api::result_blob(context, Julid::new().to_bytes().as_slice());
    Ok(())
}

fn julid_timestamp(context: *mut sqlite3_context, id: &[*mut sqlite3_value]) -> Result<()> {
    if let Some(value) = id.get(0) {
        let id = api::value_blob(value);
        let bytes: [u8; 16] = id.try_into().map_err(|_| {
            sqlite_loadable::Error::new_message("Could not convert given value to Julid")
        })?;
        let id: Julid = bytes.into();
        api::result_int64(context, id.timestamp() as i64);
    } else {
        return Err(sqlite_loadable::Error::new_message(
            "Could not get timestamp for empty Julid",
        ));
    }

    Ok(())
}

fn julid_counter(context: *mut sqlite3_context, id: &[*mut sqlite3_value]) -> Result<()> {
    if let Some(value) = id.get(0) {
        let id = api::value_blob(value);
        let bytes: [u8; 16] = id.try_into().map_err(|_| {
            sqlite_loadable::Error::new_message("Could not convert given value to Julid")
        })?;
        let id: Julid = bytes.into();
        api::result_int64(context, id.counter() as i64);
    } else {
        return Err(sqlite_loadable::Error::new_message(
            "Could not get counter value for empty Julid",
        ));
    }

    Ok(())
}

fn julid_sortable(context: *mut sqlite3_context, id: &[*mut sqlite3_value]) -> Result<()> {
    if let Some(value) = id.get(0) {
        let id = api::value_blob(value);
        let bytes: [u8; 16] = id.try_into().map_err(|_| {
            sqlite_loadable::Error::new_message("Could not convert given value to Julid")
        })?;
        let id: Julid = bytes.into();
        api::result_int64(context, id.sortable() as i64);
    } else {
        return Err(sqlite_loadable::Error::new_message(
            "Could not get sortable bits for empty Julid",
        ));
    }

    Ok(())
}

fn julid_string(context: *mut sqlite3_context, id: &[*mut sqlite3_value]) -> Result<()> {
    if let Some(value) = id.get(0) {
        let id = api::value_blob(value);
        let bytes: [u8; 16] = id.try_into().map_err(|_| {
            sqlite_loadable::Error::new_message("Could not convert given value to Julid")
        })?;
        let id: Julid = bytes.into();
        api::result_text(context, id.as_string())?;
    } else {
        return Err(sqlite_loadable::Error::new_message(
            "Could not convert empty Julid to string",
        ));
    }

    Ok(())
}