ephemeris_documents/
scalar.rs1use std::fmt;
4
5use facet_value::VNumber;
6
7use crate::annotation::StringStyle;
8use crate::error::{Feature, ValueError};
9use crate::format::Format;
10
11#[derive(Debug, Clone, Copy, PartialEq)]
13pub(crate) struct Finite(f64);
14
15impl Finite {
16 pub(crate) const fn new(value: f64) -> Result<Self, ValueError> {
21 if value.is_finite() {
22 Ok(Self(value))
23 } else {
24 Err(ValueError::NonFinite)
25 }
26 }
27
28 pub(crate) const fn get(self) -> f64 {
29 self.0
30 }
31}
32
33impl fmt::Display for Finite {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 let text = self.0.to_string();
38 if text.contains('.') {
39 f.write_str(&text)
40 } else {
41 write!(f, "{text}.0")
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq)]
48pub(crate) enum Scalar {
49 Null,
51 Bool(bool),
53 Integer(i64),
55 Unsigned(u64),
57 Float(Finite),
59 String {
61 text: String,
63 style: StringStyle,
65 },
66}
67
68impl Scalar {
69 pub(crate) fn check(&self, format: Format) -> Result<(), ValueError> {
75 match self {
76 Self::Null => format.require(Feature::Null),
77 Self::Unsigned(_) => format.require(Feature::LargeInteger),
78 Self::String {
79 text,
80 style: StringStyle::Literal,
81 } => {
82 format.require(Feature::LiteralString)?;
83 if format.reproduces_literal(text) {
84 Ok(())
85 } else {
86 Err(format.reject(Feature::LiteralString))
87 }
88 }
89 Self::Bool(_)
90 | Self::Integer(_)
91 | Self::Float(_)
92 | Self::String {
93 text: _,
94 style: StringStyle::Plain,
95 } => Ok(()),
96 }
97 }
98
99 pub(crate) fn number(number: &VNumber) -> Result<Self, ValueError> {
106 if number.is_float() {
107 let value = number.to_f64().ok_or(ValueError::UnsupportedKind)?;
108 return Finite::new(value).map(Self::Float);
109 }
110 if let Some(value) = number.to_i64() {
111 return Ok(Self::Integer(value));
112 }
113 number
114 .to_u64()
115 .map(Self::Unsigned)
116 .ok_or(ValueError::IntegerOutOfRange)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn whole_floats_stay_floats() {
126 let two = Scalar::number(&VNumber::from_f64(2.0));
127 assert_eq!(two, Ok(Scalar::Float(Finite::new(2.0).unwrap())));
128 let big = 2.0f64.powi(63);
129 let two_63 = Scalar::number(&VNumber::from_f64(big));
130 assert_eq!(two_63, Ok(Scalar::Float(Finite::new(big).unwrap())));
131 }
132
133 #[test]
134 fn integers_above_u64_are_rejected() {
135 let huge = VNumber::from_u128(u128::from(u64::MAX) + 1);
136 assert_eq!(Scalar::number(&huge), Err(ValueError::IntegerOutOfRange));
137 }
138}