1use reifydb_value::value::{
5 constraint::{Constraint, TypeConstraint, bytes::MaxBytes},
6 dictionary::DictionaryId,
7 digest::Digest,
8 sumtype::SumTypeId,
9 value_type::ValueType,
10};
11
12use crate::{
13 error::{DecodeError, EncodeError},
14 tag::{TypeTag, ValueKind, peel_options},
15 unscaled::{decode_params, family_type, params},
16};
17
18#[repr(C)]
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub struct EncodedTypeConstraint {
21 pub base_type: u8,
22
23 pub constraint_type: u8,
24
25 pub constraint_param1: u32,
26
27 pub constraint_param2: u32,
28}
29
30pub fn encode_type_constraint(tc: &TypeConstraint) -> Result<EncodedTypeConstraint, EncodeError> {
31 let ty = tc.get_type();
32 let base_type = TypeTag::of_type(&ty)?.byte();
33 if let (
34 ValueType::Digest {
35 inner,
36 accuracy,
37 },
38 _,
39 ) = peel_options(&ty)
40 {
41 if let Some(constraint) = tc.constraint() {
42 return Err(EncodeError::UnsupportedType(format!(
43 "{ty} cannot carry the {constraint:?} constraint"
44 )));
45 }
46 Digest::new((**inner).clone(), *accuracy)
47 .map_err(|error| EncodeError::UnsupportedType(error.to_string()))?;
48 return Ok(EncodedTypeConstraint {
49 base_type,
50 constraint_type: 5,
51 constraint_param1: *accuracy,
52 constraint_param2: u32::from(TypeTag::of_type(inner)?.byte()),
53 });
54 }
55 if let Some((precision, scale)) = params(peel_options(&ty).0) {
56 if let Some(constraint) = tc.constraint() {
57 return Err(EncodeError::UnsupportedType(format!(
58 "{ty} cannot carry the {constraint:?} constraint"
59 )));
60 }
61 return Ok(EncodedTypeConstraint {
62 base_type,
63 constraint_type: 2,
64 constraint_param1: u32::from(precision.value()),
65 constraint_param2: u32::from(scale.value()),
66 });
67 }
68 Ok(match tc.constraint() {
69 None => EncodedTypeConstraint {
70 base_type,
71 constraint_type: 0,
72 constraint_param1: 0,
73 constraint_param2: 0,
74 },
75 Some(Constraint::MaxBytes(max)) => EncodedTypeConstraint {
76 base_type,
77 constraint_type: 1,
78 constraint_param1: max.value(),
79 constraint_param2: 0,
80 },
81 Some(Constraint::Dictionary(dict_id, id_type)) => EncodedTypeConstraint {
82 base_type,
83 constraint_type: 3,
84 constraint_param1: dict_id.to_u64() as u32,
85 constraint_param2: TypeTag::of_type(id_type)?.byte() as u32,
86 },
87 Some(Constraint::SumType(id)) => EncodedTypeConstraint {
88 base_type,
89 constraint_type: 4,
90 constraint_param1: id.to_u64() as u32,
91 constraint_param2: 0,
92 },
93 })
94}
95
96pub fn decode_type_constraint(encoded: &EncodedTypeConstraint) -> Result<TypeConstraint, DecodeError> {
97 let tag = TypeTag::from_byte(encoded.base_type)?;
98 if encoded.constraint_type == 5 {
99 return decode_digest_type(tag, encoded).map(TypeConstraint::unconstrained);
100 }
101 if encoded.constraint_type == 2 {
102 return decode_family_type(tag, encoded).map(TypeConstraint::unconstrained);
103 }
104 let ty = tag.to_type()?;
105 Ok(match encoded.constraint_type {
106 1 => TypeConstraint::with_constraint(
107 ty,
108 Constraint::MaxBytes(MaxBytes::new(encoded.constraint_param1)),
109 ),
110 3 => TypeConstraint::with_constraint(
111 ty,
112 Constraint::Dictionary(
113 DictionaryId::from(encoded.constraint_param1 as u64),
114 TypeTag::from_byte(encoded.constraint_param2 as u8)?.to_type()?,
115 ),
116 ),
117 4 => TypeConstraint::with_constraint(
118 ty,
119 Constraint::SumType(SumTypeId::from(encoded.constraint_param1 as u64)),
120 ),
121 _ => TypeConstraint::unconstrained(ty),
122 })
123}
124
125fn decode_family_type(tag: TypeTag, encoded: &EncodedTypeConstraint) -> Result<ValueType, DecodeError> {
126 let kind = tag.kind().ok_or(DecodeError::UnknownTypeCode(encoded.base_type))?;
127 let param = |value: u32| {
128 u8::try_from(value)
129 .map_err(|_| DecodeError::InvalidData(format!("type parameter {value} does not fit in a byte")))
130 };
131 let (precision, scale) = decode_params(param(encoded.constraint_param1)?, param(encoded.constraint_param2)?)?;
132 let base = family_type(kind, precision, scale)?;
133 Ok((0..tag.depth()).fold(base, |ty, _| ValueType::Option(Box::new(ty))))
134}
135
136fn decode_digest_type(tag: TypeTag, encoded: &EncodedTypeConstraint) -> Result<ValueType, DecodeError> {
137 if tag.kind() != Some(ValueKind::Digest) {
138 return Err(DecodeError::InvalidData(format!(
139 "digest constraint on base type tag 0x{:02X}, which is not a digest",
140 encoded.base_type
141 )));
142 }
143 let inner_byte = u8::try_from(encoded.constraint_param2).map_err(|_| {
144 DecodeError::InvalidData(format!(
145 "digest inner type tag {} does not fit in a type tag byte",
146 encoded.constraint_param2
147 ))
148 })?;
149 let inner = TypeTag::from_byte(inner_byte)?.to_type()?;
150 Digest::new(inner.clone(), encoded.constraint_param1)
151 .map_err(|error| DecodeError::InvalidData(format!("invalid digest type: {error}")))?;
152 let base = ValueType::Digest {
153 inner: Box::new(inner),
154 accuracy: encoded.constraint_param1,
155 };
156 Ok((0..tag.depth()).fold(base, |ty, _| ValueType::Option(Box::new(ty))))
157}