elektra 0.9.10

Elektra serves as a universal and secure framework to access configuration parameters in a global, hierarchical key database.
use crate::{ReadableKey, KeyNameInvalidError, KeyNameReadOnlyError};
use std::convert::TryInto;
use std::ffi::CString;

pub trait WriteableKey: ReadableKey {
    /// The value that is passed to set_value.
    type SetValue;

    /// Returns the raw pointer of the key.
    /// Should be used with caution. In particular,
    /// the pointer should only be modified with
    /// `elektra_sys::key*` functions, but `keyDel` 
    /// should not be called. 
    /// 
    /// You can use it to call functions in the raw bindings
    /// that modify the key, if the safe API doesn't fulfill your usecase.
    /// 
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # use elektra_sys;
    /// # use std::ffi::CString;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new("user:/test/key")?;
    /// let cstr = CString::new("newbasename").unwrap();
    /// let ret_val = unsafe { elektra_sys::keySetBaseName(key.as_ptr(), cstr.as_ptr()) };
    /// assert_eq!(key.name(), "user:/test/newbasename");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn as_ptr(&mut self) -> *mut elektra_sys::Key;

    /// Set the value of the key.
    /// 
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new_empty();
    /// key.set_value("rust");
    /// assert_eq!(key.value(), "rust");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn set_value(&mut self, value: Self::SetValue)
    where
        Self: Sized;

    /// Construct a new key with a name.
    ///
    /// # Panics
    /// Panics if an allocation error (out of memory) occurs in the C-constructor.
    fn new(name: &str) -> Result<Self, KeyNameInvalidError>
    where
        Self: Sized,
    {
        let mut key = Self::new_empty();
        key.set_name(name)?;
        Ok(key)
    }

    /// Construct a new key with name "/".
    /// 
    /// # Panics
    /// Panics if an allocation error (out of memory) occurs in the C-constructor.
    fn new_empty() -> Self
    where
        Self: Sized,
    {
        let cstr = CString::new("/").unwrap();
        let key_ptr = unsafe { elektra_sys::keyNew(cstr.as_ptr(), elektra_sys::KEY_END) };
        unsafe { Self::from_ptr(key_ptr) }
    }

    /// Increment the viability of a key object.
    /// Returns the value of the new reference counter.
    /// 
    /// # Notes
    /// This function is unsafe, since forgetting to call `dec_ref`
    /// after a call to `inc_ref` results in a memory leak. 
    /// It is preferable to use duplicate instead.
    /// 
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key2;
    /// {
    ///     let mut key = StringKey::new("user:/test/language")?;
    ///     unsafe { key.inc_ref(); }
    ///     key2 = unsafe { StringKey::from_ptr(key.as_ptr()) };
    /// } // <- key is dropped here, but key2 can still be used
    /// assert_eq!(key2.name(), "user:/test/language");
    /// // Forgetting to call this method would leak memory.
    /// unsafe { key2.dec_ref(); }
    /// #     Ok(())
    /// # }
    /// ```
    unsafe fn inc_ref(&mut self) -> u16 {
        elektra_sys::keyIncRef(self.as_ptr())
    }

    /// Decrement the viability of a key object.
    /// Returns the value of the new reference counter.
    unsafe fn dec_ref(&mut self) -> u16 {
        elektra_sys::keyDecRef(self.as_ptr())
    }

    /// Clears the key.
    /// After this call you will receive a fresh key.
    fn clear(&mut self) {
        unsafe {
            elektra_sys::keyClear(self.as_ptr());
        }
    }

    /// Set the name of a key. Must adhere to the rules for keynames otherwise a `KeyNameInvalidError` is returned.
    /// Note that the error could also represent a KeyNameReadOnlyError, but the C-API doesn't allow for the distinction,
    /// so only one error can be returned.
    /// Returns the size in bytes of this new key name including the ending NUL.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new_empty();
    /// key.set_name("user:/test/rust")?;
    /// assert_eq!(key.name(), "user:/test/rust");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    /// 
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn set_name(&mut self, name: &str) -> Result<u32, KeyNameInvalidError> {
        let cstr = CString::new(name).unwrap();
        let ret_val = unsafe { elektra_sys::keySetName(self.as_ptr(), cstr.as_ptr()) };

        if ret_val > 0 {
            Ok(ret_val as u32)
        } else {
            // This error is either InvalidName or NameReadOnly, but error codes are equal.
            Err(KeyNameInvalidError::new(name.to_owned()))
        }
    }

    /// Set the basename of the key
    /// Returns a `KeyNameReadOnlyError` if the key is part of a keyset.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new("user:/test/key")?;
    /// key.set_basename("rust")?;
    /// assert_eq!(key.name(), "user:/test/rust");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn set_basename(&mut self, basename: &str) -> Result<(), KeyNameReadOnlyError> {
        let cstr = CString::new(basename).unwrap();
        let ret_val = unsafe { elektra_sys::keySetBaseName(self.as_ptr(), cstr.as_ptr()) };
        if ret_val == -1 {
            Err(KeyNameReadOnlyError::new(basename.to_owned()))
        } else {
            Ok(())
        }
    }

    /// Add a basename to the key
    /// Returns a `KeyNameReadOnlyError` if the key is part of a keyset.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new("user:/test/key")?;
    /// key.add_basename("rust")?;
    /// assert_eq!(key.name(), "user:/test/key/rust");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn add_basename(&mut self, basename: &str) -> Result<(), KeyNameReadOnlyError> {
        let cstr = CString::new(basename).unwrap();
        let ret_val = unsafe { elektra_sys::keyAddBaseName(self.as_ptr(), cstr.as_ptr()) };
        if ret_val == -1 {
            Err(KeyNameReadOnlyError::new(basename.to_owned()))
        } else {
            Ok(())
        }
    }

    /// Add an already escaped name to the keyname.
    /// Returns an `KeyNameInvalidError` if the name is not a valid escaped name.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new("user:/x/r").unwrap();
    /// key.add_name("../y/a//././z").unwrap();
    /// assert_eq!(key.name(), "user:/x/y/a/z");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn add_name(&mut self, name: &str) -> Result<(), KeyNameInvalidError> {
        let cstr = CString::new(name).unwrap();
        let ret_val = unsafe { elektra_sys::keyAddName(self.as_ptr(), cstr.as_ptr()) };
        if ret_val <= 0 {
            Err(KeyNameInvalidError::new(name.to_owned()))
        } else {
            Ok(())
        }
    }

    /// Copies all metadata from source to the self.
    ///
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new("user:/test/mykey")?;
    /// let mut key2 = StringKey::new("user:/test/mykey")?;
    /// key.set_meta("rusty", "metal");
    /// key2.copy_all_meta(&key);
    /// assert_eq!(key.meta("rusty")?.value(), "metal");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn copy_all_meta(&mut self, source: &Self)
    where
        Self: Sized,
    {
        unsafe {
            elektra_sys::keyCopyAllMeta(self.as_ptr(), source.as_ref());
        }
    }

    /// Copy metakey with name metaname from source to self.
    /// 
    /// # Examples
    /// ```
    /// use elektra::{StringKey,WriteableKey,ReadableKey};
    /// let mut key = StringKey::new_empty();
    /// key.set_meta("meta", "value");
    /// let mut key2 = StringKey::new_empty();
    /// key2.copy_meta(&key, "meta");
    /// assert_eq!(key2.meta("meta").unwrap().value(), "value");
    /// ```
    ///
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn copy_meta(&mut self, source: &Self, metaname: &str) -> i32
    where
        Self: Sized,
    {
        let cstr = CString::new(metaname).unwrap();
        unsafe { elektra_sys::keyCopyMeta(self.as_ptr(), source.as_ref(), cstr.as_ptr()) }
    }

    /// Set a new meta-information.
    /// Returns the size of the new meta information on success,
    /// or a `KeyNameInvalidError` if the name is invalid.
    ///
    /// # Panics
    /// Panics if any of the provided strings contains interior nul bytes.
    fn set_meta(&mut self, metaname: &str, metavalue: &str) -> Result<usize, KeyNameInvalidError> {
        let name = CString::new(metaname).unwrap();
        let value = CString::new(metavalue).unwrap();
        let ret_val =
            unsafe { elektra_sys::keySetMeta(self.as_ptr(), name.as_ptr(), value.as_ptr()) };
        if ret_val < 0 {
            Err(KeyNameInvalidError::new(metaname.to_owned()))
        } else {
            Ok(ret_val.try_into().unwrap())
        }
    }

    /// Delete the metadata at metaname
    /// Returns the size of the new meta information on success,
    /// or a `KeyNameInvalidError` if the name is invalid or out of memory.
    ///
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn delete_meta(&mut self, metaname: &str) -> Result<usize, KeyNameInvalidError> {
        let name = CString::new(metaname).unwrap();
        let ret_val =
            unsafe { elektra_sys::keySetMeta(self.as_ptr(), name.as_ptr(), std::ptr::null()) };
        if ret_val < 0 {
            Err(KeyNameInvalidError::new(metaname.to_owned()))
        } else {
            Ok(ret_val.try_into().unwrap())
        }
    }
}