1use chrono::NaiveDateTime;
15use mz_lowertest::deserialize_optional_generic;
16use mz_ore::str::StrExt;
17use mz_repr::adt::numeric::Numeric;
18use mz_repr::adt::timestamp::CheckedTimestamp;
19use mz_repr::strconv::parse_jsonb;
20use mz_repr::{Datum, Row, RowArena, SqlScalarType};
21use proc_macro2::TokenTree;
22
23fn parse_litval<'a, F>(litval: &'a str, littyp: &str) -> Result<F, String>
26where
27 F: std::str::FromStr,
28 F::Err: ToString,
29{
30 litval.parse::<F>().map_err(|e| {
31 format!(
32 "error when parsing {} into {}: {}",
33 litval,
34 littyp,
35 e.to_string()
36 )
37 })
38}
39
40pub fn test_spec_to_row<'a, I>(datum_iter: I) -> Result<Row, String>
53where
54 I: Iterator<Item = (&'a str, &'a SqlScalarType)>,
55{
56 let temp_storage = RowArena::new();
57 Row::try_pack(datum_iter.map(|(litval, littyp)| {
58 if litval == "null" {
59 Ok(Datum::Null)
60 } else {
61 match littyp {
62 SqlScalarType::Bool => Ok(Datum::from(parse_litval::<bool>(litval, "bool")?)),
63 SqlScalarType::Numeric { .. } => {
64 Ok(Datum::from(parse_litval::<Numeric>(litval, "Numeric")?))
65 }
66 SqlScalarType::Int16 => Ok(Datum::from(parse_litval::<i16>(litval, "i16")?)),
67 SqlScalarType::Int32 => Ok(Datum::from(parse_litval::<i32>(litval, "i32")?)),
68 SqlScalarType::Int64 => Ok(Datum::from(parse_litval::<i64>(litval, "i64")?)),
69 SqlScalarType::Float32 => Ok(Datum::from(parse_litval::<f32>(litval, "f32")?)),
70 SqlScalarType::Float64 => Ok(Datum::from(parse_litval::<f64>(litval, "f64")?)),
71 SqlScalarType::String => Ok(Datum::from(
72 temp_storage.push_string(mz_lowertest::unquote(litval)),
73 )),
74 SqlScalarType::Timestamp { .. } => {
75 let datetime = if litval.contains('.') {
76 NaiveDateTime::parse_from_str(litval, "\"%Y-%m-%d %H:%M:%S%.f\"")
77 } else {
78 NaiveDateTime::parse_from_str(litval, "\"%Y-%m-%d %H:%M:%S\"")
79 };
80 Ok(Datum::from(
81 CheckedTimestamp::from_timestamplike(
82 datetime
83 .map_err(|e| format!("Error while parsing NaiveDateTime: {}", e))?,
84 )
85 .unwrap(),
86 ))
87 }
88 SqlScalarType::Jsonb => parse_jsonb(&mz_lowertest::unquote(litval))
89 .map(|jsonb| temp_storage.push_unary_row(jsonb.into_row()))
90 .map_err(|parse| format!("Invalid JSON literal: {:?}", parse)),
91 _ => Err(format!("Unsupported literal type {:?}", littyp)),
92 }
93 }
94 }))
95}
96
97pub fn datum_to_test_spec(datum: Datum) -> String {
102 let result = format!("{}", datum);
103 match datum {
104 Datum::Timestamp(_) => result.quoted().to_string(),
105 _ => result,
106 }
107}
108
109pub fn get_scalar_type_or_default<I>(
119 litval: &str,
120 scalar_type_stream: &mut I,
121) -> Result<SqlScalarType, String>
122where
123 I: Iterator<Item = TokenTree>,
124{
125 let typ: Option<SqlScalarType> =
126 deserialize_optional_generic(scalar_type_stream, "SqlScalarType")?;
127 match typ {
128 Some(typ) => Ok(typ),
129 None => {
130 if ["true", "false", "null"].contains(&litval) {
131 Ok(SqlScalarType::Bool)
132 } else if litval.starts_with('\"') {
133 Ok(SqlScalarType::String)
134 } else if litval.contains('.') {
135 Ok(SqlScalarType::Float64)
136 } else {
137 Ok(SqlScalarType::Int64)
138 }
139 }
140 }
141}
142
143pub fn extract_literal_string<I>(
158 first_arg: &TokenTree,
159 rest_of_stream: &mut I,
160) -> Result<Option<String>, String>
161where
162 I: Iterator<Item = TokenTree>,
163{
164 match first_arg {
165 TokenTree::Ident(ident) => {
166 if ["true", "false", "null"].contains(&&ident.to_string()[..]) {
167 Ok(Some(ident.to_string()))
168 } else {
169 Ok(None)
170 }
171 }
172 TokenTree::Literal(literal) => Ok(Some(literal.to_string())),
173 TokenTree::Punct(punct) if punct.as_char() == '-' => {
174 match rest_of_stream.next() {
175 Some(TokenTree::Literal(literal)) => {
176 Ok(Some(format!("{}{}", punct.as_char(), literal)))
177 }
178 None => Ok(None),
179 Some(other) => Err(format!(
182 "`{}` `{}` is not a valid literal",
183 punct.as_char(),
184 other
185 )),
186 }
187 }
188 _ => Ok(None),
189 }
190}
191
192pub fn parse_vec_of_literals(token: &TokenTree) -> Result<Vec<String>, String> {
196 match token {
197 TokenTree::Group(group) => {
198 let mut inner_iter = group.stream().into_iter();
199 let mut result = Vec::new();
200 while let Some(symbol) = inner_iter.next() {
201 match extract_literal_string(&symbol, &mut inner_iter)? {
202 Some(dat) => result.push(dat),
203 None => {
204 return Err(format!(
205 "TokenTree `{}` cannot be interpreted as a literal.",
206 symbol
207 ));
208 }
209 }
210 }
211 Ok(result)
212 }
213 invalid => Err(format!(
214 "TokenTree `{}` cannot be parsed as a vec of literals",
215 invalid
216 )),
217 }
218}