realm-db-reader 0.2.1

A Rust library for reading Realm database files
Documentation
use std::fmt::Debug;
use std::sync::Arc;

use tracing::instrument;

use crate::Realm;
use crate::realm::RealmNode;
use crate::traits::{ArrayLike, Node, NodeWithContext};
use crate::utils::read_array_value;

use super::RealmRef;

#[derive(Debug)]
pub(crate) struct ScalarArray {
    node: RealmNode,
}

impl NodeWithContext<()> for ScalarArray {
    fn from_ref_with_context(realm: Arc<Realm>, ref_: RealmRef, _: ()) -> crate::RealmResult<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            node: RealmNode::from_ref(realm, ref_)?,
        })
    }
}

macro_rules! impl_scalar_bytewise {
    ($scalar:ty) => {
        impl ArrayLike<$scalar> for ScalarArray {
            #[instrument(level = "debug")]
            fn get(&self, index: usize) -> crate::RealmResult<$scalar> {
                assert!(
                    index < self.node.header.size as usize,
                    "Index out of bounds: {index} >= {}",
                    self.node.header.size
                );

                let offset_start = index * std::mem::size_of::<$scalar>();
                let offset_end = offset_start + std::mem::size_of::<$scalar>();
                let bytes = &self.node.payload()[offset_start..offset_end];

                Ok(<$scalar>::from_le_bytes(bytes.try_into().unwrap()))
            }

            fn get_direct(
                realm: Arc<Realm>,
                ref_: RealmRef,
                index: usize,
                _: (),
            ) -> crate::RealmResult<$scalar> {
                let header = realm.header(ref_)?;
                let payload = realm.payload(ref_, header.payload_len());
                let offset_start = index * std::mem::size_of::<$scalar>();
                let offset_end = offset_start + std::mem::size_of::<$scalar>();
                let bytes = &payload[offset_start..offset_end];

                Ok(<$scalar>::from_le_bytes(bytes.try_into().unwrap()))
            }

            fn is_null(&self, _: usize) -> crate::RealmResult<bool> {
                Ok(false)
            }

            fn size(&self) -> usize {
                self.node.header.size as usize
            }
        }
    };
}

impl_scalar_bytewise!(f32);
impl_scalar_bytewise!(f64);

impl ArrayLike<bool> for ScalarArray {
    fn get(&self, index: usize) -> crate::RealmResult<bool> {
        let value = read_array_value(self.node.payload(), self.node.header.width(), index);
        Ok(value != 0)
    }

    fn get_direct(
        realm: Arc<Realm>,
        ref_: RealmRef,
        index: usize,
        _: (),
    ) -> crate::RealmResult<bool> {
        let header = realm.header(ref_)?;
        let payload = realm.payload(ref_, header.payload_len());

        let value = read_array_value(payload, header.width(), index);
        Ok(value != 0)
    }

    fn is_null(&self, _: usize) -> crate::RealmResult<bool> {
        Ok(false)
    }

    fn size(&self) -> usize {
        self.node.header.size as usize
    }
}

impl ArrayLike<Option<bool>> for ScalarArray {
    fn get(&self, index: usize) -> crate::RealmResult<Option<bool>> {
        let value = read_array_value(self.node.payload(), self.node.header.width(), index + 1);
        let null_value = read_array_value(self.node.payload(), self.node.header.width(), 0);

        Ok(if value == null_value {
            None
        } else {
            Some(value != 0)
        })
    }

    fn get_direct(
        realm: Arc<Realm>,
        ref_: RealmRef,
        index: usize,
        _: (),
    ) -> crate::RealmResult<Option<bool>> {
        let header = realm.header(ref_)?;
        let payload = realm.payload(ref_, header.payload_len());

        let value = read_array_value(payload, header.width(), index + 1);
        let null_value = read_array_value(payload, header.width(), 0);

        Ok(if value == null_value {
            None
        } else {
            Some(value != 0)
        })
    }

    fn is_null(&self, index: usize) -> crate::RealmResult<bool> {
        let value = read_array_value(self.node.payload(), self.node.header.width(), index + 1);
        let null_value = read_array_value(self.node.payload(), self.node.header.width(), 0);

        Ok(value == null_value)
    }

    fn size(&self) -> usize {
        self.node.header.size as usize
    }
}