realm-db-reader 0.2.1

A Rust library for reading Realm database files
Documentation
use std::any::type_name;
use std::error::Error;

use chrono::{DateTime, Utc};

use crate::error::ValueError;
use crate::table::Row;
use crate::value::{ARRAY_VALUE_KEY, Backlink, Link, Value};

macro_rules! value_try_into {
    (Option<$target:ty>, $source:ident) => {
        impl TryFrom<Value> for Option<$target> {
            type Error = ValueError;

            fn try_from(value: Value) -> Result<Self, Self::Error> {
                match value {
                    Value::$source(val) => Ok(Some(val)),
                    Value::None => Ok(None),
                    value => Err(ValueError::UnexpectedType {
                        expected: stringify!($target),
                        found: value,
                    }),
                }
            }
        }
    };

    ($target:ty, $source:ident) => {
        impl TryFrom<Value> for $target {
            type Error = ValueError;

            fn try_from(value: Value) -> Result<Self, Self::Error> {
                match value {
                    Value::$source(val) => Ok(val),
                    value => Err(ValueError::UnexpectedType {
                        expected: stringify!($target),
                        found: value,
                    }),
                }
            }
        }

        impl<'a> TryFrom<Row<'a>> for $target {
            type Error = ValueError;

            fn try_from(mut value: Row<'a>) -> Result<Self, Self::Error> {
                let Some(value) = value.take(ARRAY_VALUE_KEY) else {
                    return Err(ValueError::ExpectedArrayRow {
                        field: ARRAY_VALUE_KEY,
                        found: value.into_owned(),
                    });
                };

                value.try_into()
            }
        }
    };
}

value_try_into!(String, String);
value_try_into!(Option<String>, String);
value_try_into!(i64, Int);
value_try_into!(Option<i64>, Int);
value_try_into!(bool, Bool);
value_try_into!(f32, Float);
value_try_into!(f64, Double);
value_try_into!(DateTime<Utc>, Timestamp);
value_try_into!(Option<DateTime<Utc>>, Timestamp);
value_try_into!(Backlink, BackLink);
value_try_into!(Link, Link);
value_try_into!(Option<Link>, Link);

impl<'a, T> TryFrom<Value> for Vec<T>
where
    T: TryFrom<Row<'a>>,
    T::Error: Error + 'static,
{
    type Error = ValueError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        match value {
            Value::Table(rows) => {
                let mut result = Vec::with_capacity(rows.len());
                for row in rows {
                    result.push(row.try_into().map_err(|e| ValueError::VecConversionError {
                        element_type: type_name::<T>(),
                        source: Box::new(e),
                    })?);
                }

                Ok(result)
            }
            value => Err(ValueError::ExpectedTable { found: value }),
        }
    }
}