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            distinct: self.distinct.clone(),
319            top_before_distinct: false,
320            top: self.top.clone(),
321            projection: self.projection.clone().unwrap_or_default(),
322            into: self.into.clone(),
323            from: self
324                .from
325                .iter()
326                .filter_map(|b| b.build().transpose())
327                .collect::<Result<Vec<_>, BuilderError>>()?,
328            lateral_views: self.lateral_views.clone(),
329            selection: self.selection.clone(),
330            group_by: match self.group_by {
331                Some(ref value) => value.clone(),
332                None => {
333                    return Err(Into::into(UninitializedFieldError::from("group_by")));
334                }
335            },
336            cluster_by: self.cluster_by.clone(),
337            distribute_by: self.distribute_by.clone(),
338            sort_by: self.sort_by.clone(),
339            having: self.having.clone(),
340            named_window: self.named_window.clone(),
341            qualify: self.qualify.clone(),
342            value_table_mode: self.value_table_mode,
343            connect_by: None,
344            window_before_qualify: false,
345            prewhere: None,
346            select_token: AttachedToken::empty(),
347            flavor: match self.flavor {
348                Some(ref value) => value.clone(),
349                None => return Err(Into::into(UninitializedFieldError::from("flavor"))),
350            },
351            exclude: None,
352        })
353    }
354    fn create_empty() -> Self {
355        Self {
356            distinct: Default::default(),
357            top: Default::default(),
358            projection: None,
359            into: Default::default(),
360            from: Default::default(),
361            lateral_views: Default::default(),
362            selection: Default::default(),
363            group_by: Some(ast::GroupByExpr::Expressions(Vec::new(), Vec::new())),
364            cluster_by: Default::default(),
365            distribute_by: Default::default(),
366            sort_by: Default::default(),
367            having: Default::default(),
368            named_window: Default::default(),
369            qualify: Default::default(),
370            value_table_mode: Default::default(),
371            flavor: Some(SelectFlavor::Standard),
372        }
373    }
374}
375impl Default for SelectBuilder {
376    fn default() -> Self {
377        Self::create_empty()
378    }
379}
380
381#[derive(Clone)]
382pub struct TableWithJoinsBuilder {
383    relation: Option<RelationBuilder>,
384    joins: Vec<ast::Join>,
385}
386
387impl TableWithJoinsBuilder {
388    pub fn relation(&mut self, value: RelationBuilder) -> &mut Self {
389        self.relation = Some(value);
390        self
391    }
392
393    pub fn joins(&mut self, value: Vec<ast::Join>) -> &mut Self {
394        self.joins = value;
395        self
396    }
397    pub fn push_join(&mut self, value: ast::Join) -> &mut Self {
398        self.joins.push(value);
399        self
400    }
401
402    pub fn build(&self) -> Result<Option<ast::TableWithJoins>, BuilderError> {
403        match self.relation {
404            Some(ref value) => match value.build()? {
405                Some(relation) => Ok(Some(ast::TableWithJoins {
406                    relation,
407                    joins: self.joins.clone(),
408                })),
409                None => Ok(None),
410            },
411            None => Err(Into::into(UninitializedFieldError::from("relation"))),
412        }
413    }
414    fn create_empty() -> Self {
415        Self {
416            relation: Default::default(),
417            joins: Default::default(),
418        }
419    }
420}
421impl Default for TableWithJoinsBuilder {
422    fn default() -> Self {
423        Self::create_empty()
424    }
425}
426
427#[derive(Clone)]
428pub struct RelationBuilder {
429    relation: Option<TableFactorBuilder>,
430}
431
432#[derive(Clone)]
433#[expect(clippy::large_enum_variant)]
434enum TableFactorBuilder {
435    Table(TableRelationBuilder),
436    Derived(DerivedRelationBuilder),
437    Unnest(UnnestRelationBuilder),
438    Empty,
439}
440
441impl RelationBuilder {
442    pub fn has_relation(&self) -> bool {
443        self.relation.is_some()
444    }
445    pub fn table(&mut self, value: TableRelationBuilder) -> &mut Self {
446        self.relation = Some(TableFactorBuilder::Table(value));
447        self
448    }
449    pub fn derived(&mut self, value: DerivedRelationBuilder) -> &mut Self {
450        self.relation = Some(TableFactorBuilder::Derived(value));
451        self
452    }
453
454    pub fn unnest(&mut self, value: UnnestRelationBuilder) -> &mut Self {
455        self.relation = Some(TableFactorBuilder::Unnest(value));
456        self
457    }
458
459    pub fn empty(&mut self) -> &mut Self {
460        self.relation = Some(TableFactorBuilder::Empty);
461        self
462    }
463    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
464        let new = self;
465        match new.relation {
466            Some(TableFactorBuilder::Table(ref mut rel_builder)) => {
467                rel_builder.alias = value;
468            }
469            Some(TableFactorBuilder::Derived(ref mut rel_builder)) => {
470                rel_builder.alias = value;
471            }
472            Some(TableFactorBuilder::Unnest(ref mut rel_builder)) => {
473                rel_builder.alias = value;
474            }
475            Some(TableFactorBuilder::Empty) => (),
476            None => (),
477        }
478        new
479    }
480    pub fn build(&self) -> Result<Option<ast::TableFactor>, BuilderError> {
481        Ok(match self.relation {
482            Some(TableFactorBuilder::Table(ref value)) => Some(value.build()?),
483            Some(TableFactorBuilder::Derived(ref value)) => Some(value.build()?),
484            Some(TableFactorBuilder::Unnest(ref value)) => Some(value.build()?),
485            Some(TableFactorBuilder::Empty) => None,
486            None => return Err(Into::into(UninitializedFieldError::from("relation"))),
487        })
488    }
489    fn create_empty() -> Self {
490        Self {
491            relation: Default::default(),
492        }
493    }
494}
495impl Default for RelationBuilder {
496    fn default() -> Self {
497        Self::create_empty()
498    }
499}
500
501#[derive(Clone)]
502pub struct TableRelationBuilder {
503    name: Option<ast::ObjectName>,
504    alias: Option<ast::TableAlias>,
505    args: Option<Vec<ast::FunctionArg>>,
506    with_hints: Vec<ast::Expr>,
507    version: Option<ast::TableVersion>,
508    partitions: Vec<ast::Ident>,
509    index_hints: Vec<ast::TableIndexHints>,
510}
511
512impl TableRelationBuilder {
513    pub fn name(&mut self, value: ast::ObjectName) -> &mut Self {
514        self.name = Some(value);
515        self
516    }
517    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
518        self.alias = value;
519        self
520    }
521    pub fn args(&mut self, value: Option<Vec<ast::FunctionArg>>) -> &mut Self {
522        self.args = value;
523        self
524    }
525    pub fn with_hints(&mut self, value: Vec<ast::Expr>) -> &mut Self {
526        self.with_hints = value;
527        self
528    }
529    pub fn version(&mut self, value: Option<ast::TableVersion>) -> &mut Self {
530        self.version = value;
531        self
532    }
533    pub fn partitions(&mut self, value: Vec<ast::Ident>) -> &mut Self {
534        self.partitions = value;
535        self
536    }
537    pub fn index_hints(&mut self, value: Vec<ast::TableIndexHints>) -> &mut Self {
538        self.index_hints = value;
539        self
540    }
541    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
542        Ok(ast::TableFactor::Table {
543            name: match self.name {
544                Some(ref value) => value.clone(),
545                None => return Err(Into::into(UninitializedFieldError::from("name"))),
546            },
547            alias: self.alias.clone(),
548            args: self.args.clone().map(|args| ast::TableFunctionArgs {
549                args,
550                settings: None,
551            }),
552            with_hints: self.with_hints.clone(),
553            version: self.version.clone(),
554            partitions: self.partitions.clone(),
555            with_ordinality: false,
556            json_path: None,
557            sample: None,
558            index_hints: self.index_hints.clone(),
559        })
560    }
561    fn create_empty() -> Self {
562        Self {
563            name: Default::default(),
564            alias: Default::default(),
565            args: Default::default(),
566            with_hints: Default::default(),
567            version: Default::default(),
568            partitions: Default::default(),
569            index_hints: Default::default(),
570        }
571    }
572}
573impl Default for TableRelationBuilder {
574    fn default() -> Self {
575        Self::create_empty()
576    }
577}
578#[derive(Clone)]
579pub struct DerivedRelationBuilder {
580    lateral: Option<bool>,
581    subquery: Option<Box<ast::Query>>,
582    alias: Option<ast::TableAlias>,
583}
584
585impl DerivedRelationBuilder {
586    pub fn lateral(&mut self, value: bool) -> &mut Self {
587        self.lateral = Some(value);
588        self
589    }
590    pub fn subquery(&mut self, value: Box<ast::Query>) -> &mut Self {
591        self.subquery = Some(value);
592        self
593    }
594    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
595        self.alias = value;
596        self
597    }
598    fn build(&self) -> Result<ast::TableFactor, BuilderError> {
599        Ok(ast::TableFactor::Derived {
600            lateral: match self.lateral {
601                Some(ref value) => *value,
602                None => return Err(Into::into(UninitializedFieldError::from("lateral"))),
603            },
604            subquery: match self.subquery {
605                Some(ref value) => value.clone(),
606                None => {
607                    return Err(Into::into(UninitializedFieldError::from("subquery")));
608                }
609            },
610            alias: self.alias.clone(),
611        })
612    }
613    fn create_empty() -> Self {
614        Self {
615            lateral: Default::default(),
616            subquery: Default::default(),
617            alias: Default::default(),
618        }
619    }
620}
621impl Default for DerivedRelationBuilder {
622    fn default() -> Self {
623        Self::create_empty()
624    }
625}
626
627#[derive(Clone)]
628pub struct UnnestRelationBuilder {
629    pub alias: Option<ast::TableAlias>,
630    pub array_exprs: Vec<ast::Expr>,
631    with_offset: bool,
632    with_offset_alias: Option<ast::Ident>,
633    with_ordinality: bool,
634}
635
636impl UnnestRelationBuilder {
637    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
638        self.alias = value;
639        self
640    }
641    pub fn array_exprs(&mut self, value: Vec<ast::Expr>) -> &mut Self {
642        self.array_exprs = value;
643        self
644    }
645
646    pub fn with_offset(&mut self, value: bool) -> &mut Self {
647        self.with_offset = value;
648        self
649    }
650
651    pub fn with_offset_alias(&mut self, value: Option<ast::Ident>) -> &mut Self {
652        self.with_offset_alias = value;
653        self
654    }
655
656    pub fn with_ordinality(&mut self, value: bool) -> &mut Self {
657        self.with_ordinality = value;
658        self
659    }
660
661    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
662        Ok(ast::TableFactor::UNNEST {
663            alias: self.alias.clone(),
664            array_exprs: self.array_exprs.clone(),
665            with_offset: self.with_offset,
666            with_offset_alias: self.with_offset_alias.clone(),
667            with_ordinality: self.with_ordinality,
668        })
669    }
670
671    fn create_empty() -> Self {
672        Self {
673            alias: Default::default(),
674            array_exprs: Default::default(),
675            with_offset: Default::default(),
676            with_offset_alias: Default::default(),
677            with_ordinality: Default::default(),
678        }
679    }
680}
681
682impl Default for UnnestRelationBuilder {
683    fn default() -> Self {
684        Self::create_empty()
685    }
686}
687
688/// Runtime error when a `build()` method is called and one or more required fields
689/// do not have a value.
690#[derive(Debug, Clone)]
691pub struct UninitializedFieldError(&'static str);
692
693impl UninitializedFieldError {
694    /// Create a new `UninitializedFieldError` for the specified field name.
695    pub fn new(field_name: &'static str) -> Self {
696        UninitializedFieldError(field_name)
697    }
698
699    /// Get the name of the first-declared field that wasn't initialized
700    pub fn field_name(&self) -> &'static str {
701        self.0
702    }
703}
704
705impl fmt::Display for UninitializedFieldError {
706    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
707        write!(f, "Field not initialized: {}", self.0)
708    }
709}
710
711impl From<&'static str> for UninitializedFieldError {
712    fn from(field_name: &'static str) -> Self {
713        Self::new(field_name)
714    }
715}
716impl std::error::Error for UninitializedFieldError {}
717
718#[derive(Debug)]
719pub enum BuilderError {
720    UninitializedField(&'static str),
721    ValidationError(String),
722}
723impl From<UninitializedFieldError> for BuilderError {
724    fn from(s: UninitializedFieldError) -> Self {
725        Self::UninitializedField(s.field_name())
726    }
727}
728impl From<String> for BuilderError {
729    fn from(s: String) -> Self {
730        Self::ValidationError(s)
731    }
732}
733impl fmt::Display for BuilderError {
734    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
735        match self {
736            Self::UninitializedField(field) => {
737                write!(f, "`{field}` must be initialized")
738            }
739            Self::ValidationError(error) => write!(f, "{error}"),
740        }
741    }
742}
743impl std::error::Error for BuilderError {}