1515// specific language governing permissions and limitations
1616// under the License.
1717
18+ use std:: collections:: HashSet ;
1819use std:: fmt:: { self , Display } ;
1920use std:: str:: FromStr ;
2021
2122use arrow:: compute:: CastOptions ;
2223use arrow:: util:: display:: { DurationFormat , FormatOptions } ;
24+ use itertools:: Itertools ;
25+ use strum:: EnumString ;
2326
2427use crate :: config:: { ConfigField , Visit } ;
2528use crate :: error:: { DataFusionError , Result } ;
@@ -222,14 +225,26 @@ impl ConfigField for ExplainFormat {
222225/// (when specified) or aggregated metrics are displayed (when omitted).
223226/// In contrast, `MetricType` determines which *levels* of metrics are
224227/// displayed.
225- #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
228+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash , strum:: Display , EnumString ) ]
229+ #[ strum(
230+ serialize_all = "lowercase" ,
231+ ascii_case_insensitive,
232+ parse_err_ty = DataFusionError ,
233+ parse_err_fn = metric_type_parse_error
234+ ) ]
226235pub enum MetricType {
227236 /// Common metrics for high-level insights (answering which operator is slow)
228237 Summary ,
229238 /// For deep operator-level introspection for developers
230239 Dev ,
231240}
232241
242+ fn metric_type_parse_error ( s : & str ) -> DataFusionError {
243+ DataFusionError :: Configuration ( format ! (
244+ "Invalid explain analyze level '{s}'. Expected 'summary' or 'dev'."
245+ ) )
246+ }
247+
233248impl MetricType {
234249 /// Returns the set of metric types that should be shown for this level.
235250 ///
@@ -243,36 +258,13 @@ impl MetricType {
243258 }
244259}
245260
246- impl FromStr for MetricType {
247- type Err = DataFusionError ;
248-
249- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
250- match s. trim ( ) . to_lowercase ( ) . as_str ( ) {
251- "summary" => Ok ( Self :: Summary ) ,
252- "dev" => Ok ( Self :: Dev ) ,
253- other => Err ( DataFusionError :: Configuration ( format ! (
254- "Invalid explain analyze level. Expected 'summary' or 'dev'. Got '{other}'"
255- ) ) ) ,
256- }
257- }
258- }
259-
260- impl Display for MetricType {
261- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
262- match self {
263- Self :: Summary => write ! ( f, "summary" ) ,
264- Self :: Dev => write ! ( f, "dev" ) ,
265- }
266- }
267- }
268-
269261impl ConfigField for MetricType {
270262 fn visit < V : Visit > ( & self , v : & mut V , key : & str , description : & ' static str ) {
271263 v. some ( key, self , description)
272264 }
273265
274266 fn set ( & mut self , _: & str , value : & str ) -> Result < ( ) > {
275- * self = MetricType :: from_str ( value) ?;
267+ * self = value. parse ( ) ?;
276268 Ok ( ( ) )
277269 }
278270}
@@ -301,7 +293,13 @@ impl ConfigField for MetricType {
301293/// Metrics that do not declare a category (the default for custom
302294/// `Count` / `Gauge` metrics) are treated as
303295/// [`Uncategorized`](Self::Uncategorized) for filtering purposes.
304- #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
296+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash , strum:: Display , EnumString ) ]
297+ #[ strum(
298+ serialize_all = "lowercase" ,
299+ ascii_case_insensitive,
300+ parse_err_ty = DataFusionError ,
301+ parse_err_fn = metric_category_parse_error
302+ ) ]
305303pub enum MetricCategory {
306304 /// Row counts and related dimensionless counters: `output_rows`,
307305 /// `spilled_rows`, `output_batches`, pruning metrics, ratios, etc.
@@ -333,32 +331,11 @@ pub enum MetricCategory {
333331 Uncategorized ,
334332}
335333
336- impl FromStr for MetricCategory {
337- type Err = DataFusionError ;
338-
339- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
340- match s. trim ( ) . to_lowercase ( ) . as_str ( ) {
341- "rows" => Ok ( Self :: Rows ) ,
342- "bytes" => Ok ( Self :: Bytes ) ,
343- "timing" => Ok ( Self :: Timing ) ,
344- "uncategorized" => Ok ( Self :: Uncategorized ) ,
345- other => Err ( DataFusionError :: Configuration ( format ! (
346- "Invalid metric category '{other}'. \
347- Expected 'rows', 'bytes', 'timing', or 'uncategorized'."
348- ) ) ) ,
349- }
350- }
351- }
352-
353- impl Display for MetricCategory {
354- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
355- match self {
356- Self :: Rows => write ! ( f, "rows" ) ,
357- Self :: Bytes => write ! ( f, "bytes" ) ,
358- Self :: Timing => write ! ( f, "timing" ) ,
359- Self :: Uncategorized => write ! ( f, "uncategorized" ) ,
360- }
361- }
334+ fn metric_category_parse_error ( s : & str ) -> DataFusionError {
335+ DataFusionError :: Configuration ( format ! (
336+ "Invalid metric category '{s}'. \
337+ Expected 'rows', 'bytes', 'timing', or 'uncategorized'."
338+ ) )
362339}
363340
364341/// Controls which [`MetricCategory`] values are shown in `EXPLAIN ANALYZE`.
@@ -389,12 +366,11 @@ impl FromStr for ExplainAnalyzeCategories {
389366 "all" => Ok ( Self :: All ) ,
390367 "none" => Ok ( Self :: Only ( vec ! [ ] ) ) ,
391368 other => {
392- let mut cats = Vec :: new ( ) ;
369+ let mut cats = HashSet :: new ( ) ;
393370 for part in other. split ( ',' ) {
394- cats. push ( part. trim ( ) . parse :: < MetricCategory > ( ) ?) ;
371+ cats. insert ( part. trim ( ) . parse :: < MetricCategory > ( ) ?) ;
395372 }
396- cats. dedup ( ) ;
397- Ok ( Self :: Only ( cats) )
373+ Ok ( Self :: Only ( cats. into_iter ( ) . collect_vec ( ) ) )
398374 }
399375 }
400376 }
0 commit comments