1use std::{borrow::Cow, convert::TryFrom, fmt, sync::Arc};
2
3use crate::diagnostic::{DiagnosticMessage, Label, Note, Urls};
4use crate::value::{Value, ValueRegex};
5use bytes::Bytes;
6use chrono::{DateTime, SecondsFormat, Utc};
7use ordered_float::NotNan;
8use regex::Regex;
9
10use crate::compiler::{
11 Context, Expression, Span, TypeDef,
12 expression::Resolved,
13 state::{TypeInfo, TypeState},
14};
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum Literal {
18 String(Bytes),
19 Integer(i64),
20 Float(NotNan<f64>),
21 Boolean(bool),
22 Regex(ValueRegex),
23 Timestamp(DateTime<Utc>),
24 Null,
25}
26
27impl Literal {
28 pub fn to_value(&self) -> Value {
35 use Literal::{Boolean, Float, Integer, Null, Regex, String, Timestamp};
36
37 match self {
38 String(v) => Value::Bytes(v.clone()),
39 Integer(v) => Value::Integer(*v),
40 Float(v) => Value::Float(*v),
41 Boolean(v) => Value::Boolean(*v),
42 Regex(v) => Value::Regex(v.clone()),
43 Timestamp(v) => Value::Timestamp(*v),
44 Null => Value::Null,
45 }
46 }
47}
48
49impl Expression for Literal {
50 fn resolve(&self, _: &mut Context) -> Resolved {
51 Ok(self.to_value())
52 }
53
54 fn resolve_constant(&self, _state: &TypeState) -> Option<Value> {
55 Some(self.to_value())
56 }
57
58 fn type_info(&self, state: &TypeState) -> TypeInfo {
59 use Literal::{Boolean, Float, Integer, Null, Regex, String, Timestamp};
60
61 let type_def = match self {
62 String(_) => TypeDef::bytes(),
63 Integer(_) => TypeDef::integer(),
64 Float(_) => TypeDef::float(),
65 Boolean(_) => TypeDef::boolean(),
66 Regex(_) => TypeDef::regex(),
67 Timestamp(_) => TypeDef::timestamp(),
68 Null => TypeDef::null(),
69 };
70
71 TypeInfo::new(state, type_def.infallible())
72 }
73}
74
75impl fmt::Display for Literal {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 use Literal::{Boolean, Float, Integer, Null, Regex, String, Timestamp};
78
79 match self {
80 String(v) => write!(f, r#""{}""#, std::string::String::from_utf8_lossy(v)),
81 Integer(v) => v.fmt(f),
82 Float(v) => v.fmt(f),
83 Boolean(v) => v.fmt(f),
84 Regex(v) => v.fmt(f),
85 Timestamp(v) => write!(f, "t'{}'", v.to_rfc3339_opts(SecondsFormat::AutoSi, true)),
86 Null => f.write_str("null"),
87 }
88 }
89}
90
91impl From<Bytes> for Literal {
94 fn from(v: Bytes) -> Self {
95 Literal::String(v)
96 }
97}
98
99impl From<Cow<'_, str>> for Literal {
100 fn from(v: Cow<'_, str>) -> Self {
101 v.as_ref().into()
102 }
103}
104
105impl From<Vec<u8>> for Literal {
106 fn from(v: Vec<u8>) -> Self {
107 v.as_slice().into()
108 }
109}
110
111impl From<&[u8]> for Literal {
112 fn from(v: &[u8]) -> Self {
113 Literal::String(Bytes::copy_from_slice(v))
114 }
115}
116
117impl From<String> for Literal {
118 fn from(v: String) -> Self {
119 Literal::String(v.into())
120 }
121}
122
123impl From<&str> for Literal {
124 fn from(v: &str) -> Self {
125 Literal::String(Bytes::copy_from_slice(v.as_bytes()))
126 }
127}
128
129impl From<i8> for Literal {
132 fn from(v: i8) -> Self {
133 Literal::Integer(i64::from(v))
134 }
135}
136
137impl From<i16> for Literal {
138 fn from(v: i16) -> Self {
139 Literal::Integer(i64::from(v))
140 }
141}
142
143impl From<i32> for Literal {
144 fn from(v: i32) -> Self {
145 Literal::Integer(i64::from(v))
146 }
147}
148
149impl From<i64> for Literal {
150 fn from(v: i64) -> Self {
151 Literal::Integer(v)
152 }
153}
154
155impl From<u16> for Literal {
156 fn from(v: u16) -> Self {
157 Literal::Integer(i64::from(v))
158 }
159}
160
161impl From<u32> for Literal {
162 fn from(v: u32) -> Self {
163 Literal::Integer(i64::from(v))
164 }
165}
166
167impl From<u64> for Literal {
168 fn from(v: u64) -> Self {
169 #[allow(clippy::cast_possible_wrap)]
170 Literal::Integer(v as i64)
171 }
172}
173
174impl From<usize> for Literal {
175 fn from(v: usize) -> Self {
176 #[allow(clippy::cast_possible_wrap)]
177 Literal::Integer(v as i64)
178 }
179}
180
181impl From<NotNan<f64>> for Literal {
184 fn from(v: NotNan<f64>) -> Self {
185 Literal::Float(v)
186 }
187}
188
189impl TryFrom<f64> for Literal {
190 type Error = Error;
191
192 fn try_from(v: f64) -> Result<Self, Self::Error> {
193 Ok(Literal::Float(NotNan::new(v).map_err(|_| Error {
194 span: Span::default(),
195 variant: ErrorVariant::NanFloat,
196 })?))
197 }
198}
199
200impl From<bool> for Literal {
203 fn from(v: bool) -> Self {
204 Literal::Boolean(v)
205 }
206}
207
208impl From<Arc<Regex>> for Literal {
211 fn from(regex: Arc<Regex>) -> Self {
212 Literal::Regex(ValueRegex::new(regex))
213 }
214}
215
216impl From<ValueRegex> for Literal {
217 fn from(regex: ValueRegex) -> Self {
218 Literal::Regex(regex)
219 }
220}
221
222impl From<()> for Literal {
225 fn from((): ()) -> Self {
226 Literal::Null
227 }
228}
229
230impl<T: Into<Literal>> From<Option<T>> for Literal {
231 fn from(literal: Option<T>) -> Self {
232 match literal {
233 None => Literal::Null,
234 Some(v) => v.into(),
235 }
236 }
237}
238
239impl From<DateTime<Utc>> for Literal {
242 fn from(dt: DateTime<Utc>) -> Self {
243 Literal::Timestamp(dt)
244 }
245}
246
247#[derive(Debug)]
250pub struct Error {
251 pub(crate) variant: ErrorVariant,
252 span: Span,
253}
254
255#[derive(thiserror::Error, Debug)]
256pub(crate) enum ErrorVariant {
257 #[error("invalid regular expression")]
258 InvalidRegex(#[from] regex::Error),
259
260 #[error("invalid timestamp")]
261 InvalidTimestamp(#[from] chrono::ParseError),
262
263 #[error("float literal can't be NaN")]
264 NanFloat,
265}
266
267impl fmt::Display for Error {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write!(f, "{:#}", self.variant)
270 }
271}
272
273impl std::error::Error for Error {
274 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
275 Some(&self.variant)
276 }
277}
278
279impl DiagnosticMessage for Error {
280 fn code(&self) -> usize {
281 use ErrorVariant::{InvalidRegex, InvalidTimestamp, NanFloat};
282
283 match &self.variant {
284 InvalidRegex(..) => 101,
285 InvalidTimestamp(..) => 601,
286 NanFloat => 602,
287 }
288 }
289
290 fn labels(&self) -> Vec<Label> {
291 use ErrorVariant::{InvalidRegex, InvalidTimestamp, NanFloat};
292
293 match &self.variant {
294 InvalidRegex(err) => {
295 let error = err
296 .to_string()
297 .lines()
298 .filter_map(|line| {
299 if line.trim() == "^" || line == "regex parse error:" {
300 return None;
301 }
302
303 Some(line.trim_start_matches("error: ").trim())
304 })
305 .rev()
306 .collect::<Vec<_>>()
307 .join(": ");
308
309 vec![Label::primary(
310 format!("regex parse error: {error}"),
311 self.span,
312 )]
313 }
314 InvalidTimestamp(err) => vec![Label::primary(
315 format!("invalid timestamp format: {err}"),
316 self.span,
317 )],
318
319 NanFloat => vec![],
320 }
321 }
322
323 fn notes(&self) -> Vec<Note> {
324 use ErrorVariant::{InvalidRegex, InvalidTimestamp, NanFloat};
325
326 match &self.variant {
327 InvalidRegex(_) => vec![Note::SeeDocs(
328 "regular expressions".to_owned(),
329 Urls::expression_docs_url("#regular-expression"),
330 )],
331 InvalidTimestamp(_) => vec![Note::SeeDocs(
332 "timestamps".to_owned(),
333 Urls::expression_docs_url("#timestamp"),
334 )],
335 NanFloat => vec![Note::SeeDocs(
336 "floats".to_owned(),
337 Urls::expression_docs_url("#float"),
338 )],
339 }
340 }
341}
342
343impl From<(Span, regex::Error)> for Error {
344 fn from((span, err): (Span, regex::Error)) -> Self {
345 Self {
346 variant: err.into(),
347 span,
348 }
349 }
350}
351
352impl From<(Span, chrono::ParseError)> for Error {
353 fn from((span, err): (Span, chrono::ParseError)) -> Self {
354 Self {
355 variant: err.into(),
356 span,
357 }
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use crate::compiler::TypeDef;
364 use crate::{expr, test_type_def};
365
366 test_type_def![
367 bytes {
368 expr: |_| expr!("foo"),
369 want: TypeDef::bytes(),
370 }
371
372 integer {
373 expr: |_| expr!(12),
374 want: TypeDef::integer(),
375 }
376 ];
377}