plabble-codec 0.1.0

Plabble Transport Protocol codec
Documentation
use rand::{thread_rng, RngCore};

use crate::abstractions::{Serializable, SerializationInfo, ID_SIZE};

/// Permissions for a bucket
///
/// # Fields
///
/// * `pub_read` - Whether the bucket can be read by everyone
/// * `pub_write` - Whether the bucket can be written by everyone
/// * `pub_append` - Whether the bucket can be appended by everyone
/// * `priv_write` - Whether the bucket can be written by the owner
/// * `priv_append` - Whether the bucket can be appended by the owner
/// * `delete_bucket` - Whether the bucket can be deleted by the owner
#[derive(Debug, PartialEq, Eq)]
pub struct BucketPermissions {
    pub pub_read: bool,      // bit 3 (4)
    pub pub_write: bool,     // bit 4 (8)
    pub pub_append: bool,    // bit 5 (16)
    pub priv_write: bool,    // bit 6 (32)
    pub priv_append: bool,   // bit 7 (64)
    pub delete_bucket: bool, // bit 8 (128)
}

impl Default for BucketPermissions {
    fn default() -> Self {
        Self {
            pub_read: true,
            pub_write: false,
            pub_append: false,
            priv_write: true,
            priv_append: false,
            delete_bucket: false,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct BucketId {
    data: [u8; ID_SIZE],
}

/// Bucket Identifier
impl BucketId {
    /// Create a new bucket ID
    ///
    /// # Arguments
    ///
    /// * `lifetime` - The lifetime of the bucket in days. 0 for infinite
    pub fn new(lifetime: u8) -> Self {
        let mut bytes = [0u8; 16];
        thread_rng().fill_bytes(&mut bytes);
        let mut res = Self { data: bytes };
        res.set_permissions(BucketPermissions::default());
        res.set_lifetime(lifetime);
        res
    }

    /// Get the lifetime of the bucket
    pub fn lifetime(&self) -> u8 {
        self.data[14]
    }

    /// Get the permissions of the bucket
    pub fn permissions(&self) -> BucketPermissions {
        BucketPermissions {
            pub_read: self.data[15] & 4 != 0,
            pub_write: self.data[15] & 8 != 0,
            pub_append: self.data[15] & 16 != 0,
            priv_write: self.data[15] & 32 != 0,
            priv_append: self.data[15] & 64 != 0,
            delete_bucket: self.data[15] & 128 != 0,
        }
    }

    /// Set the lifetime of the bucket
    pub fn set_lifetime(&mut self, lifetime: u8) {
        self.data[14] = lifetime;
    }

    /// Set the permissions of the bucket
    pub fn set_permissions(&mut self, permissions: BucketPermissions) {
        let mut set_bits: u8 = 0b00000000;
        let mut reset_bits: u8 = 0b11111111;
        if permissions.pub_read {
            set_bits += 4
        } else {
            reset_bits -= 4
        };
        if permissions.pub_write {
            set_bits += 8
        } else {
            reset_bits -= 8
        };
        if permissions.pub_append {
            set_bits += 16
        } else {
            reset_bits -= 16
        };
        if permissions.priv_write {
            set_bits += 32
        } else {
            reset_bits -= 32
        };
        if permissions.priv_append {
            set_bits += 64
        } else {
            reset_bits -= 64
        };
        if permissions.delete_bucket {
            set_bits += 128
        } else {
            reset_bits -= 128
        };

        // Apply bit masks
        let mut permissions = self.data[15];
        permissions |= set_bits;
        permissions &= reset_bits;
        self.data[15] = permissions;
    }
}

impl Serializable for BucketId {
    fn size(&self) -> usize {
        ID_SIZE
    }

    fn get_bytes(&self) -> Vec<u8> {
        self.data.to_vec()
    }

    fn from_bytes(
        data: &[u8],
        _: Option<SerializationInfo>,
    ) -> Result<Self, crate::abstractions::SerializationError>
    where
        Self: Sized,
    {
        Ok(BucketId {
            data: data.try_into().unwrap(),
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn can_create_bucket_id() {
        let bucket_id = BucketId::new(0);
        assert_eq!(0, bucket_id.lifetime());
        assert_eq!(BucketPermissions::default(), bucket_id.permissions());
    }

    #[test]
    fn can_set_bucket_permissions_and_lifetime() {
        let bytes = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 255];
        let mut id = BucketId::from_bytes(bytes, None).unwrap();
        assert_eq!(
            BucketPermissions {
                pub_read: true,
                pub_write: true,
                pub_append: true,
                priv_write: true,
                priv_append: true,
                delete_bucket: true
            },
            id.permissions()
        );

        assert_eq!(0b11111111, id.data[15]);
        assert_eq!("10111111", &format!("{:08b}", 255 - 64));
        assert_eq!("00111111", &format!("{:08b}", 255 - 64 - 128));

        id.set_permissions(BucketPermissions {
            pub_read: true,
            pub_write: false,
            pub_append: true,
            priv_write: false,
            priv_append: false,
            delete_bucket: true,
        });

        assert_eq!(
            BucketPermissions {
                pub_read: true,
                pub_write: false,
                pub_append: true,
                priv_write: false,
                priv_append: false,
                delete_bucket: true
            },
            id.permissions()
        );

        assert_eq!(0b10010111, id.data[15]);
        assert_eq!(25, id.lifetime());
        id.set_lifetime(253);
        assert_eq!(253, id.lifetime());
    }
}