Skip to main content

reifydb_codec/
typeinfo.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::str::from_utf8;
5
6use reifydb_value::value::{digest::Digest, value_type::ValueType};
7
8use crate::{
9	error::{DecodeError, EncodeError},
10	reader::Reader,
11	tag::{EXTENDED_TYPE_TAG, MAX_OPTION_DEPTH, TypeTag, ValueKind, peel_options},
12	unscaled::{decode_params, family_type},
13};
14
15pub fn encode_value_type(ty: &ValueType, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
16	let (base, depth) = peel_options(ty);
17	if depth > MAX_OPTION_DEPTH as u32 {
18		if depth > u8::MAX as u32 {
19			return Err(EncodeError::OptionDepthTooDeep {
20				depth,
21				max: u8::MAX,
22			});
23		}
24		buf.push(EXTENDED_TYPE_TAG);
25		buf.push(depth as u8);
26		encode_base(base, 0, buf)
27	} else {
28		encode_base(base, depth as u8, buf)
29	}
30}
31
32fn encode_base(base: &ValueType, depth: u8, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
33	let tag = TypeTag::new(ValueKind::of_type(base), depth)?;
34	buf.push(tag.byte());
35	match base {
36		ValueType::List(element) => encode_value_type(element, buf)?,
37		ValueType::Record(fields) => {
38			buf.extend_from_slice(&(fields.len() as u16).to_le_bytes());
39			for (name, field_ty) in fields {
40				let name_bytes = name.as_bytes();
41				buf.extend_from_slice(&(name_bytes.len() as u16).to_le_bytes());
42				buf.extend_from_slice(name_bytes);
43				encode_value_type(field_ty, buf)?;
44			}
45		}
46		ValueType::Tuple(elements) => {
47			buf.extend_from_slice(&(elements.len() as u16).to_le_bytes());
48			for element in elements {
49				encode_value_type(element, buf)?;
50			}
51		}
52		ValueType::Digest {
53			inner,
54			accuracy,
55		} => encode_digest_params(inner, *accuracy, buf)?,
56		ValueType::Decimal {
57			precision,
58			scale,
59		} => {
60			buf.push(precision.value());
61			buf.push(scale.value());
62		}
63		_ => {}
64	}
65	Ok(())
66}
67
68pub fn encode_digest_params(inner: &ValueType, accuracy: u32, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
69	Digest::new(inner.clone(), accuracy).map_err(|error| EncodeError::UnsupportedType(error.to_string()))?;
70	buf.push(ValueKind::of_type(inner).byte());
71	buf.extend_from_slice(&accuracy.to_le_bytes());
72	Ok(())
73}
74
75pub fn decode_digest_params(r: &mut Reader) -> Result<(ValueType, u32), DecodeError> {
76	let inner = TypeTag::from_byte(r.u8()?)?.to_type()?;
77	let accuracy = r.u32()?;
78	Digest::new(inner.clone(), accuracy)
79		.map_err(|error| DecodeError::InvalidData(format!("invalid digest type: {error}")))?;
80	Ok((inner, accuracy))
81}
82
83pub fn decode_value_type(r: &mut Reader) -> Result<ValueType, DecodeError> {
84	let first = r.u8()?;
85	if first == EXTENDED_TYPE_TAG {
86		let depth = r.u8()?;
87		let base = decode_value_type(r)?;
88		return Ok((0..depth).fold(base, |ty, _| ValueType::Option(Box::new(ty))));
89	}
90	let tag = TypeTag::from_byte(first)?;
91	let kind = tag.kind().ok_or(DecodeError::UnknownTypeCode(first))?;
92	let base = match kind {
93		ValueKind::List => ValueType::List(Box::new(decode_value_type(r)?)),
94		ValueKind::Record => {
95			let count = r.u16()?;
96			let mut fields = Vec::with_capacity(count as usize);
97			for _ in 0..count {
98				let name_len = r.u16()? as usize;
99				let name = from_utf8(r.take(name_len)?)
100					.map_err(|e| {
101						DecodeError::InvalidData(format!(
102							"invalid UTF-8 in record field name: {e}"
103						))
104					})?
105					.to_string();
106				fields.push((name, decode_value_type(r)?));
107			}
108			ValueType::Record(fields)
109		}
110		ValueKind::Tuple => {
111			let count = r.u16()?;
112			let mut elements = Vec::with_capacity(count as usize);
113			for _ in 0..count {
114				elements.push(decode_value_type(r)?);
115			}
116			ValueType::Tuple(elements)
117		}
118		ValueKind::Digest => {
119			let (inner, accuracy) = decode_digest_params(r)?;
120			ValueType::Digest {
121				inner: Box::new(inner),
122				accuracy,
123			}
124		}
125		ValueKind::Decimal => {
126			let precision = r.u8()?;
127			let (precision, scale) = decode_params(precision, r.u8()?)?;
128			family_type(kind, precision, scale)?
129		}
130		_ => return tag.to_type(),
131	};
132	Ok((0..tag.depth()).fold(base, |ty, _| ValueType::Option(Box::new(ty))))
133}