Skip to main content

datafusion_sql/unparser/
ast.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use core::fmt;
19use std::ops::ControlFlow;
20
21use sqlparser::ast::helpers::attached_token::AttachedToken;
22use sqlparser::ast::{
23    self, LimitClause, OrderByKind, SelectFlavor, visit_expressions_mut,
24};
25
26#[derive(Clone)]
27pub struct QueryBuilder {
28    with: Option<ast::With>,
29    body: Option<Box<ast::SetExpr>>,
30    order_by_kind: Option<OrderByKind>,
31    limit: Option<ast::Expr>,
32    limit_by: Vec<ast::Expr>,
33    offset: Option<ast::Offset>,
34    fetch: Option<ast::Fetch>,
35    locks: Vec<ast::LockClause>,
36    for_clause: Option<ast::ForClause>,
37    // If true, we need to unparse LogicalPlan::Union as a SQL `UNION` rather than a `UNION ALL`.
38    distinct_union: bool,
39}
40
41impl QueryBuilder {
42    pub fn with(&mut self, value: Option<ast::With>) -> &mut Self {
43        self.with = value;
44        self
45    }
46    pub fn body(&mut self, value: Box<ast::SetExpr>) -> &mut Self {
47        self.body = Some(value);
48        self
49    }
50    pub fn take_body(&mut self) -> Option<Box<ast::SetExpr>> {
51        self.body.take()
52    }
53    pub fn order_by(&mut self, value: OrderByKind) -> &mut Self {
54        self.order_by_kind = Some(value);
55        self
56    }
57    pub fn limit(&mut self, value: Option<ast::Expr>) -> &mut Self {
58        self.limit = value;
59        self
60    }
61    pub fn limit_by(&mut self, value: Vec<ast::Expr>) -> &mut Self {
62        self.limit_by = value;
63        self
64    }
65    pub fn offset(&mut self, value: Option<ast::Offset>) -> &mut Self {
66        self.offset = value;
67        self
68    }
69    pub fn fetch(&mut self, value: Option<ast::Fetch>) -> &mut Self {
70        self.fetch = value;
71        self
72    }
73    pub fn locks(&mut self, value: Vec<ast::LockClause>) -> &mut Self {
74        self.locks = value;
75        self
76    }
77    pub fn for_clause(&mut self, value: Option<ast::ForClause>) -> &mut Self {
78        self.for_clause = value;
79        self
80    }
81    pub fn distinct_union(&mut self) -> &mut Self {
82        self.distinct_union = true;
83        self
84    }
85    pub fn is_distinct_union(&self) -> bool {
86        self.distinct_union
87    }
88    pub fn build(&self) -> Result<ast::Query, BuilderError> {
89        let order_by = self
90            .order_by_kind
91            .as_ref()
92            .map(|order_by_kind| ast::OrderBy {
93                kind: order_by_kind.clone(),
94                interpolate: None,
95            });
96
97        Ok(ast::Query {
98            with: self.with.clone(),
99            body: match self.body {
100                Some(ref value) => value.clone(),
101                None => return Err(Into::into(UninitializedFieldError::from("body"))),
102            },
103            order_by,
104            limit_clause: Some(LimitClause::LimitOffset {
105                limit: self.limit.clone(),
106                offset: self.offset.clone(),
107                limit_by: self.limit_by.clone(),
108            }),
109            fetch: self.fetch.clone(),
110            locks: self.locks.clone(),
111            for_clause: self.for_clause.clone(),
112            settings: None,
113            format_clause: None,
114            pipe_operators: vec![],
115        })
116    }
117    fn create_empty() -> Self {
118        Self {
119            with: Default::default(),
120            body: Default::default(),
121            order_by_kind: Default::default(),
122            limit: Default::default(),
123            limit_by: Default::default(),
124            offset: Default::default(),
125            fetch: Default::default(),
126            locks: Default::default(),
127            for_clause: Default::default(),
128            distinct_union: false,
129        }
130    }
131}
132impl Default for QueryBuilder {
133    fn default() -> Self {
134        Self::create_empty()
135    }
136}
137
138#[derive(Clone)]
139pub struct SelectBuilder {
140    distinct: Option<ast::Distinct>,
141    top: Option<ast::Top>,
142    /// Projection items for the SELECT clause.
143    ///
144    /// This field uses `Option` to distinguish between three distinct states:
145    /// - `None`: No projection has been set (not yet initialized)
146    /// - `Some(vec![])`: Empty projection explicitly set (generates `SELECT FROM ...` or `SELECT 1 FROM ...`)
147    /// - `Some(vec![SelectItem::Wildcard(...)])`: Wildcard projection (generates `SELECT * FROM ...`)
148    /// - `Some(vec![...])`: Non-empty projection with specific columns/expressions
149    ///
150    /// Use `projection()` to set this field and `already_projected()` to check if it has been set.
151    projection: Option<Vec<ast::SelectItem>>,
152    into: Option<ast::SelectInto>,
153    from: Vec<TableWithJoinsBuilder>,
154    lateral_views: Vec<ast::LateralView>,
155    selection: Option<ast::Expr>,
156    group_by: Option<ast::GroupByExpr>,
157    cluster_by: Vec<ast::Expr>,
158    distribute_by: Vec<ast::Expr>,
159    sort_by: Vec<ast::OrderByExpr>,
160    having: Option<ast::Expr>,
161    named_window: Vec<ast::NamedWindowDefinition>,
162    qualify: Option<ast::Expr>,
163    value_table_mode: Option<ast::ValueTableMode>,
164    flavor: Option<SelectFlavor>,
165}
166
167impl SelectBuilder {
168    pub fn distinct(&mut self, value: Option<ast::Distinct>) -> &mut Self {
169        self.distinct = value;
170        self
171    }
172    pub fn top(&mut self, value: Option<ast::Top>) -> &mut Self {
173        self.top = value;
174        self
175    }
176    pub fn projection(&mut self, value: Vec<ast::SelectItem>) -> &mut Self {
177        self.projection = Some(value);
178        self
179    }
180    pub fn pop_projections(&mut self) -> Vec<ast::SelectItem> {
181        self.projection.take().unwrap_or_default()
182    }
183    /// Returns true if a projection has been explicitly set via `projection()`.
184    ///
185    /// This method is used to determine whether the SELECT clause has already been
186    /// defined, which helps avoid creating duplicate projection nodes during query
187    /// unparsing. It returns `true` for both empty and non-empty projections.
188    ///
189    /// # Returns
190    ///
191    /// - `true` if `projection()` has been called (regardless of whether it was empty or not)
192    /// - `false` if no projection has been set yet
193    ///
194    /// # Example
195    ///
196    /// ```ignore
197    /// let mut builder = SelectBuilder::default();
198    /// assert!(!builder.already_projected());
199    ///
200    /// builder.projection(vec![]);
201    /// assert!(builder.already_projected()); // true even for empty projection
202    ///
203    /// builder.projection(vec![SelectItem::Wildcard(...)]);
204    /// assert!(builder.already_projected()); // true for non-empty projection
205    /// ```
206    pub fn already_projected(&self) -> bool {
207        self.projection.is_some()
208    }
209    pub fn into(&mut self, value: Option<ast::SelectInto>) -> &mut Self {
210        self.into = value;
211        self
212    }
213    pub fn from(&mut self, value: Vec<TableWithJoinsBuilder>) -> &mut Self {
214        self.from = value;
215        self
216    }
217    pub fn push_from(&mut self, value: TableWithJoinsBuilder) -> &mut Self {
218        self.from.push(value);
219        self
220    }
221    pub fn pop_from(&mut self) -> Option<TableWithJoinsBuilder> {
222        self.from.pop()
223    }
224    pub fn lateral_views(&mut self, value: Vec<ast::LateralView>) -> &mut Self {
225        self.lateral_views = value;
226        self
227    }
228
229    /// Replaces the selection with a new value.
230    ///
231    /// This function is used to replace a specific expression within the selection.
232    /// Unlike the `selection` method which combines existing and new selections with AND,
233    /// this method searches for and replaces occurrences of a specific expression.
234    ///
235    /// This method is primarily used to modify LEFT MARK JOIN expressions.
236    /// When processing a LEFT MARK JOIN, we need to replace the placeholder expression
237    /// with the actual join condition in the selection clause.
238    ///
239    /// # Arguments
240    ///
241    /// * `existing_expr` - The expression to replace
242    /// * `value` - The new expression to set as the selection
243    pub fn replace_mark(
244        &mut self,
245        existing_expr: &ast::Expr,
246        value: &ast::Expr,
247    ) -> &mut Self {
248        if let Some(selection) = &mut self.selection {
249            let _ = visit_expressions_mut(selection, |expr| {
250                if expr == existing_expr {
251                    *expr = value.clone();
252                }
253                ControlFlow::<()>::Continue(())
254            });
255        }
256        self
257    }
258
259    pub fn selection(&mut self, value: Option<ast::Expr>) -> &mut Self {
260        // With filter pushdown optimization, the LogicalPlan can have filters defined as part of `TableScan` and `Filter` nodes.
261        // To avoid overwriting one of the filters, we combine the existing filter with the additional filter.
262        // Example:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
263        // |  Projection: customer.c_phone AS cntrycode, customer.c_acctbal                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
264        // |   Filter: CAST(customer.c_acctbal AS Decimal128(38, 6)) > (<subquery>)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
265        // |     Subquery:
266        // |     ..                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
267        // |     TableScan: customer, full_filters=[customer.c_mktsegment = Utf8("BUILDING")]
268        match (&self.selection, value) {
269            (Some(existing_selection), Some(new_selection)) => {
270                self.selection = Some(ast::Expr::BinaryOp {
271                    left: Box::new(existing_selection.clone()),
272                    op: ast::BinaryOperator::And,
273                    right: Box::new(new_selection),
274                });
275            }
276            (None, Some(new_selection)) => {
277                self.selection = Some(new_selection);
278            }
279            (_, None) => (),
280        }
281
282        self
283    }
284    pub fn group_by(&mut self, value: ast::GroupByExpr) -> &mut Self {
285        self.group_by = Some(value);
286        self
287    }
288    pub fn cluster_by(&mut self, value: Vec<ast::Expr>) -> &mut Self {
289        self.cluster_by = value;
290        self
291    }
292    pub fn distribute_by(&mut self, value: Vec<ast::Expr>) -> &mut Self {
293        self.distribute_by = value;
294        self
295    }
296    pub fn sort_by(&mut self, value: Vec<ast::OrderByExpr>) -> &mut Self {
297        self.sort_by = value;
298        self
299    }
300    pub fn having(&mut self, value: Option<ast::Expr>) -> &mut Self {
301        self.having = value;
302        self
303    }
304    pub fn named_window(&mut self, value: Vec<ast::NamedWindowDefinition>) -> &mut Self {
305        self.named_window = value;
306        self
307    }
308    pub fn qualify(&mut self, value: Option<ast::Expr>) -> &mut Self {
309        self.qualify = value;
310        self
311    }
312    pub fn value_table_mode(&mut self, value: Option<ast::ValueTableMode>) -> &mut Self {
313        self.value_table_mode = value;
314        self
315    }
316    pub fn build(&self) -> Result<ast::Select, BuilderError> {
317        Ok(ast::Select {
318            optimizer_hint: None,
319            distinct: self.distinct.clone(),
320            select_modifiers: None,
321            top_before_distinct: false,
322            top: self.top.clone(),
323            projection: self.projection.clone().unwrap_or_default(),
324            into: self.into.clone(),
325            from: self
326                .from
327                .iter()
328                .filter_map(|b| b.build().transpose())
329                .collect::<Result<Vec<_>, BuilderError>>()?,
330            lateral_views: self.lateral_views.clone(),
331            selection: self.selection.clone(),
332            group_by: match self.group_by {
333                Some(ref value) => value.clone(),
334                None => {
335                    return Err(Into::into(UninitializedFieldError::from("group_by")));
336                }
337            },
338            cluster_by: self.cluster_by.clone(),
339            distribute_by: self.distribute_by.clone(),
340            sort_by: self.sort_by.clone(),
341            having: self.having.clone(),
342            named_window: self.named_window.clone(),
343            qualify: self.qualify.clone(),
344            value_table_mode: self.value_table_mode,
345            connect_by: Vec::new(),
346            window_before_qualify: false,
347            prewhere: None,
348            select_token: AttachedToken::empty(),
349            flavor: match self.flavor {
350                Some(ref value) => *value,
351                None => return Err(Into::into(UninitializedFieldError::from("flavor"))),
352            },
353            exclude: None,
354        })
355    }
356    fn create_empty() -> Self {
357        Self {
358            distinct: Default::default(),
359            top: Default::default(),
360            projection: None,
361            into: Default::default(),
362            from: Default::default(),
363            lateral_views: Default::default(),
364            selection: Default::default(),
365            group_by: Some(ast::GroupByExpr::Expressions(Vec::new(), Vec::new())),
366            cluster_by: Default::default(),
367            distribute_by: Default::default(),
368            sort_by: Default::default(),
369            having: Default::default(),
370            named_window: Default::default(),
371            qualify: Default::default(),
372            value_table_mode: Default::default(),
373            flavor: Some(SelectFlavor::Standard),
374        }
375    }
376}
377impl Default for SelectBuilder {
378    fn default() -> Self {
379        Self::create_empty()
380    }
381}
382
383#[derive(Clone)]
384pub struct TableWithJoinsBuilder {
385    relation: Option<RelationBuilder>,
386    joins: Vec<ast::Join>,
387}
388
389impl TableWithJoinsBuilder {
390    pub fn relation(&mut self, value: RelationBuilder) -> &mut Self {
391        self.relation = Some(value);
392        self
393    }
394
395    pub fn joins(&mut self, value: Vec<ast::Join>) -> &mut Self {
396        self.joins = value;
397        self
398    }
399    pub fn push_join(&mut self, value: ast::Join) -> &mut Self {
400        self.joins.push(value);
401        self
402    }
403
404    pub fn build(&self) -> Result<Option<ast::TableWithJoins>, BuilderError> {
405        match self.relation {
406            Some(ref value) => match value.build()? {
407                Some(relation) => Ok(Some(ast::TableWithJoins {
408                    relation,
409                    joins: self.joins.clone(),
410                })),
411                None => Ok(None),
412            },
413            None => Err(Into::into(UninitializedFieldError::from("relation"))),
414        }
415    }
416    fn create_empty() -> Self {
417        Self {
418            relation: Default::default(),
419            joins: Default::default(),
420        }
421    }
422}
423impl Default for TableWithJoinsBuilder {
424    fn default() -> Self {
425        Self::create_empty()
426    }
427}
428
429#[derive(Clone)]
430pub struct RelationBuilder {
431    relation: Option<TableFactorBuilder>,
432}
433
434#[derive(Clone)]
435#[expect(clippy::large_enum_variant)]
436enum TableFactorBuilder {
437    Table(TableRelationBuilder),
438    Derived(DerivedRelationBuilder),
439    Unnest(UnnestRelationBuilder),
440    Empty,
441}
442
443impl RelationBuilder {
444    pub fn has_relation(&self) -> bool {
445        self.relation.is_some()
446    }
447    pub fn table(&mut self, value: TableRelationBuilder) -> &mut Self {
448        self.relation = Some(TableFactorBuilder::Table(value));
449        self
450    }
451    pub fn derived(&mut self, value: DerivedRelationBuilder) -> &mut Self {
452        self.relation = Some(TableFactorBuilder::Derived(value));
453        self
454    }
455
456    pub fn unnest(&mut self, value: UnnestRelationBuilder) -> &mut Self {
457        self.relation = Some(TableFactorBuilder::Unnest(value));
458        self
459    }
460
461    pub fn empty(&mut self) -> &mut Self {
462        self.relation = Some(TableFactorBuilder::Empty);
463        self
464    }
465    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
466        let new = self;
467        match new.relation {
468            Some(TableFactorBuilder::Table(ref mut rel_builder)) => {
469                rel_builder.alias = value;
470            }
471            Some(TableFactorBuilder::Derived(ref mut rel_builder)) => {
472                rel_builder.alias = value;
473            }
474            Some(TableFactorBuilder::Unnest(ref mut rel_builder)) => {
475                rel_builder.alias = value;
476            }
477            Some(TableFactorBuilder::Empty) => (),
478            None => (),
479        }
480        new
481    }
482    pub fn build(&self) -> Result<Option<ast::TableFactor>, BuilderError> {
483        Ok(match self.relation {
484            Some(TableFactorBuilder::Table(ref value)) => Some(value.build()?),
485            Some(TableFactorBuilder::Derived(ref value)) => Some(value.build()?),
486            Some(TableFactorBuilder::Unnest(ref value)) => Some(value.build()?),
487            Some(TableFactorBuilder::Empty) => None,
488            None => return Err(Into::into(UninitializedFieldError::from("relation"))),
489        })
490    }
491    fn create_empty() -> Self {
492        Self {
493            relation: Default::default(),
494        }
495    }
496}
497impl Default for RelationBuilder {
498    fn default() -> Self {
499        Self::create_empty()
500    }
501}
502
503#[derive(Clone)]
504pub struct TableRelationBuilder {
505    name: Option<ast::ObjectName>,
506    alias: Option<ast::TableAlias>,
507    args: Option<Vec<ast::FunctionArg>>,
508    with_hints: Vec<ast::Expr>,
509    version: Option<ast::TableVersion>,
510    partitions: Vec<ast::Ident>,
511    index_hints: Vec<ast::TableIndexHints>,
512}
513
514impl TableRelationBuilder {
515    pub fn name(&mut self, value: ast::ObjectName) -> &mut Self {
516        self.name = Some(value);
517        self
518    }
519    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
520        self.alias = value;
521        self
522    }
523    pub fn args(&mut self, value: Option<Vec<ast::FunctionArg>>) -> &mut Self {
524        self.args = value;
525        self
526    }
527    pub fn with_hints(&mut self, value: Vec<ast::Expr>) -> &mut Self {
528        self.with_hints = value;
529        self
530    }
531    pub fn version(&mut self, value: Option<ast::TableVersion>) -> &mut Self {
532        self.version = value;
533        self
534    }
535    pub fn partitions(&mut self, value: Vec<ast::Ident>) -> &mut Self {
536        self.partitions = value;
537        self
538    }
539    pub fn index_hints(&mut self, value: Vec<ast::TableIndexHints>) -> &mut Self {
540        self.index_hints = value;
541        self
542    }
543    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
544        Ok(ast::TableFactor::Table {
545            name: match self.name {
546                Some(ref value) => value.clone(),
547                None => return Err(Into::into(UninitializedFieldError::from("name"))),
548            },
549            alias: self.alias.clone(),
550            args: self.args.clone().map(|args| ast::TableFunctionArgs {
551                args,
552                settings: None,
553            }),
554            with_hints: self.with_hints.clone(),
555            version: self.version.clone(),
556            partitions: self.partitions.clone(),
557            with_ordinality: false,
558            json_path: None,
559            sample: None,
560            index_hints: self.index_hints.clone(),
561        })
562    }
563    fn create_empty() -> Self {
564        Self {
565            name: Default::default(),
566            alias: Default::default(),
567            args: Default::default(),
568            with_hints: Default::default(),
569            version: Default::default(),
570            partitions: Default::default(),
571            index_hints: Default::default(),
572        }
573    }
574}
575impl Default for TableRelationBuilder {
576    fn default() -> Self {
577        Self::create_empty()
578    }
579}
580#[derive(Clone)]
581pub struct DerivedRelationBuilder {
582    lateral: Option<bool>,
583    subquery: Option<Box<ast::Query>>,
584    alias: Option<ast::TableAlias>,
585}
586
587impl DerivedRelationBuilder {
588    pub fn lateral(&mut self, value: bool) -> &mut Self {
589        self.lateral = Some(value);
590        self
591    }
592    pub fn subquery(&mut self, value: Box<ast::Query>) -> &mut Self {
593        self.subquery = Some(value);
594        self
595    }
596    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
597        self.alias = value;
598        self
599    }
600    fn build(&self) -> Result<ast::TableFactor, BuilderError> {
601        Ok(ast::TableFactor::Derived {
602            lateral: match self.lateral {
603                Some(ref value) => *value,
604                None => return Err(Into::into(UninitializedFieldError::from("lateral"))),
605            },
606            subquery: match self.subquery {
607                Some(ref value) => value.clone(),
608                None => {
609                    return Err(Into::into(UninitializedFieldError::from("subquery")));
610                }
611            },
612            alias: self.alias.clone(),
613            sample: None,
614        })
615    }
616    fn create_empty() -> Self {
617        Self {
618            lateral: Default::default(),
619            subquery: Default::default(),
620            alias: Default::default(),
621        }
622    }
623}
624impl Default for DerivedRelationBuilder {
625    fn default() -> Self {
626        Self::create_empty()
627    }
628}
629
630#[derive(Clone)]
631pub struct UnnestRelationBuilder {
632    pub alias: Option<ast::TableAlias>,
633    pub array_exprs: Vec<ast::Expr>,
634    with_offset: bool,
635    with_offset_alias: Option<ast::Ident>,
636    with_ordinality: bool,
637}
638
639impl UnnestRelationBuilder {
640    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
641        self.alias = value;
642        self
643    }
644    pub fn array_exprs(&mut self, value: Vec<ast::Expr>) -> &mut Self {
645        self.array_exprs = value;
646        self
647    }
648
649    pub fn with_offset(&mut self, value: bool) -> &mut Self {
650        self.with_offset = value;
651        self
652    }
653
654    pub fn with_offset_alias(&mut self, value: Option<ast::Ident>) -> &mut Self {
655        self.with_offset_alias = value;
656        self
657    }
658
659    pub fn with_ordinality(&mut self, value: bool) -> &mut Self {
660        self.with_ordinality = value;
661        self
662    }
663
664    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
665        Ok(ast::TableFactor::UNNEST {
666            alias: self.alias.clone(),
667            array_exprs: self.array_exprs.clone(),
668            with_offset: self.with_offset,
669            with_offset_alias: self.with_offset_alias.clone(),
670            with_ordinality: self.with_ordinality,
671        })
672    }
673
674    fn create_empty() -> Self {
675        Self {
676            alias: Default::default(),
677            array_exprs: Default::default(),
678            with_offset: Default::default(),
679            with_offset_alias: Default::default(),
680            with_ordinality: Default::default(),
681        }
682    }
683}
684
685impl Default for UnnestRelationBuilder {
686    fn default() -> Self {
687        Self::create_empty()
688    }
689}
690
691/// Runtime error when a `build()` method is called and one or more required fields
692/// do not have a value.
693#[derive(Debug, Clone)]
694pub struct UninitializedFieldError(&'static str);
695
696impl UninitializedFieldError {
697    /// Create a new `UninitializedFieldError` for the specified field name.
698    pub fn new(field_name: &'static str) -> Self {
699        UninitializedFieldError(field_name)
700    }
701
702    /// Get the name of the first-declared field that wasn't initialized
703    pub fn field_name(&self) -> &'static str {
704        self.0
705    }
706}
707
708impl fmt::Display for UninitializedFieldError {
709    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
710        write!(f, "Field not initialized: {}", self.0)
711    }
712}
713
714impl From<&'static str> for UninitializedFieldError {
715    fn from(field_name: &'static str) -> Self {
716        Self::new(field_name)
717    }
718}
719impl std::error::Error for UninitializedFieldError {}
720
721#[derive(Debug)]
722pub enum BuilderError {
723    UninitializedField(&'static str),
724    ValidationError(String),
725}
726impl From<UninitializedFieldError> for BuilderError {
727    fn from(s: UninitializedFieldError) -> Self {
728        Self::UninitializedField(s.field_name())
729    }
730}
731impl From<String> for BuilderError {
732    fn from(s: String) -> Self {
733        Self::ValidationError(s)
734    }
735}
736impl fmt::Display for BuilderError {
737    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
738        match self {
739            Self::UninitializedField(field) => {
740                write!(f, "`{field}` must be initialized")
741            }
742            Self::ValidationError(error) => write!(f, "{error}"),
743        }
744    }
745}
746impl std::error::Error for BuilderError {}