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(bytes: &Vec<u8>) -> AvroResult<BigDecimal> {
51 let mut bytes: &[u8] = bytes.as_slice();
52 let mut big_decimal_buffer = match decode_len(&mut bytes) {
53 Ok(size) => vec![0u8; size],
54 Err(err) => return Err(Details::BigDecimalLen(Box::new(err)).into()),
55 };
56
57 bytes
58 .read_exact(&mut big_decimal_buffer[..])
59 .map_err(Details::ReadDouble)?;
60
61 match decode_long(&mut bytes) {
62 Ok(Value::Long(scale_value)) => {
63 let big_int: BigInt = BigInt::from_signed_bytes_be(&big_decimal_buffer);
64 let decimal = BigDecimal::new(big_int, scale_value);
65 Ok(decimal)
66 }
67 _ => Err(Details::BigDecimalScale.into()),
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::{Codec, Reader, Schema, Writer, error::Error, types::Record};
75 use apache_avro_test_helper::TestResult;
76 use bigdecimal::{One, Zero};
77 use pretty_assertions::assert_eq;
78 use std::{
79 fs::File,
80 io::BufReader,
81 ops::{Div, Mul},
82 str::FromStr,
83 };
84
85 #[test]
86 fn test_avro_3779_bigdecimal_serial() -> TestResult {
87 let value: BigDecimal =
88 bigdecimal::BigDecimal::from(-1421).div(bigdecimal::BigDecimal::from(2));
89 let mut current: BigDecimal = BigDecimal::one();
90
91 for iter in 1..180 {
92 let buffer: Vec<u8> = serialize_big_decimal(¤t)?;
93
94 let mut as_slice = buffer.as_slice();
95 decode_long(&mut as_slice)?;
96
97 let mut result: Vec<u8> = Vec::new();
98 result.extend_from_slice(as_slice);
99
100 let deserialize_big_decimal: Result<BigDecimal, Error> =
101 deserialize_big_decimal(&result);
102 assert!(
103 deserialize_big_decimal.is_ok(),
104 "can't deserialize for iter {iter}"
105 );
106 assert_eq!(current, deserialize_big_decimal?, "not equals for {iter}");
107 current = current.mul(&value);
108 }
109
110 let buffer: Vec<u8> = serialize_big_decimal(&BigDecimal::zero())?;
111 let mut as_slice = buffer.as_slice();
112 decode_long(&mut as_slice)?;
113
114 let mut result: Vec<u8> = Vec::new();
115 result.extend_from_slice(as_slice);
116
117 let deserialize_big_decimal: Result<BigDecimal, Error> = deserialize_big_decimal(&result);
118 assert!(
119 deserialize_big_decimal.is_ok(),
120 "can't deserialize for zero"
121 );
122 assert_eq!(
123 BigDecimal::zero(),
124 deserialize_big_decimal?,
125 "not equals for zero"
126 );
127
128 Ok(())
129 }
130
131 #[test]
132 fn test_avro_3779_record_with_bg() -> TestResult {
133 let schema_str = r#"
134 {
135 "type": "record",
136 "name": "test",
137 "fields": [
138 {
139 "name": "field_name",
140 "type": "bytes",
141 "logicalType": "big-decimal"
142 }
143 ]
144 }
145 "#;
146 let schema = Schema::parse_str(schema_str)?;
147
148 let mut record = Record::new(&schema).unwrap();
150 let val = BigDecimal::new(BigInt::from(12), 2);
151 record.put("field_name", val.clone());
152
153 let codec = Codec::Null;
155 let mut writer = Writer::builder()
156 .schema(&schema)
157 .codec(codec)
158 .writer(Vec::new())
159 .build();
160
161 writer.append(record.clone())?;
162 writer.flush()?;
163
164 let wrote_data = writer.into_inner()?;
166 let mut reader = Reader::new(&wrote_data[..])?;
167
168 let value = reader.next().unwrap()?;
169
170 let big_decimal_value: &Value = match value {
172 Value::Record(ref fields) => Ok(&fields[0].1),
173 other => Err(format!("Expected a Value::Record, got: {other:?}")),
174 }?;
175
176 let x1res: &BigDecimal = match big_decimal_value {
177 Value::BigDecimal(s) => Ok(s),
178 other => Err(format!("Expected Value::BigDecimal, got: {other:?}")),
179 }?;
180 assert_eq!(&val, x1res);
181
182 Ok(())
183 }
184
185 #[test]
186 fn test_avro_3779_from_java_file() -> TestResult {
187 let file: File = File::open("./tests/bigdec.avro")?;
190 let mut reader = Reader::new(BufReader::new(&file))?;
191 let next_element = reader.next();
192 assert!(next_element.is_some());
193 let value = next_element.unwrap()?;
194 let bg = match value {
195 Value::Record(ref fields) => Ok(&fields[0].1),
196 other => Err(format!("Expected a Value::Record, got: {other:?}")),
197 }?;
198 let value_big_decimal = match bg {
199 Value::BigDecimal(val) => Ok(val),
200 other => Err(format!("Expected a Value::BigDecimal, got: {other:?}")),
201 }?;
202
203 let ref_value = BigDecimal::from_str("2.24")?;
204 assert_eq!(&ref_value, value_big_decimal);
205
206 Ok(())
207 }
208}