spherenet-program-whitelist-interface 0.1.0

Interface of SphereNet's Program Whitelist Program
Documentation
pub mod account_state;
pub mod entry;
pub mod whitelist;
use {
    bytemuck::{Pod, Zeroable},
    core::mem,
    pinocchio::program_error::ProgramError,
};

/// Trait for types that have a fixed, known size
pub trait SizeOf {
    /// Size of the type in bytes
    const SIZE_OF: usize;
}

/// Implement SizeOf for all Pod types
impl<T: Pod> SizeOf for T {
    const SIZE_OF: usize = mem::size_of::<T>();
}

/// Type alias for fields represented as `COption`.
pub type COption<T> = ([u8; 4], T);

/// `COption<T>` stored as a Pod type for compatibility with bytemuck
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct PodCOption<T>
where
    T: Pod + Default,
{
    pub(crate) option: [u8; 4],
    pub(crate) value: T,
}

impl<T> PodCOption<T>
where
    T: Pod + Default,
{
    /// Represents that no value is stored in the option, like `Option::None`
    pub const NONE: [u8; 4] = [0; 4];
    /// Represents that some value is stored in the option, like
    /// `Option::Some(v)`
    pub const SOME: [u8; 4] = [1, 0, 0, 0];

    /// Create a `PodCOption` equivalent of `Option::None`
    ///
    /// This could be made `const` by using `std::mem::zeroed`, but that would
    /// require `unsafe` code, which is prohibited at the crate level.
    pub fn none() -> Self {
        Self {
            option: Self::NONE,
            value: T::default(),
        }
    }

    /// Create a `PodCOption` equivalent of `Option::Some(value)`
    pub const fn some(value: T) -> Self {
        Self {
            option: Self::SOME,
            value,
        }
    }

    /// Get the underlying value or another provided value if it isn't set,
    /// equivalent of `Option::unwrap_or`
    pub fn unwrap_or(self, default: T) -> T {
        if self.option == Self::NONE {
            default
        } else {
            self.value
        }
    }

    /// Checks to see if a value is set, equivalent of `Option::is_some`
    pub fn is_some(&self) -> bool {
        self.option == Self::SOME
    }

    /// Checks to see if no value is set, equivalent of `Option::is_none`
    pub fn is_none(&self) -> bool {
        self.option == Self::NONE
    }

    /// Converts the option into a Result, similar to `Option::ok_or`
    pub fn ok_or<E>(self, error: E) -> Result<T, E> {
        match self {
            Self {
                option: Self::SOME,
                value,
            } => Ok(value),
            _ => Err(error),
        }
    }
}

impl<T: Pod + Default> From<COption<T>> for PodCOption<T> {
    fn from(opt: COption<T>) -> Self {
        if opt.0[0] == 0 {
            Self {
                option: Self::NONE,
                value: T::default(),
            }
        } else {
            Self {
                option: Self::SOME,
                value: opt.1,
            }
        }
    }
}

/// Trait to represent a type that can be initialized.
pub trait Initializable {
    /// Return `true` if the object is initialized.
    fn is_initialized(&self) -> bool;
}

/// Load a reference for an initialized `T` from the given bytes
#[inline(always)]
pub fn load<'data, T: Pod + Initializable>(input: &'data [u8]) -> Result<&'data T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    let account = bytemuck::from_bytes::<T>(input);

    if !account.is_initialized() {
        return Err(ProgramError::UninitializedAccount);
    }

    Ok(account)
}

/// Load a mutable reference for an initialized `T` from the given bytes
#[inline(always)]
pub fn load_mut<'data, T: Pod + Initializable>(
    input: &'data mut [u8],
) -> Result<&'data mut T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    let account = bytemuck::from_bytes_mut::<T>(input);

    if !account.is_initialized() {
        return Err(ProgramError::UninitializedAccount);
    }

    Ok(account)
}

/// Load a reference without checking if the data is initialized
#[inline(always)]
pub fn load_unchecked<'data, T: Pod>(input: &'data [u8]) -> Result<&'data T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    Ok(bytemuck::from_bytes::<T>(input))
}

/// Load a mutable reference without checking if the data is initialized
#[inline(always)]
pub fn load_mut_unchecked<'data, T: Pod>(
    input: &'data mut [u8],
) -> Result<&'data mut T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    Ok(bytemuck::from_bytes_mut::<T>(input))
}