1#![forbid(unsafe_code)]
2
3#[cfg(doctest)]
4doc_comment::doctest!("../README.md");
5
6use serde::{Deserialize, Serialize};
7use std::{
8 convert::{TryFrom, TryInto},
9 ops::Add,
10 ops::AddAssign,
11};
12use ux_serde::i54 as ux_i54;
13
14#[derive(thiserror::Error, Debug)]
15#[allow(non_camel_case_types)]
16pub enum i54Error {
17 #[error("i54 conversion failed")]
18 ConversionFailed,
19}
20
21pub const MAX_SAFE_INTEGER: i64 = 9007199254740991;
22pub const MIN_SAFE_INTEGER: i64 = -9007199254740991;
23
24impl std::str::FromStr for i54 {
25 type Err = String;
26 fn from_str(_value: &str) -> Result<Self, Self::Err> {
27 unimplemented!()
28 }
29}
30
31impl std::fmt::Display for i54 {
33 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
34 write!(f, "{}", self.0)
35 }
36}
37
38#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
39#[allow(non_camel_case_types)]
40pub struct i54(ux_i54);
41
42impl<T> PartialEq<T> for i54
43where
44 T: TryInto<i54> + Copy + PartialEq + Ord,
45{
46 fn eq(&self, other: &T) -> bool {
47 match (*other).try_into() {
48 Ok(v) => v.0 == self.0,
49 Err(_) => false,
50 }
51 }
52}
53
54impl PartialEq for i54 {
55 fn eq(&self, other: &i54) -> bool {
56 self.0 == other.0
57 }
58}
59
60impl Eq for i54 {}
61
62#[cfg(feature = "juniper")]
63#[juniper::graphql_scalar(
64 description = "i54: 54-bit signed integer abstraction; represented as `i54`/`i64` in Rust, `Float` in GraphQL, `number` in TypeScript."
65)]
66impl<S> GraphQLScalar for i54
67where
68 S: ScalarValue,
69{
70 fn resolve(&self) -> Value {
72 let val: i64 = self.0.into();
73 juniper::Value::scalar(val as f64)
74 }
75
76 fn from_input_value(v: &InputValue) -> Option<i54> {
78 v.as_float_value()?.try_into().ok()
79 }
80
81 fn from_str<'a>(value: ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> {
83 <String as juniper::ParseScalarValue<S>>::from_str(value)
84 }
85}
86
87impl From<i32> for i54 {
88 fn from(item: i32) -> Self {
89 i54(ux_i54::new(item as i64))
90 }
91}
92
93impl TryFrom<i64> for i54 {
94 type Error = i54Error;
95 fn try_from(item: i64) -> Result<Self, Self::Error> {
96 let item_i64 = item as i64;
97 let item_i54 = i54(ux_i54::new(item_i64));
98 if item_i54.as_i64() as i64 != item {
99 return Err(i54Error::ConversionFailed);
100 }
101
102 Ok(item_i54)
103 }
104}
105
106impl TryFrom<usize> for i54 {
107 type Error = i54Error;
108 fn try_from(item: usize) -> Result<Self, Self::Error> {
109 let item_i64 = item as i64;
110 let item_i54 = i54(ux_i54::new(item_i64));
111 if item_i54.as_i64() as usize != item {
112 return Err(i54Error::ConversionFailed);
113 }
114
115 Ok(item_i54)
116 }
117}
118
119impl TryFrom<i128> for i54 {
120 type Error = i54Error;
121 fn try_from(item: i128) -> Result<Self, Self::Error> {
122 let item_i128 = item as i128;
123 let item_i54 = i54(ux_i54::new(item_i128 as i64));
124 if item_i54.as_i64() as i128 != item {
125 return Err(i54Error::ConversionFailed);
126 }
127
128 Ok(item_i54)
129 }
130}
131
132impl TryFrom<u128> for i54 {
133 type Error = i54Error;
134 fn try_from(item: u128) -> Result<Self, Self::Error> {
135 let item_u128 = item as u128;
136 let item_i54 = i54(ux_i54::new(item_u128 as i64));
137 if item_i54.as_i64() as u128 != item {
138 return Err(i54Error::ConversionFailed);
139 }
140
141 Ok(item_i54)
142 }
143}
144
145impl TryFrom<f64> for i54 {
146 type Error = i54Error;
147 fn try_from(item: f64) -> Result<Self, Self::Error> {
148 let item_i64 = item as i64;
149 let item_i54 = i54(ux_i54::new(item_i64));
150 let item_f64 = item_i54.as_i64() as f64;
151 if item_f64.to_ne_bytes() != item.to_ne_bytes() {
152 return Err(i54Error::ConversionFailed);
153 }
154
155 Ok(item_i54)
156 }
157}
158
159impl i54 {
160 pub fn as_i64(&self) -> i64 {
161 self.0.into()
162 }
163}
164
165impl AddAssign for i54 {
166 fn add_assign(&mut self, other: Self) {
167 *self = Self(self.0 + other.0);
168 }
169}
170
171impl Add for i54 {
172 type Output = Self;
173
174 fn add(self, other: Self) -> Self {
175 Self(self.0 + other.0)
176 }
177}
178
179#[cfg(feature = "rusqlite")]
180impl rusqlite::types::FromSql for i54 {
181 fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
182 value
183 .as_i64()?
184 .try_into()
185 .map_err(|_| rusqlite::types::FromSqlError::InvalidType)
186 }
187}
188
189#[cfg(feature = "rusqlite")]
190impl rusqlite::types::ToSql for i54 {
191 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
192 Ok(rusqlite::types::ToSqlOutput::Owned(
193 rusqlite::types::Value::Integer(self.0.into()),
194 ))
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_rectangle() {
204 let i: i54 = 20_usize.try_into().unwrap();
206 assert_eq!(i, 20_i32)
207 }
208}