Skip to main content

reifydb_value/value/constraint/
precision.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::fmt::{self, Display, Formatter};
5
6use serde::{Deserialize, Serialize};
7
8use crate::error::{Error, TypeError};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
11#[repr(transparent)]
12pub struct Precision(u8);
13
14impl Precision {
15	pub const fn new(precision: u8) -> Self {
16		assert!(precision >= Self::MIN.0 && precision <= Self::MAX.0, "precision must be between 1 and 76");
17		Self(precision)
18	}
19
20	pub fn try_new(precision: u8) -> Result<Self, Error> {
21		if !(Self::MIN.0..=Self::MAX.0).contains(&precision) {
22			return Err(TypeError::DecimalPrecisionInvalid {
23				precision,
24			}
25			.into());
26		}
27		Ok(Self(precision))
28	}
29
30	pub const fn value(self) -> u8 {
31		self.0
32	}
33
34	pub const MAX: Self = Self(76);
35
36	pub const MIN: Self = Self(1);
37}
38
39impl Display for Precision {
40	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
41		self.0.fmt(f)
42	}
43}
44
45impl From<Precision> for u8 {
46	fn from(precision: Precision) -> Self {
47		precision.0
48	}
49}
50
51impl From<u8> for Precision {
52	fn from(value: u8) -> Self {
53		Self::new(value)
54	}
55}
56
57#[cfg(test)]
58mod tests {
59	use super::*;
60
61	#[test]
62	fn precision_is_bounded_by_one_and_seventy_six() {
63		// Precision 77 has no Arrow decimal type and precision 0 holds no digit, so both must be refused.
64		assert_eq!(Precision::MAX.value(), 76);
65		assert!(Precision::try_new(76).is_ok());
66		assert!(Precision::try_new(1).is_ok());
67		assert_eq!(Precision::try_new(77).unwrap_err().code, "NUMBER_006");
68		assert_eq!(Precision::try_new(0).unwrap_err().code, "NUMBER_006");
69	}
70
71	#[test]
72	#[should_panic(expected = "precision must be between 1 and 76")]
73	fn new_panics_past_seventy_six() {
74		// A silently accepted precision 77 would build a column no array can store.
75		Precision::new(77);
76	}
77}