ydb 0.10.4

Crate contains generated low-level grpc code from YDB API protobuf, used as base for ydb crate
Documentation
#[cfg(test)]
#[path = "value_ydb_test.rs"]
mod value_ydb_test;

use std::time::{Duration, SystemTime};

use itertools::Itertools;
use uuid::Uuid;

use super::r#type::DecimalType;
use crate::grpc_wrapper::raw_errors::{RawError, RawResult};
use crate::grpc_wrapper::raw_table_service::value::r#type::{RawType, StructMember, StructType};
use crate::grpc_wrapper::raw_table_service::value::{RawTypedValue, RawValue};
use crate::types::SECONDS_PER_DAY;
use crate::{Bytes, SignedInterval, Value};

impl TryFrom<crate::Value> for RawTypedValue {
    type Error = RawError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let res = match value {
            Value::Void => RawTypedValue {
                r#type: RawType::Void,
                value: RawValue::NullFlag,
            },
            Value::Null => RawTypedValue {
                r#type: RawType::Null,
                value: RawValue::NullFlag,
            },
            Value::Bool(v) => RawTypedValue {
                r#type: RawType::Bool,
                value: RawValue::Bool(v),
            },
            Value::Int8(v) => RawTypedValue {
                r#type: RawType::Int8,
                value: RawValue::Int32(v as i32),
            },
            Value::Uint8(v) => RawTypedValue {
                r#type: RawType::Uint8,
                value: RawValue::UInt32(v as u32),
            },
            Value::Int16(v) => RawTypedValue {
                r#type: RawType::Int16,
                value: RawValue::Int32(v as i32),
            },
            Value::Uint16(v) => RawTypedValue {
                r#type: RawType::Uint16,
                value: RawValue::UInt32(v as u32),
            },
            Value::Int32(v) => RawTypedValue {
                r#type: RawType::Int32,
                value: RawValue::Int32(v),
            },
            Value::Uint32(v) => RawTypedValue {
                r#type: RawType::Uint32,
                value: RawValue::UInt32(v),
            },
            Value::Int64(v) => RawTypedValue {
                r#type: RawType::Int64,
                value: RawValue::Int64(v),
            },
            Value::Uint64(v) => RawTypedValue {
                r#type: RawType::Uint64,
                value: RawValue::UInt64(v),
            },
            Value::Float(v) => RawTypedValue {
                r#type: RawType::Float,
                value: RawValue::Float(v),
            },
            Value::Double(v) => RawTypedValue {
                r#type: RawType::Double,
                value: RawValue::Double(v),
            },
            Value::Date(v) => RawTypedValue {
                r#type: RawType::Date,
                value: RawValue::UInt32(
                    (v.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() / SECONDS_PER_DAY)
                        .try_into()?,
                ),
            },
            Value::DateTime(v) => RawTypedValue {
                r#type: RawType::DateTime,
                value: RawValue::UInt32(
                    v.duration_since(SystemTime::UNIX_EPOCH)
                        .unwrap()
                        .as_secs()
                        .try_into()?,
                ),
            },
            Value::Timestamp(v) => RawTypedValue {
                r#type: RawType::Timestamp,
                value: RawValue::UInt64(
                    v.duration_since(SystemTime::UNIX_EPOCH)
                        .unwrap()
                        .as_micros()
                        .try_into()?,
                ),
            },
            Value::Interval(v) => RawTypedValue {
                r#type: RawType::Interval,
                value: RawValue::Int64(v.as_nanos()?),
            },
            Value::Bytes(v) => RawTypedValue {
                r#type: RawType::Bytes,
                value: RawValue::Bytes(v.into()),
            },
            Value::Text(v) => RawTypedValue {
                r#type: RawType::UTF8,
                value: RawValue::Text(v),
            },
            Value::Yson(v) => RawTypedValue {
                r#type: RawType::Yson,
                value: RawValue::Bytes(v.into()),
            },
            Value::Json(v) => RawTypedValue {
                r#type: RawType::Json,
                value: RawValue::Text(v),
            },
            Value::JsonDocument(v) => RawTypedValue {
                r#type: RawType::JSONDocument,
                value: RawValue::Text(v),
            },
            Value::Decimal(v) => {
                let (int_val, _scale, negative) = v.into_parts();
                let int_value = (if negative { -1 } else { 1 }) * (int_val as i128);
                let (high, low) = split_to_parts(int_value as u128);

                RawTypedValue {
                    r#type: RawType::Decimal(DecimalType {
                        precision: v.precision(),
                        scale: v.scale(),
                    }),
                    value: RawValue::HighLow128(high, low),
                }
            }
            Value::Uuid(v) => {
                let bytes = v.to_bytes_le();

                let low = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
                let high = u64::from_le_bytes(bytes[8..16].try_into().unwrap());

                RawTypedValue {
                    r#type: RawType::Uuid,
                    value: RawValue::HighLow128(high, low),
                }
            }
            Value::Optional(v) => {
                let type_example: RawTypedValue = v.t.try_into()?;
                if let Some(v) = v.value {
                    let value: RawTypedValue = v.try_into()?;
                    RawTypedValue {
                        r#type: RawType::Optional(Box::new(type_example.r#type)),
                        value: value.value,
                    }
                } else {
                    RawTypedValue {
                        r#type: RawType::Optional(Box::new(type_example.r#type)),
                        value: RawValue::NullFlag,
                    }
                }
            }
            Value::List(v) => {
                let type_example: RawTypedValue = v.t.try_into()?;
                let items_res: Result<Vec<RawTypedValue>, _> =
                    v.values.into_iter().map(|item| item.try_into()).collect();

                RawTypedValue {
                    r#type: RawType::List(Box::new(type_example.r#type)),
                    value: RawValue::Items(items_res?.into_iter().map(|item| item.value).collect()),
                }
            }
            Value::Struct(v) => {
                if v.values.len() != v.fields_name.len() {
                    return Err(RawError::custom(format!(
                        "struct fields len: {} not equals with values len: {}",
                        v.fields_name.len(),
                        v.values.len()
                    )));
                }

                let items_res: Result<Vec<RawTypedValue>, _> =
                    v.values.into_iter().map(RawTypedValue::try_from).collect();
                let items = items_res?;

                let mut raw_members = Vec::with_capacity(items.len());
                let mut raw_items = Vec::with_capacity(items.len());

                for (name, raw_typed_value) in v.fields_name.into_iter().zip_eq(items) {
                    raw_members.push(StructMember {
                        name,
                        member_type: raw_typed_value.r#type,
                    });
                    raw_items.push(raw_typed_value.value);
                }

                RawTypedValue {
                    r#type: RawType::Struct(StructType {
                        members: raw_members,
                    }),
                    value: RawValue::Items(raw_items),
                }
            }
        };
        Ok(res)
    }
}

pub(crate) fn split_to_parts(v: u128) -> (u64, u64) {
    let high = (v >> 64) as u64;
    let low = v as u64;
    (high, low)
}

pub(crate) fn merge_parts(high: u64, low: u64) -> u128 {
    (high as u128) << 64 | (low as u128)
}

impl TryFrom<RawTypedValue> for Value {
    type Error = RawError;

    fn try_from(value: RawTypedValue) -> Result<Self, Self::Error> {
        fn types_mismatch(t: RawType, v: RawValue) -> Result<Value, RawError> {
            Err(RawError::custom(format!(
                "unexpected combination of type '{t:?}' and value '{v:?}'"
            )))
        }

        fn type_unimplemented(t: RawType) -> Result<Value, RawError> {
            Err(RawError::custom(format!(
                "unimplemented raw to internal rust type conversion for type: {t:?}"
            )))
        }

        let res = match (value.r#type, value.value) {
            (RawType::Bool, RawValue::Bool(v)) => Value::Bool(v),
            (t @ RawType::Bool, v) => return types_mismatch(t, v),
            (RawType::Int8, RawValue::Int32(v)) => Value::Int8(v.try_into()?),
            (t @ RawType::Int8, v) => return types_mismatch(t, v),
            (RawType::Uint8, RawValue::UInt32(v)) => Value::Uint8(v.try_into()?),
            (t @ RawType::Uint8, v) => return types_mismatch(t, v),
            (RawType::Int16, RawValue::Int32(v)) => Value::Int16(v.try_into()?),
            (t @ RawType::Int16, v) => return types_mismatch(t, v),
            (RawType::Uint16, RawValue::UInt32(v)) => Value::Uint16(v.try_into()?),
            (t @ RawType::Uint16, v) => return types_mismatch(t, v),
            (RawType::Int32, RawValue::Int32(v)) => Value::Int32(v),
            (t @ RawType::Int32, v) => return types_mismatch(t, v),
            (RawType::Uint32, RawValue::UInt32(v)) => Value::Uint32(v),
            (t @ RawType::Uint32, v) => return types_mismatch(t, v),
            (RawType::Int64, RawValue::Int64(v)) => Value::Int64(v),
            (t @ RawType::Int64, v) => return types_mismatch(t, v),
            (RawType::Uint64, RawValue::UInt64(v)) => Value::Uint64(v),
            (t @ RawType::Uint64, v) => return types_mismatch(t, v),
            (RawType::Float, RawValue::Float(v)) => Value::Float(v),
            (t @ RawType::Float, v) => return types_mismatch(t, v),
            (RawType::Double, RawValue::Double(v)) => Value::Double(v),
            (t @ RawType::Double, v) => return types_mismatch(t, v),
            (RawType::Date, RawValue::UInt32(v)) => Value::Date(
                SystemTime::UNIX_EPOCH + Duration::from_secs((v as u64) * SECONDS_PER_DAY),
            ),
            (t @ RawType::Date, v) => return types_mismatch(t, v),
            (RawType::DateTime, RawValue::UInt32(v)) => {
                Value::DateTime(SystemTime::UNIX_EPOCH + Duration::from_secs(v.into()))
            }
            (t @ RawType::DateTime, v) => return types_mismatch(t, v),
            (RawType::Timestamp, RawValue::UInt64(v)) => {
                Value::Timestamp(SystemTime::UNIX_EPOCH + Duration::from_micros(v))
            }
            (t @ RawType::Timestamp, v) => return types_mismatch(t, v),
            (RawType::Interval, RawValue::Int64(v)) => {
                Value::Interval(SignedInterval::from_nanos(v))
            }
            (t @ RawType::Interval, v) => return types_mismatch(t, v),
            (t @ RawType::TzDate, _) => return type_unimplemented(t),
            (t @ RawType::TzDatetime, _) => return type_unimplemented(t),
            (t @ RawType::TzTimestamp, _) => return type_unimplemented(t),
            (RawType::Bytes, RawValue::Bytes(v)) => Value::Bytes(Bytes::from(v)),
            (t @ RawType::Bytes, v) => return types_mismatch(t, v),
            (RawType::UTF8, RawValue::Text(v)) => Value::Text(v),
            (t @ RawType::UTF8, v) => return types_mismatch(t, v),
            (RawType::Yson, RawValue::Bytes(v)) => Value::Yson(Bytes::from(v)),
            (t @ RawType::Yson, v) => return types_mismatch(t, v),
            (RawType::Json, RawValue::Text(v)) => Value::Json(v),
            (t @ RawType::Json, v) => return types_mismatch(t, v),
            (RawType::Uuid, RawValue::HighLow128(high, low)) => {
                let mut le_bytes = [0u8; 16];
                le_bytes[0..8].copy_from_slice(&low.to_le_bytes());
                le_bytes[8..16].copy_from_slice(&high.to_le_bytes());

                let uuid = Uuid::from_bytes_le(le_bytes);

                Value::Uuid(uuid)
            }
            (t @ RawType::Uuid, v) => return types_mismatch(t, v),
            (RawType::JSONDocument, RawValue::Text(v)) => Value::JsonDocument(v),
            (t @ RawType::JSONDocument, v) => return types_mismatch(t, v),
            (t @ RawType::DyNumber, _) => return type_unimplemented(t),
            (RawType::Decimal(t), RawValue::HighLow128(high, low)) => {
                let int_val = merge_parts(high, low) as i128;

                let value =
                    decimal_rs::Decimal::from_parts(int_val.unsigned_abs(), t.scale, int_val < 0)
                        .map_err(|e| RawError::decode_error(e.to_string()))?;
                return Ok(Value::Decimal(value));
            }
            (t @ RawType::Decimal(_), v) => return types_mismatch(t, v),
            (RawType::Optional(inner_type), v) => {
                let opt_value: Option<Value> = if let RawValue::NullFlag = v {
                    None
                } else {
                    let val: Value = RawTypedValue {
                        r#type: (*inner_type).clone(),
                        value: v,
                    }
                    .try_into()?;
                    Some(val)
                };

                let type_example: Value = (*inner_type).into_value_example()?;

                match Value::optional_from(type_example, opt_value) {
                    Ok(val) => val,
                    Err(err) => {
                        return Err(RawError::custom(format!(
                            "can't create optional value from rawtype: {err}"
                        )))
                    }
                }
            }
            (RawType::List(inner_type), v) => {
                let values = match v {
                    RawValue::NullFlag => Vec::default(),
                    RawValue::Items(items) => {
                        let values_res: Result<Vec<_>, _> = items
                            .into_iter()
                            .map(|item| {
                                RawTypedValue {
                                    r#type: (*inner_type).clone(),
                                    value: item,
                                }
                                .try_into()
                            })
                            .collect();

                        values_res?
                    }
                    _ => return types_mismatch(RawType::List(inner_type), v),
                };
                let type_example = (*inner_type).into_value_example()?;
                match Value::list_from(type_example, values) {
                    Ok(val) => val,
                    Err(err) => {
                        return Err(RawError::custom(format!(
                            "can't create list value from rawtype: {err}"
                        )))
                    }
                }
            }
            (t @ RawType::Tuple(_), _) => return type_unimplemented(t),
            (RawType::Struct(struct_name_types), RawValue::Items(items)) => {
                if struct_name_types.members.len() != items.len() {
                    return Err(RawError::custom(format!(
                        "mismatch struct field len description: '{:?}' and value items while decode raw types values with len: {}",
                        struct_name_types,
                        items.len()
                    )));
                };

                let fields: Vec<_> = struct_name_types
                    .members
                    .into_iter()
                    .zip_eq(items)
                    .map(|(st, item)| {
                        let value_result: RawResult<Value> = RawTypedValue {
                            r#type: st.member_type,
                            value: item,
                        }
                        .try_into();

                        match value_result {
                            Ok(v) => Ok((st.name, v)),
                            Err(err) => Err(err),
                        }
                    })
                    .try_collect()?;
                Value::struct_from_fields(fields)
            }
            (t @ RawType::Struct(_), v) => return types_mismatch(t, v),
            (t @ RawType::Dict(_), _) => return type_unimplemented(t),
            (t @ RawType::Variant(_), _) => return type_unimplemented(t),
            (t @ RawType::Tagged(_), _) => return type_unimplemented(t),
            (RawType::Null, RawValue::NullFlag) => Value::Null,
            (t @ RawType::Null, v) => return types_mismatch(t, v),
            (RawType::Void, RawValue::NullFlag) => Value::Void,
            (t @ RawType::Void, v) => return types_mismatch(t, v),
            (t @ RawType::EmptyList, v) => return types_mismatch(t, v),
            (t @ RawType::EmptyDict, v) => return types_mismatch(t, v),
        };
        Ok(res)
    }
}