1use arrow::datatypes::ArrowPrimitiveType;
2use std::ops::Bound;
3
4#[derive(Debug, Clone, PartialEq)]
8pub enum Literal {
9 Integer(i128),
10 Float(f64),
11 String(String),
12 }
14
15macro_rules! impl_from_for_literal {
16 ($variant:ident, $($t:ty),*) => {
17 $(
18 impl From<$t> for Literal {
19 fn from(v: $t) -> Self {
20 Literal::$variant(v.into())
21 }
22 }
23 )*
24 };
25}
26
27impl_from_for_literal!(Integer, i8, i16, i32, i64, i128, u8, u16, u32, u64);
28impl_from_for_literal!(Float, f32, f64);
29
30impl From<&str> for Literal {
31 fn from(v: &str) -> Self {
32 Literal::String(v.to_string())
33 }
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub enum LiteralCastError {
39 TypeMismatch {
41 expected: &'static str,
42 got: &'static str,
43 },
44 OutOfRange { target: &'static str, value: i128 },
46 FloatOutOfRange { target: &'static str, value: f64 },
48}
49
50impl std::fmt::Display for LiteralCastError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 LiteralCastError::TypeMismatch { expected, got } => {
54 write!(f, "expected {}, got {}", expected, got)
55 }
56 LiteralCastError::OutOfRange { target, value } => {
57 write!(f, "value {} out of range for {}", value, target)
58 }
59 LiteralCastError::FloatOutOfRange { target, value } => {
60 write!(f, "value {} out of range for {}", value, target)
61 }
62 }
63 }
64}
65
66impl std::error::Error for LiteralCastError {}
67
68pub trait FromLiteral: Sized {
70 fn from_literal(lit: &Literal) -> Result<Self, LiteralCastError>;
71}
72
73macro_rules! impl_from_literal_int {
74 ($($ty:ty),* $(,)?) => {
75 $(
76 impl FromLiteral for $ty {
77 fn from_literal(lit: &Literal) -> Result<Self, LiteralCastError> {
78 match lit {
79 Literal::Integer(i) => <$ty>::try_from(*i).map_err(|_| {
80 LiteralCastError::OutOfRange {
81 target: std::any::type_name::<$ty>(),
82 value: *i,
83 }
84 }),
85 Literal::Float(_) => Err(LiteralCastError::TypeMismatch {
86 expected: "integer",
87 got: "float",
88 }),
89 Literal::String(_) => Err(LiteralCastError::TypeMismatch {
90 expected: "integer",
91 got: "string",
92 }),
93 }
94 }
95 }
96 )*
97 };
98}
99
100impl_from_literal_int!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize);
101
102impl FromLiteral for f32 {
103 fn from_literal(lit: &Literal) -> Result<Self, LiteralCastError> {
104 let value = match lit {
105 Literal::Float(f) => *f,
106 Literal::Integer(i) => *i as f64,
107 Literal::String(_) => {
108 return Err(LiteralCastError::TypeMismatch {
109 expected: "float",
110 got: "string",
111 });
112 }
113 };
114 let cast = value as f32;
115 if value.is_finite() && !cast.is_finite() {
116 return Err(LiteralCastError::FloatOutOfRange {
117 target: "f32",
118 value,
119 });
120 }
121 Ok(cast)
122 }
123}
124
125impl FromLiteral for bool {
126 fn from_literal(lit: &Literal) -> Result<Self, LiteralCastError> {
127 match lit {
128 Literal::Integer(i) => match *i {
129 0 => Ok(false),
130 1 => Ok(true),
131 value => Err(LiteralCastError::OutOfRange {
132 target: "bool",
133 value,
134 }),
135 },
136 Literal::Float(_) => Err(LiteralCastError::TypeMismatch {
137 expected: "bool",
138 got: "float",
139 }),
140 Literal::String(s) => {
141 let normalized = s.trim().to_ascii_lowercase();
142 match normalized.as_str() {
143 "true" | "t" | "1" => Ok(true),
144 "false" | "f" | "0" => Ok(false),
145 _ => Err(LiteralCastError::TypeMismatch {
146 expected: "bool",
147 got: "string",
148 }),
149 }
150 }
151 }
152 }
153}
154
155impl FromLiteral for f64 {
156 fn from_literal(lit: &Literal) -> Result<Self, LiteralCastError> {
157 match lit {
158 Literal::Float(f) => Ok(*f),
159 Literal::Integer(i) => Ok(*i as f64),
160 Literal::String(_) => Err(LiteralCastError::TypeMismatch {
161 expected: "float",
162 got: "string",
163 }),
164 }
165 }
166}
167
168fn literal_type_name(lit: &Literal) -> &'static str {
169 match lit {
170 Literal::Integer(_) => "integer",
171 Literal::Float(_) => "float",
172 Literal::String(_) => "string",
173 }
174}
175
176pub fn literal_to_string(lit: &Literal) -> Result<String, LiteralCastError> {
178 match lit {
179 Literal::String(s) => Ok(s.clone()),
180 _ => Err(LiteralCastError::TypeMismatch {
181 expected: "string",
182 got: literal_type_name(lit),
183 }),
184 }
185}
186
187pub fn literal_to_native<T>(lit: &Literal) -> Result<T, LiteralCastError>
189where
190 T: FromLiteral + Copy + 'static,
191{
192 T::from_literal(lit)
193}
194
195pub fn bound_to_native<T>(bound: &Bound<Literal>) -> Result<Bound<T::Native>, LiteralCastError>
202where
203 T: ArrowPrimitiveType,
204 T::Native: FromLiteral + Copy,
205{
206 Ok(match bound {
207 Bound::Unbounded => Bound::Unbounded,
208 Bound::Included(l) => Bound::Included(literal_to_native::<T::Native>(l)?),
209 Bound::Excluded(l) => Bound::Excluded(literal_to_native::<T::Native>(l)?),
210 })
211}