use crate::error::CoreError;
use num_bigint::{BigInt, BigUint, Sign};
use prost::bytes::Bytes;
#[must_use]
pub fn encode_big_int_caster(value: &BigInt) -> Bytes {
let (sign, magnitude) = value.to_bytes_be();
if magnitude.is_empty() {
return Bytes::from_static(&[0, 0]);
}
let sign_byte = match sign {
Sign::Minus => 1,
Sign::NoSign | Sign::Plus => 0,
};
let mut encoded = Vec::with_capacity(magnitude.len() + 1);
encoded.push(sign_byte);
encoded.extend_from_slice(&magnitude);
Bytes::from(encoded)
}
pub fn decode_big_int_caster(bytes: &[u8]) -> Result<Option<BigInt>, CoreError> {
match bytes.len() {
0 => Err(CoreError::InvalidBigIntEncoding(
"empty buffer is not a valid BigIntCaster value".to_owned(),
)),
1 => {
if bytes[0] == 0 {
Ok(None)
} else {
Err(CoreError::InvalidBigIntEncoding(format!(
"single-byte encoding must be nil marker 0x00, got 0x{:02x}",
bytes[0]
)))
}
}
_ => {
let magnitude = BigUint::from_bytes_be(&bytes[1..]);
let value = match bytes[0] {
0 => BigInt::from_biguint(Sign::Plus, magnitude),
1 => BigInt::from_biguint(Sign::Minus, magnitude),
sign => {
return Err(CoreError::InvalidBigIntEncoding(format!(
"invalid sign byte 0x{sign:02x}"
)));
}
};
Ok(Some(value))
}
}
}
pub fn parse_big_uint(value: &str) -> Result<Bytes, CoreError> {
let trimmed = value.trim();
let number = if let Some(hex_body) = trimmed.strip_prefix("0x") {
BigUint::parse_bytes(hex_body.as_bytes(), 16)
} else {
BigUint::parse_bytes(trimmed.as_bytes(), 10)
};
let num = number.ok_or_else(|| CoreError::InvalidNumeric(value.to_owned()))?;
Ok(encode_big_int_caster(&BigInt::from_biguint(
Sign::Plus,
num,
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_big_int_caster_zero() {
let encoded = encode_big_int_caster(&BigInt::from(0));
assert_eq!(encoded.as_ref(), &[0, 0]);
}
#[test]
fn test_encode_big_int_caster_positive() {
let encoded = encode_big_int_caster(&BigInt::from(12345));
assert_eq!(encoded.as_ref(), &[0, 0x30, 0x39]);
}
#[test]
fn test_encode_big_int_caster_negative() {
let encoded = encode_big_int_caster(&BigInt::from(-12345));
assert_eq!(encoded.as_ref(), &[1, 0x30, 0x39]);
}
#[test]
fn test_decode_big_int_caster_nil() {
assert_eq!(decode_big_int_caster(&[0]).unwrap(), None);
}
#[test]
fn test_decode_big_int_caster_zero() {
assert_eq!(
decode_big_int_caster(&[0, 0]).unwrap(),
Some(BigInt::from(0))
);
}
#[test]
fn test_decode_big_int_caster_positive() {
assert_eq!(
decode_big_int_caster(&[0, 0x30, 0x39]).unwrap(),
Some(BigInt::from(12345))
);
}
#[test]
fn test_decode_big_int_caster_negative() {
assert_eq!(
decode_big_int_caster(&[1, 0x30, 0x39]).unwrap(),
Some(BigInt::from(-12345))
);
}
#[test]
fn test_decode_big_int_caster_invalid_sign() {
let err = decode_big_int_caster(&[2, 3, 4]).unwrap_err();
assert!(matches!(err, CoreError::InvalidBigIntEncoding(_)));
}
#[test]
fn test_parse_big_uint_zero() {
let zero = parse_big_uint("0").unwrap();
assert_eq!(zero.as_ref(), &[0, 0]);
}
#[test]
fn test_parse_big_uint_decimal() {
let val = parse_big_uint("1000000000000000").unwrap();
assert!(!val.is_empty());
assert_eq!(val[0], 0);
}
#[test]
fn test_parse_big_uint_hex() {
let val = parse_big_uint("0x3039").unwrap();
assert_eq!(val.as_ref(), &[0, 0x30, 0x39]);
}
#[test]
fn test_parse_big_uint_invalid() {
let result = parse_big_uint("not_a_number");
assert!(result.is_err());
}
#[test]
fn test_parse_big_uint_whitespace() {
let val = parse_big_uint(" 123 ").unwrap();
assert_eq!(val.as_ref(), &[0, 123]);
}
}