elektra 0.9.10

Elektra serves as a universal and secure framework to access configuration parameters in a global, hierarchical key database.
use crate::{KeyNotFoundError, ReadOnly, StringKey};
use std::borrow::Cow;
use std::convert::TryInto;
use std::ffi::{CStr, CString};

pub trait ReadableKey: AsRef<elektra_sys::Key> + PartialEq + Eq + PartialOrd + Ord {
    /// The type returned by value.
    type GetValue;

    /// Returns the value this key holds.
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = StringKey::new("user:/sw/app")?;
    /// key.set_value("myvalue");
    /// assert_eq!(key.value(), "myvalue");
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn value(&self) -> Self::GetValue;

    /// Construct a new key from a raw key pointer
    unsafe fn from_ptr(ptr: *mut elektra_sys::Key) -> Self
    where
        Self: Sized;

    /// Return the name of the key as a borrowed slice.
    fn name(&self) -> Cow<str> {
        let c_str = unsafe { CStr::from_ptr(elektra_sys::keyName(self.as_ref())) };
        c_str.to_string_lossy()
    }

    /// Return the basename of the key as a borrowed slice.
    fn basename(&self) -> Cow<str> {
        let c_str = unsafe { CStr::from_ptr(elektra_sys::keyBaseName(self.as_ref())) };
        c_str.to_string_lossy()
    }

    /// Calculates number of bytes needed to store basename of key.
    fn basename_size(&self) -> isize {
        unsafe { elektra_sys::keyGetBaseNameSize(self.as_ref()) }
    }

    /// Return how many references the key has.
    fn get_ref(&self) -> u16 {
        unsafe { elektra_sys::keyGetRef(self.as_ref()) }
    }


    /// Returns the namespace of the name of this key.
    /// Note that there are some convenience methods implemented.
    /// 
    /// # Examples
    /// ```
    /// # use elektra::{BinaryKey,WriteableKey,ReadableKey};
    /// # use elektra_sys;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = BinaryKey::new("user:/sw/app")?;
    /// assert_eq!(key.namespace(), elektra_sys::KEY_NS_USER);
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn namespace(&self) -> u32 {
        unsafe { elektra_sys::keyGetNamespace(self.as_ref())  as u32 }
    }

    /// Determines if the key is in the spec namespace
    fn is_spec(&self) -> bool {
        self.namespace() == elektra_sys::KEY_NS_SPEC
    }

    /// Determines if the key is in the dir namespace
    fn is_dir(&self) -> bool {
        self.namespace() == elektra_sys::KEY_NS_DIR
    }

    /// Determines if the key is in the proc namespace
    fn is_proc(&self) -> bool {
        self.namespace() == elektra_sys::KEY_NS_PROC
    }

    /// Determines if the key is in the user namespace
    fn is_user(&self) -> bool {
        self.namespace() == elektra_sys::KEY_NS_USER
    }

    /// Determines if the key is in the system namespace
    fn is_system(&self) -> bool {
        self.namespace() == elektra_sys::KEY_NS_SYSTEM
    }

    /// Determines if the key is a cascading key
    fn is_cascading(&self) -> bool {
        self.namespace() == elektra_sys::KEY_NS_CASCADING
    }

    /// Returns the number of bytes needed to store the key's value, including the
    /// NULL terminator.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{BinaryKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = BinaryKey::new("user:/sw/app")?;
    /// key.set_value(b"12345");
    /// assert_eq!(key.value_size(), 5);
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn value_size(&self) -> usize {
        let ret_val = unsafe { elektra_sys::keyGetValueSize(self.as_ref()) };
        // keyGetValueSize returns -1 on null pointers, but we can be sure self.ptr is valid
        // so this conversion is safe
        ret_val.try_into().unwrap()
    }

    /// Returns true if the key has a binary value.
    ///
    /// # Notes
    /// Note that this does not return true for a newly created BinaryKey,
    /// but only when actual binary data has been set, due to the underlying
    /// generic Key.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{BinaryKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut key = BinaryKey::new("user:/sw/app")?;
    /// key.set_value(b"");
    /// assert!(key.is_binary());
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_binary(&self) -> bool {
        unsafe { elektra_sys::keyIsBinary(self.as_ref()) == 1 }
    }

    /// Returns true if the key has a string value.
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let key = StringKey::new("user:/sw/app")?;
    /// assert!(key.is_string());
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_string(&self) -> bool {
        unsafe { elektra_sys::keyIsString(self.as_ref()) == 1 }
    }

    /// Returns true if other is below self
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let key = StringKey::new("user:/sw/app")?;
    /// let key2 = StringKey::new("user:/sw/app/folder/key")?;
    /// assert!(key2.is_below(&key));
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_below(&self, other: &Self) -> bool
    where
        Self: Sized,
    {
        unsafe { elektra_sys::keyIsBelow(other.as_ref(), self.as_ref()) == 1 }
    }

    /// Returns true if other is *directly* below self
    ///
    /// # Examples
    /// ```
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let key = StringKey::new("user:/sw/app")?;
    /// let key2 = StringKey::new("user:/sw/app/key")?;
    /// assert!(key2.is_directly_below(&key));
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_directly_below(&self, other: &Self) -> bool
    where
        Self: Sized,
    {
        unsafe { elektra_sys::keyIsDirectlyBelow(other.as_ref(), self.as_ref()) == 1 }
    }

    /// Returns the metadata with the given metaname
    ///
    /// # Errors
    /// Returns `KeyNotFoundError` if no metakey with the given name was found.
    /// 
    /// # Panics
    /// Panics if the provided string contains interior nul bytes.
    fn meta(&self, metaname: &str) -> Result<ReadOnly<StringKey<'_>>, KeyNotFoundError>
    where
        Self: Sized,
    {
        let cstr = CString::new(metaname).unwrap();
        let key_ptr = unsafe { elektra_sys::keyGetMeta(self.as_ref(), cstr.as_ptr()) };
        if key_ptr.is_null() {
            Err(KeyNotFoundError::new(metaname.to_owned()))
        } else {
            let key: ReadOnly<StringKey<'_>> = unsafe { ReadOnly::from_ptr(key_ptr as *mut elektra_sys::Key) };
            Ok(key)
        }
    }
}