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, ScalarType};
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 ScalarType)>,
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 ScalarType::Bool => Ok(Datum::from(parse_litval::<bool>(litval, "bool")?)),
63 ScalarType::Numeric { .. } => {
64 Ok(Datum::from(parse_litval::<Numeric>(litval, "Numeric")?))
65 }
66 ScalarType::Int16 => Ok(Datum::from(parse_litval::<i16>(litval, "i16")?)),
67 ScalarType::Int32 => Ok(Datum::from(parse_litval::<i32>(litval, "i32")?)),
68 ScalarType::Int64 => Ok(Datum::from(parse_litval::<i64>(litval, "i64")?)),
69 ScalarType::Float32 => Ok(Datum::from(parse_litval::<f32>(litval, "f32")?)),
70 ScalarType::Float64 => Ok(Datum::from(parse_litval::<f64>(litval, "f64")?)),
71 ScalarType::String => Ok(Datum::from(
72 temp_storage.push_string(mz_lowertest::unquote(litval)),
73 )),
74 ScalarType::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 ScalarType::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<ScalarType, String>
122where
123 I: Iterator<Item = TokenTree>,
124{
125 let typ: Option<ScalarType> = deserialize_optional_generic(scalar_type_stream, "ScalarType")?;
126 match typ {
127 Some(typ) => Ok(typ),
128 None => {
129 if ["true", "false", "null"].contains(&litval) {
130 Ok(ScalarType::Bool)
131 } else if litval.starts_with('\"') {
132 Ok(ScalarType::String)
133 } else if litval.contains('.') {
134 Ok(ScalarType::Float64)
135 } else {
136 Ok(ScalarType::Int64)
137 }
138 }
139 }
140}
141
142pub fn extract_literal_string<I>(
157 first_arg: &TokenTree,
158 rest_of_stream: &mut I,
159) -> Result<Option<String>, String>
160where
161 I: Iterator<Item = TokenTree>,
162{
163 match first_arg {
164 TokenTree::Ident(ident) => {
165 if ["true", "false", "null"].contains(&&ident.to_string()[..]) {
166 Ok(Some(ident.to_string()))
167 } else {
168 Ok(None)
169 }
170 }
171 TokenTree::Literal(literal) => Ok(Some(literal.to_string())),
172 TokenTree::Punct(punct) if punct.as_char() == '-' => {
173 match rest_of_stream.next() {
174 Some(TokenTree::Literal(literal)) => {
175 Ok(Some(format!("{}{}", punct.as_char(), literal)))
176 }
177 None => Ok(None),
178 Some(other) => Err(format!(
181 "`{}` `{}` is not a valid literal",
182 punct.as_char(),
183 other
184 )),
185 }
186 }
187 _ => Ok(None),
188 }
189}
190
191pub fn parse_vec_of_literals(token: &TokenTree) -> Result<Vec<String>, String> {
195 match token {
196 TokenTree::Group(group) => {
197 let mut inner_iter = group.stream().into_iter();
198 let mut result = Vec::new();
199 while let Some(symbol) = inner_iter.next() {
200 match extract_literal_string(&symbol, &mut inner_iter)? {
201 Some(dat) => result.push(dat),
202 None => {
203 return Err(format!(
204 "TokenTree `{}` cannot be interpreted as a literal.",
205 symbol
206 ));
207 }
208 }
209 }
210 Ok(result)
211 }
212 invalid => Err(format!(
213 "TokenTree `{}` cannot be parsed as a vec of literals",
214 invalid
215 )),
216 }
217}