apache_avro/
bigdecimal.rs1use crate::{
19 AvroResult,
20 decode::{decode_len, decode_long},
21 encode::{encode_bytes, encode_long},
22 error::Details,
23 types::Value,
24};
25pub use bigdecimal::BigDecimal;
26use num_bigint::BigInt;
27use std::io::Read;
28
29pub(crate) fn big_decimal_as_bytes(decimal: &BigDecimal) -> AvroResult<Vec<u8>> {
30 let mut buffer: Vec<u8> = Vec::new();
31 let (big_int, exponent): (BigInt, i64) = decimal.as_bigint_and_exponent();
32 let big_endian_value: Vec<u8> = big_int.to_signed_bytes_be();
33 encode_bytes(&big_endian_value, &mut buffer)?;
34 encode_long(exponent, &mut buffer)?;
35
36 Ok(buffer)
37}
38
39pub(crate) fn serialize_big_decimal(decimal: &BigDecimal) -> AvroResult<Vec<u8>> {
40 let buffer = big_decimal_as_bytes(decimal)?;
42
43 let mut final_buffer: Vec<u8> = Vec::new();
45 encode_bytes(&buffer, &mut final_buffer)?;
46
47 Ok(final_buffer)
48}
49
50pub(crate) fn deserialize_big_decimal(mut bytes: &[u8]) -> AvroResult<BigDecimal> {
51 let mut big_decimal_buffer = match decode_len(&mut bytes) {
52 Ok(size) => vec![0u8; size],
53 Err(err) => return Err(Details::BigDecimalLen(Box::new(err)).into()),
54 };
55
56 bytes
57 .read_exact(&mut big_decimal_buffer[..])
58 .map_err(Details::ReadDouble)?;
59
60 match decode_long(&mut bytes) {
61 Ok(Value::Long(scale_value)) => {
62 let big_int: BigInt = BigInt::from_signed_bytes_be(&big_decimal_buffer);
63 let decimal = BigDecimal::new(big_int, scale_value);
64 Ok(decimal)
65 }
66 _ => Err(Details::BigDecimalScale.into()),
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use std::{
73 fs::File,
74 io::BufReader,
75 ops::{Div, Mul},
76 str::FromStr,
77 };
78
79 use apache_avro_test_helper::TestResult;
80 use bigdecimal::{One, Zero};
81 use pretty_assertions::assert_eq;
82
83 use super::*;
84 use crate::{
85 Codec, Reader, Schema, Writer, error::Error, reader::datum::GenericDatumReader,
86 types::Record, writer::datum::GenericDatumWriter,
87 };
88
89 #[test]
90 fn test_avro_3779_bigdecimal_serial() -> TestResult {
91 let value: BigDecimal =
92 bigdecimal::BigDecimal::from(-1421).div(bigdecimal::BigDecimal::from(2));
93 let mut current: BigDecimal = BigDecimal::one();
94
95 for iter in 1..180 {
96 let buffer: Vec<u8> = serialize_big_decimal(¤t)?;
97
98 let mut as_slice = buffer.as_slice();
99 decode_long(&mut as_slice)?;
100
101 let mut result: Vec<u8> = Vec::new();
102 result.extend_from_slice(as_slice);
103
104 let deserialize_big_decimal: Result<BigDecimal, Error> =
105 deserialize_big_decimal(&result);
106 assert!(
107 deserialize_big_decimal.is_ok(),
108 "can't deserialize for iter {iter}"
109 );
110 assert_eq!(current, deserialize_big_decimal?, "not equals for {iter}");
111 current = current.mul(&value);
112 }
113
114 let buffer: Vec<u8> = serialize_big_decimal(&BigDecimal::zero())?;
115 let mut as_slice = buffer.as_slice();
116 decode_long(&mut as_slice)?;
117
118 let mut result: Vec<u8> = Vec::new();
119 result.extend_from_slice(as_slice);
120
121 let deserialize_big_decimal: Result<BigDecimal, Error> = deserialize_big_decimal(&result);
122 assert!(
123 deserialize_big_decimal.is_ok(),
124 "can't deserialize for zero"
125 );
126 assert_eq!(
127 BigDecimal::zero(),
128 deserialize_big_decimal?,
129 "not equals for zero"
130 );
131
132 Ok(())
133 }
134
135 #[test]
136 fn test_avro_3779_record_with_bg() -> TestResult {
137 let schema_str = r#"
138 {
139 "type": "record",
140 "name": "test",
141 "fields": [
142 {
143 "name": "field_name",
144 "type": {
145 "type": "bytes",
146 "logicalType": "big-decimal"
147 }
148 }
149 ]
150 }
151 "#;
152 let schema = Schema::parse_str(schema_str)?;
153
154 let mut record = Record::new(&schema).unwrap();
156 let val = BigDecimal::new(BigInt::from(12), 2);
157 record.put("field_name", val.clone());
158
159 let codec = Codec::Null;
161 let mut writer = Writer::builder()
162 .schema(&schema)
163 .codec(codec)
164 .writer(Vec::new())
165 .build()?;
166
167 writer.append_value(record.clone())?;
168 writer.flush()?;
169
170 let wrote_data = writer.into_inner()?;
172 let mut reader = Reader::new(&wrote_data[..])?;
173
174 let value = reader.next().unwrap()?;
175
176 let big_decimal_value: &Value = match value {
178 Value::Record(ref fields) => Ok(&fields[0].1),
179 other => Err(format!("Expected a Value::Record, got: {other:?}")),
180 }?;
181
182 let x1res: &BigDecimal = match big_decimal_value {
183 Value::BigDecimal(s) => Ok(s),
184 other => Err(format!("Expected Value::BigDecimal, got: {other:?}")),
185 }?;
186 assert_eq!(&val, x1res);
187
188 Ok(())
189 }
190
191 #[test]
192 fn avro_rs_338_deserialize_serde_way() -> TestResult {
193 #[derive(Clone, PartialEq, Eq, Debug, Default, serde::Deserialize, serde::Serialize)]
194 struct Test {
195 big_decimal: BigDecimal,
196 }
197
198 let schema_str = r#"
199 {
200 "type": "record",
201 "name": "Test",
202 "fields": [
203 {
204 "name": "big_decimal",
205 "type": "string"
206 }
207 ]
208 }
209 "#;
210 let schema = Schema::parse_str(schema_str)?;
211
212 let test = Test::default();
213
214 let serialized = GenericDatumWriter::builder(&schema)
215 .build()?
216 .write_ser_to_vec(&test)?;
217 let value: Test = GenericDatumReader::builder(&schema)
218 .build()?
219 .read_deser(&mut &serialized[..])?;
220
221 assert_eq!(value, test);
222
223 Ok(())
224 }
225
226 #[test]
227 fn avro_rs_338_deserialize_serde_way_with_bigdecimal() -> TestResult {
228 #[derive(Clone, PartialEq, Eq, Debug, Default, serde::Deserialize, serde::Serialize)]
229 #[serde(rename = "test")]
230 struct Test {
231 #[serde(with = "crate::serde::bigdecimal")]
232 big_decimal: BigDecimal,
233 }
234
235 let schema_str = r#"
236 {
237 "type": "record",
238 "name": "test",
239 "fields": [
240 {
241 "name": "big_decimal",
242 "type": {
243 "type": "bytes",
244 "logicalType": "big-decimal"
245 }
246 }
247 ]
248 }
249 "#;
250 let schema = Schema::parse_str(schema_str)?;
251
252 let test = Test::default();
253
254 let mut writer = Writer::new(&schema, Vec::new())?;
256 writer.append_ser(test.clone())?;
257
258 let wrote_data = writer.into_inner()?;
259
260 let mut reader = Reader::new(&wrote_data[..])?.into_deser_iter();
262
263 let value = reader.next().unwrap()?;
264
265 assert_eq!(test, value);
266
267 Ok(())
268 }
269
270 #[test]
271 fn test_avro_3779_from_java_file() -> TestResult {
272 let file: File = File::open("./tests/bigdec.avro")?;
275 let mut reader = Reader::new(BufReader::new(&file))?;
276 let next_element = reader.next();
277 assert!(next_element.is_some());
278 let value = next_element.unwrap()?;
279 let bg = match value {
280 Value::Record(ref fields) => Ok(&fields[0].1),
281 other => Err(format!("Expected a Value::Record, got: {other:?}")),
282 }?;
283 let value_big_decimal = match bg {
284 Value::BigDecimal(val) => Ok(val),
285 other => Err(format!("Expected a Value::BigDecimal, got: {other:?}")),
286 }?;
287
288 let ref_value = BigDecimal::from_str("2.24")?;
289 assert_eq!(&ref_value, value_big_decimal);
290
291 Ok(())
292 }
293}