use crate::error::{DaosError, Result};
use crate::pool::Pool;
use crate::runtime::require_runtime;
use crate::unsafe_inner::ffi::{
daos_cont_alloc_oids, daos_cont_close, daos_cont_create_with_label, daos_cont_open,
daos_cont_query,
};
use crate::unsafe_inner::handle::DaosHandle;
use daos::daos_cont_info_t;
pub mod flags {
pub const CONT_OPEN_RO: u32 = daos::DAOS_COO_RO;
pub const CONT_OPEN_RW: u32 = daos::DAOS_COO_RW;
pub const CONT_OPEN_EX: u32 = daos::DAOS_COO_EX;
pub const CONT_OPEN_FORCE: u32 = daos::DAOS_COO_FORCE;
}
#[derive(Debug, Clone, Copy)]
pub enum ContainerOpen {
ByLabel,
ByUuid,
}
#[derive(Debug)]
pub struct Container<'p> {
#[allow(dead_code)]
pool: &'p Pool,
handle: Option<DaosHandle>,
}
impl<'p> Container<'p> {
fn new(pool: &'p Pool, handle: DaosHandle) -> Self {
Self {
pool,
handle: Some(handle),
}
}
#[inline]
pub(crate) fn as_handle(&self) -> Result<DaosHandle> {
self.handle.ok_or(DaosError::InvalidArg)
}
pub fn close(&mut self) -> Result<()> {
if let Some(handle) = self.handle.take() {
daos_cont_close(handle)
} else {
Err(DaosError::InvalidArg)
}
}
pub fn query(&self) -> Result<ContainerInfo> {
let handle = self.as_handle()?;
let info = daos_cont_query(handle)?;
Ok(ContainerInfo::from_daos_cont_info_t(info))
}
pub fn alloc_oids(&self, num_oids: u64) -> Result<u64> {
let handle = self.as_handle()?;
daos_cont_alloc_oids(handle, num_oids)
}
}
impl Drop for Container<'_> {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
if let Err(e) = daos_cont_close(handle) {
eprintln!(
"Container::drop: daos_cont_close() failed with {:?}, continuing with drop anyway",
e
);
}
}
}
}
#[derive(Debug, Clone)]
pub struct ContainerInfo {
pub uuid: [u8; 16],
pub snapshot_epoch: u64,
pub num_handles: u32,
pub num_snapshots: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CreateError {
InvalidArg,
Permission,
Busy,
Unknown(i32),
}
impl From<DaosError> for CreateError {
fn from(err: DaosError) -> Self {
match err {
DaosError::InvalidArg => CreateError::InvalidArg,
DaosError::Permission => CreateError::Permission,
DaosError::Busy => CreateError::Busy,
DaosError::Unknown(code) => CreateError::Unknown(code),
_ => CreateError::InvalidArg,
}
}
}
#[derive(Debug, Default)]
pub struct ContainerBuilder {
label: Option<String>,
uuid: Option<String>,
}
impl ContainerBuilder {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
#[inline]
pub fn uuid(mut self, uuid: impl Into<String>) -> Self {
self.uuid = Some(uuid.into());
self
}
fn validate(&self) -> Result<()> {
match (&self.label, &self.uuid) {
(Some(_), Some(_)) => Err(DaosError::InvalidArg),
(None, None) => Err(DaosError::InvalidArg),
_ => Ok(()),
}
}
pub fn build(self, pool: &Pool) -> Result<Container<'_>> {
require_runtime()?;
self.validate()?;
let pool_handle = pool.as_handle().ok_or(DaosError::InvalidArg)?;
let label = self.label.as_deref();
if let Some(label_str) = label {
daos_cont_create_with_label(pool_handle, label_str, None)?;
let handle = daos_cont_open(pool_handle, label_str, flags::CONT_OPEN_RW)?;
Ok(Container::new(pool, handle))
} else {
Err(DaosError::InvalidArg)
}
}
}
impl ContainerInfo {
fn from_daos_cont_info_t(info: daos_cont_info_t) -> Self {
let uuid = unsafe {
let mut uuid = [0u8; 16];
std::ptr::copy_nonoverlapping(info.ci_uuid.as_ptr(), uuid.as_mut_ptr(), 16);
uuid
};
Self {
uuid,
snapshot_epoch: info.ci_lsnapshot,
num_handles: info.ci_nhandles,
num_snapshots: info.ci_nsnapshots,
}
}
}
impl Pool {
pub fn create_container(&self, label: &str) -> Result<Container<'_>> {
require_runtime()?;
let pool_handle = self.as_handle().ok_or(DaosError::InvalidArg)?;
daos_cont_create_with_label(pool_handle, label, None)?;
let handle = daos_cont_open(pool_handle, label, flags::CONT_OPEN_RW)?;
Ok(Container::new(self, handle))
}
pub fn open_container(
&self,
identifier: &str,
open_by: ContainerOpen,
flags: u32,
) -> Result<Container<'_>> {
require_runtime()?;
let pool_handle = self.as_handle().ok_or(DaosError::InvalidArg)?;
if identifier.is_empty() {
return Err(DaosError::InvalidArg);
}
match open_by {
ContainerOpen::ByLabel => {}
ContainerOpen::ByUuid => {
}
}
let handle = daos_cont_open(pool_handle, identifier, flags)?;
Ok(Container::new(self, handle))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_container_builder_default() {
let builder = ContainerBuilder::new();
assert!(builder.label.is_none());
assert!(builder.uuid.is_none());
}
#[test]
fn test_container_builder_with_label() {
let builder = ContainerBuilder::new().label("mycontainer");
assert_eq!(builder.label.as_deref(), Some("mycontainer"));
}
#[test]
fn test_container_builder_with_uuid() {
let builder = ContainerBuilder::new().uuid("12345678-1234-1234-1234-123456789012");
assert_eq!(
builder.uuid.as_deref(),
Some("12345678-1234-1234-1234-123456789012")
);
}
#[test]
fn test_container_builder_validate_neither() {
let builder = ContainerBuilder::new();
let result = builder.validate();
assert!(result.is_err());
}
#[test]
fn test_container_builder_validate_both() {
let builder = ContainerBuilder::new()
.label("mycontainer")
.uuid("12345678-1234-1234-1234-123456789012");
let result = builder.validate();
assert!(result.is_err());
}
#[test]
fn test_container_builder_validate_label_only() {
let builder = ContainerBuilder::new().label("mycontainer");
assert!(builder.validate().is_ok());
}
#[test]
fn test_container_flags_constants() {
assert_eq!(flags::CONT_OPEN_RO, 1);
assert_eq!(flags::CONT_OPEN_RW, 2);
assert_eq!(flags::CONT_OPEN_EX, 4);
assert_eq!(flags::CONT_OPEN_FORCE, 8);
}
#[test]
fn test_container_open_by_enum() {
assert!(matches!(ContainerOpen::ByLabel, ContainerOpen::ByLabel));
assert!(matches!(ContainerOpen::ByUuid, ContainerOpen::ByUuid));
}
#[test]
fn test_require_runtime_error_when_not_init() {
while crate::runtime::is_runtime_initialized() {
drop(crate::runtime::DaosRuntime::new());
}
let pool = Pool::from_handle(unsafe {
crate::unsafe_inner::handle::DaosHandle::from_raw(daos::daos_handle_t { cookie: 12345 })
});
let result = pool.create_container("mycontainer");
assert!(result.is_err());
}
#[test]
fn test_close_without_handle() {
let pool = Pool::from_handle(unsafe {
crate::unsafe_inner::handle::DaosHandle::from_raw(daos::daos_handle_t { cookie: 12345 })
});
let mut container = Container::new(&pool, unsafe {
crate::unsafe_inner::handle::DaosHandle::from_raw(daos::daos_handle_t { cookie: 0 })
});
let result = container.close();
assert!(result.is_err());
}
#[test]
fn test_container_info_debug() {
let info = ContainerInfo {
uuid: [0x12, 0x34, 0x56, 0x78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
snapshot_epoch: 0,
num_handles: 0,
num_snapshots: 0,
};
let debug_str = format!("{:?}", info);
assert!(debug_str.contains("ContainerInfo"));
}
}