Skip to main content

nickel_lang_parser/ast/
builder.rs

1//! A builder interface for producing Nickel values.
2//!
3//! Using this module, you can define Nickel values using a builder style. For example:
4//!
5//! ```rust
6//! # use nickel_lang_parser::ast::{Ast, MergePriority, Node, builder::Record, AstAlloc};
7//!
8//! let alloc = AstAlloc::new();
9//!
10//! let record = Record::new()
11//!     .field("foo")
12//!     .priority(MergePriority::Bottom)
13//!     .doc("foo?")
14//!     .not_exported(true)
15//!     .value(&alloc, alloc.string("foo"));
16//!
17//! let record = record
18//!     .field("bar")
19//!     .value(&alloc, alloc.number(42.into()))
20//!     .build(&alloc);
21//!
22//! // Gives {foo | doc "foo?" | not_exported | default = "foo", bar = 42}
23//! ```
24//!
25//! This modules also offers a simpler interface for basic values where all arguments can be
26//! provided at once, to avoid useless intermediate allocations.
27use crate::identifier::Ident;
28
29use super::{
30    MergePriority,
31    record::{FieldMetadata, FieldPathElem, Include},
32    typ::Type,
33    *,
34};
35
36type StaticPath = Vec<Ident>;
37
38/// Typestate style tag for `Field`s that are not yet completely specified
39pub struct Incomplete();
40
41/// Typestate style tag for `Field`s that have been finalized
42pub struct Complete<'ast>(Option<Ast<'ast>>);
43
44/// A Nickel record field being constructed
45#[derive(Debug)]
46pub struct Field<'ast, State> {
47    /// This field stores different piece of information depending on the state of this field:
48    ///
49    /// - It's empty for an incomplete field (`()`).
50    /// - When a value (or `None`) has been set, `State` becomes [Complete] and stores the
51    ///   optional value of this field.
52    /// - Internally, [Self] is also used temporarily with `State` set to `Record<'ast>>` inside
53    ///   [Self::attach], before updating a record with a completed field (and thus consuming said
54    ///   field). During this transitory phase, `state` stores the parent record.
55    state: State,
56    path: StaticPath,
57    metadata: FieldMetadata<'ast>,
58    /// We need to store the documentation separately, because the metadata only accepts a string
59    /// that has been allocated in the AST allocator. We could require to thread the allocator for
60    /// calls to [Self::doc] or [Self::some_doc], but it's more ergonomic to keep the phases of
61    /// building a field and finalizing it separate. We thus store the documentation here
62    /// temporarily.
63    doc: Option<String>,
64    /// As we might build contract element by element, we can't rely on
65    /// `metadata.annotation.contracts`, which is a fixed array.
66    ///
67    /// We accumulate them in this field instead and keep `metadata.annotation.contracts` empty
68    /// until finalization, where we finally allocate it.
69    contracts: Vec<Type<'ast>>,
70}
71
72impl<'ast, A> Field<'ast, A> {
73    /// Attach documentation metadata to the field
74    pub fn doc(self, doc: impl AsRef<str>) -> Self {
75        self.some_doc(Some(doc))
76    }
77
78    /// Attach documentation metadata to the field, optionally
79    pub fn some_doc(mut self, some_doc: Option<impl AsRef<str>>) -> Self {
80        self.doc = some_doc.map(|s| s.as_ref().to_string());
81        self
82    }
83
84    /// Mark the field as optional or not, depending on `opt`
85    pub fn optional(mut self, opt: bool) -> Self {
86        self.metadata.opt = opt;
87        self
88    }
89
90    /// Mark the field as `not_exported` or not, depending on the argument
91    pub fn not_exported(mut self, not_exported: bool) -> Self {
92        self.metadata.not_exported = not_exported;
93        self
94    }
95
96    /// Attach a contract to the field
97    pub fn contract(mut self, contract: impl Into<Type<'ast>>) -> Self {
98        self.contracts.push(contract.into());
99        self
100    }
101
102    /// Attach multiple contracts to the field
103    pub fn contracts<I>(mut self, contracts: I) -> Self
104    where
105        I: IntoIterator<Item = Type<'ast>>,
106    {
107        self.contracts.extend(contracts);
108        self
109    }
110
111    /// Attach a type annotation to the field
112    pub fn types(mut self, typ: impl Into<Type<'ast>>) -> Self {
113        self.metadata.annotation.typ = Some(typ.into());
114        self
115    }
116
117    /// Set the field's merge priority
118    pub fn priority(mut self, priority: MergePriority) -> Self {
119        self.metadata.priority = priority;
120        self
121    }
122
123    /// Set the field's metadata all at once
124    pub fn metadata(mut self, metadata: FieldMetadata<'ast>) -> Self {
125        self.metadata = metadata;
126        self
127    }
128}
129
130impl<'ast> Field<'ast, Incomplete> {
131    /// Construct an incomplete [`Field`] at a given path
132    pub fn path<I, It>(path: It) -> Self
133    where
134        I: AsRef<str>,
135        It: IntoIterator<Item = I>,
136    {
137        Field {
138            state: Incomplete(),
139            path: path.into_iter().map(|e| e.as_ref().into()).collect(),
140            metadata: Default::default(),
141            doc: None,
142            contracts: Vec::new(),
143        }
144    }
145
146    /// Construct an incomplete [`Field`] with a given name
147    pub fn name(name: impl AsRef<str>) -> Self {
148        Self::path([name])
149    }
150
151    /// Finalize the [`Field`] without setting a value
152    pub fn no_value(self) -> Field<'ast, Complete<'ast>> {
153        Field {
154            state: Complete(None),
155            path: self.path,
156            metadata: self.metadata,
157            doc: self.doc,
158            contracts: self.contracts,
159        }
160    }
161
162    /// Finalize the [`Field`] by setting its value
163    pub fn value(self, value: impl Into<Ast<'ast>>) -> Field<'ast, Complete<'ast>> {
164        Field {
165            state: Complete(Some(value.into())),
166            path: self.path,
167            metadata: self.metadata,
168            doc: self.doc,
169            contracts: self.contracts,
170        }
171    }
172}
173
174impl<'ast> Field<'ast, Complete<'ast>> {
175    /// Attach a finalized [`Field`] to a [`Record`]
176    pub fn attach(self, alloc: &'ast AstAlloc, record: Record<'ast>) -> Record<'ast> {
177        let value = self.state;
178
179        let field = Field {
180            state: record,
181            path: self.path,
182            metadata: self.metadata,
183            doc: self.doc,
184            contracts: self.contracts,
185        };
186        match value {
187            Complete(Some(v)) => field.value(alloc, v),
188            Complete(None) => field.no_value(alloc),
189        }
190    }
191}
192
193impl<'ast> Field<'ast, Record<'ast>> {
194    /// Finalize the [`Field`] without setting a value
195    pub fn no_value(mut self, alloc: &'ast AstAlloc) -> Record<'ast> {
196        self.finalize_contracts(alloc);
197        self.metadata.doc = self.doc.map(|s| alloc.alloc_str(&s));
198
199        self.state.field_defs.push(record::FieldDef {
200            path: alloc.alloc_many(
201                self.path
202                    .into_iter()
203                    .map(|id| FieldPathElem::Ident(id.into())),
204            ),
205            metadata: self.metadata,
206            value: None,
207            pos: TermPos::None,
208        });
209        self.state
210    }
211
212    /// Finalize the [`Field`] by setting its a value
213    pub fn value(mut self, alloc: &'ast AstAlloc, value: impl Into<Ast<'ast>>) -> Record<'ast> {
214        self.finalize_contracts(alloc);
215        self.metadata.doc = self.doc.map(|s| alloc.alloc_str(&s));
216
217        self.state.field_defs.push(record::FieldDef {
218            path: alloc.alloc_many(
219                self.path
220                    .into_iter()
221                    .map(|id| FieldPathElem::Ident(id.into())),
222            ),
223            metadata: self.metadata,
224            value: Some(value.into()),
225            pos: TermPos::None,
226        });
227
228        self.state
229    }
230
231    /// Finalize contracts by consuming the vector `self.contracts`, allocating a fixed array and
232    /// filling the field `self.metadata.annotation.contracts` with it. After this call,
233    /// `self.contracts` is empty and shouldn't be used anymore (it will have no effect).
234    fn finalize_contracts(&mut self, alloc: &'ast AstAlloc) {
235        self.metadata.annotation.contracts = alloc.alloc_many(self.contracts.drain(..));
236    }
237}
238
239/// A Nickel record being constructed
240#[derive(Debug, Default)]
241pub struct Record<'ast> {
242    field_defs: Vec<record::FieldDef<'ast>>,
243    includes: Vec<Include<'ast>>,
244    open: bool,
245}
246
247impl<'ast> Record<'ast> {
248    /// Make a new, empty record builder
249    pub fn new() -> Self {
250        Record::default()
251    }
252
253    /// Start constructing a field with the given name
254    pub fn field(self, name: impl AsRef<str>) -> Field<'ast, Record<'ast>> {
255        Field {
256            state: self,
257            path: vec![Ident::new(name)],
258            metadata: Default::default(),
259            doc: None,
260            contracts: Vec::new(),
261        }
262    }
263
264    /// Attach possibly multiple fields to this record
265    pub fn fields<I, It>(mut self, alloc: &'ast AstAlloc, fields: It) -> Self
266    where
267        I: Into<Field<'ast, Complete<'ast>>>,
268        It: IntoIterator<Item = I>,
269    {
270        for f in fields {
271            self = f.into().attach(alloc, self)
272        }
273        self
274    }
275
276    /// Adds an `include` expression (define a field by taking it from the outer environment).
277    pub fn include(self, ident: LocIdent) -> Self {
278        self.include_with_metadata(ident, Default::default())
279    }
280
281    /// Adds an `include` expression with associated metadata.
282    pub fn include_with_metadata(mut self, ident: LocIdent, metadata: FieldMetadata<'ast>) -> Self {
283        self.includes.push(Include { ident, metadata });
284        self
285    }
286
287    /// Start constructing a field at the given path
288    pub fn path<It, I>(self, path: It) -> Field<'ast, Record<'ast>>
289    where
290        I: AsRef<str>,
291        It: IntoIterator<Item = I>,
292    {
293        Field {
294            state: self,
295            path: path.into_iter().map(|e| Ident::new(e)).collect(),
296            metadata: Default::default(),
297            doc: None,
298            contracts: Vec::new(),
299        }
300    }
301
302    /// Mark this record as "open" when used as a contract
303    pub fn open(mut self) -> Self {
304        self.open = true;
305        self
306    }
307
308    /// Mark this record as "open" when used as a contract, depending on the value of `open`
309    pub fn set_open(mut self, open: bool) -> Self {
310        self.open = open;
311        self
312    }
313
314    /// Finalize the record and turn it into a [`super::Ast`]
315    pub fn build(self, alloc: &'ast AstAlloc) -> Ast<'ast> {
316        alloc
317            .record(record::Record {
318                field_defs: alloc.alloc_many(self.field_defs),
319                includes: alloc.alloc_many(self.includes),
320                open: self.open,
321            })
322            .into()
323    }
324
325    /// Creates a record from an iterator of finalized fields.
326    ///
327    /// We can't implement `FromIterator` for `Record` because we need to provide an additional
328    /// allocator.
329    pub fn from_iterator<I, It>(alloc: &'ast AstAlloc, fields: It) -> Self
330    where
331        I: Into<Field<'ast, Complete<'ast>>>,
332        It: IntoIterator<Item = I>,
333    {
334        Record::new().fields(alloc, fields)
335    }
336}
337
338/// Multi-ary application for types implementing `Into<Ast>`.
339#[macro_export]
340macro_rules! app {
341    // We avoid a vec allocation for unary applications, which are relatively common.
342    ( $alloc:expr, $f:expr , $arg:expr $(,)?) => {
343            $crate::ast::Ast::from(
344                $alloc.app(
345                    $crate::ast::Ast::from($f),
346                    std::iter::once($crate::ast::Ast::from($arg))
347                )
348            )
349    };
350    ( $alloc:expr, $f:expr, $arg1:expr $(, $args:expr )+ $(,)?) => {
351        {
352            let args = vec![
353                $crate::ast::Ast::from($arg1)
354                $(, $crate::ast::Ast::from($args) )+
355            ];
356
357            $crate::ast::Ast::from($alloc.app($crate::ast::Ast::from($f), args))
358        }
359    };
360}
361
362#[macro_export]
363/// Multi-ary application for types implementing `Into<NickelValue>`.
364macro_rules! primop_app {
365    // We avoid a vec allocation for unary primop applications, which are relatively common.
366    ( $alloc:expr, $op:expr , $arg:expr $(,)?) => {
367            $crate::ast::Ast::from(
368                $alloc.prim_op(
369                    $op,
370                    std::iter::once($crate::ast::Ast::from($arg))
371                )
372            )
373    };
374    ( $alloc:expr, $op:expr, $arg1:expr $(, $args:expr )+ $(,)?) => {
375        {
376            let args = vec![
377                $crate::ast::Ast::from($arg1)
378                $(, $crate::ast::Ast::from($args) )+
379            ];
380            $crate::ast::Ast::from($alloc.prim_op($op, args))
381        }
382    };
383}
384
385#[macro_export]
386/// Multi argument function for types implementing `Into<Ident>` (for the identifiers), and
387/// `Into<NickelValue>` for the body.
388macro_rules! fun {
389    // Auxiliary form for `fun!` where the arguments are clearly delimited syntactically by
390    // brackets. The issue with the general interface of `fun!` is that it must extract its last
391    // argument, the body of the function, with an arbitrary number of macro arguments before it.
392    // This isn't easy to do because the macro parser can't backtrack. The bracketed form uses
393    // recursion and a separate syntax for arguments to make it work.
394    //
395    // For example, calling `fun!(alloc, args=[], arg1, arg2, body)` will expand to `fun!(alloc,
396    // args=[arg1, arg2], body)`, which is the base case that can be turned into a function. The
397    // bracket part is used to accumulate the arguments before the body.
398    ($alloc:expr, args=[ $( $args:expr, )+ ], $body:expr) => {
399        {
400            let args = vec![
401                $($crate::ast::pattern::Pattern::any($crate::identifier::LocIdent::from($args)), )+
402            ];
403
404            $crate::ast::Ast::from(
405                $alloc.fun(args, $crate::ast::Ast::from($body))
406            )
407        }
408    };
409    // Recursive case for the bracketed form.
410    ($alloc:expr, args=[ $( $args:expr, )* ], $next_arg:expr $(, $rest:expr )+) => {
411        fun!($alloc, args=[ $( $args, )* $next_arg, ] $(, $rest )+)
412    };
413    // We avoid a vec allocation for unary functions, which are relatively common.
414    ( $alloc:expr, $arg:expr, $body:expr $(,)?) => {
415        $crate::ast::Ast::from(
416            $alloc.fun(
417                std::iter::once($crate::ast::pattern::Pattern::any($crate::identifier::LocIdent::from($arg))),
418                $crate::ast::Ast::from($body)
419            )
420        )
421    };
422    ( $alloc:expr, $arg1:expr, $arg2:expr $(, $rest:expr )+ $(,)?) => {
423        fun!($alloc, args=[ $arg1, $arg2, ] $(, $rest )+)
424    };
425}
426
427pub fn var<'ast>(id: impl Into<LocIdent>) -> Ast<'ast> {
428    Ast::from(Node::Var(id.into()))
429}
430
431pub fn enum_tag<'ast>(tag: impl Into<LocIdent>) -> Ast<'ast> {
432    Ast::from(Node::EnumVariant {
433        tag: tag.into(),
434        arg: None,
435    })
436}
437
438#[cfg(test)]
439mod tests {
440    use super::*;
441    use pretty_assertions::assert_eq;
442    use std::iter;
443
444    fn build_simple_record<'ast, I, Id>(alloc: &'ast AstAlloc, fields: I, open: bool) -> Ast<'ast>
445    where
446        Id: Into<LocIdent>,
447        I: IntoIterator<Item = (Id, Node<'ast>)>,
448        I::IntoIter: ExactSizeIterator,
449    {
450        build_record(
451            alloc,
452            fields
453                .into_iter()
454                .map(|(id, node)| (id, Default::default(), Some(node))),
455            open,
456        )
457    }
458
459    fn build_record<'ast, I, Id>(alloc: &'ast AstAlloc, fields: I, open: bool) -> Ast<'ast>
460    where
461        Id: Into<LocIdent>,
462        I: IntoIterator<Item = (Id, FieldMetadata<'ast>, Option<Node<'ast>>)>,
463        I::IntoIter: ExactSizeIterator,
464    {
465        alloc
466            .record(record::Record {
467                field_defs: alloc.alloc_many(fields.into_iter().map(|(id, metadata, node)| {
468                    record::FieldDef {
469                        path: FieldPathElem::single_ident_path(alloc, id.into()),
470                        value: node.map(Ast::from),
471                        metadata,
472                        pos: TermPos::None,
473                    }
474                })),
475                includes: &[],
476                open,
477            })
478            .into()
479    }
480
481    #[test]
482    fn trivial() {
483        let alloc = AstAlloc::new();
484
485        let ast: Ast = Record::new()
486            .field("foo")
487            .value(&alloc, alloc.string("bar"))
488            .build(&alloc);
489
490        assert_eq!(
491            ast,
492            build_simple_record(&alloc, vec![("foo", alloc.string("bar"))], false)
493        );
494    }
495
496    #[test]
497    fn from_iter() {
498        let alloc = AstAlloc::new();
499
500        let ast: Ast = Record::from_iterator(
501            &alloc,
502            [
503                Field::name("foo").value(Node::Null),
504                Field::name("bar").value(Node::Null),
505            ],
506        )
507        .build(&alloc);
508
509        assert_eq!(
510            ast,
511            build_simple_record(
512                &alloc,
513                vec![("foo", Node::Null), ("bar", Node::Null),],
514                false
515            )
516        );
517    }
518
519    #[test]
520    fn some_doc() {
521        let alloc = AstAlloc::new();
522
523        let ast: Ast = Record::from_iterator(
524            &alloc,
525            [
526                Field::name("foo").some_doc(Some("foo")).no_value(),
527                Field::name("bar").some_doc(None as Option<&str>).no_value(),
528                Field::name("baz").doc("baz").no_value(),
529            ],
530        )
531        .build(&alloc);
532
533        assert_eq!(
534            ast,
535            build_record(
536                &alloc,
537                vec![
538                    (
539                        "foo",
540                        FieldMetadata {
541                            doc: Some(alloc.alloc_str("foo")),
542                            ..Default::default()
543                        },
544                        None
545                    ),
546                    ("bar", Default::default(), None),
547                    (
548                        "baz",
549                        FieldMetadata {
550                            doc: Some(alloc.alloc_str("baz")),
551                            ..Default::default()
552                        },
553                        None,
554                    )
555                ],
556                false,
557            )
558        );
559    }
560
561    #[test]
562    fn fields() {
563        let alloc = AstAlloc::new();
564
565        let ast: Ast = Record::new()
566            .fields(
567                &alloc,
568                [
569                    Field::name("foo").value(alloc.string("foo")),
570                    Field::name("bar").value(alloc.string("bar")),
571                ],
572            )
573            .build(&alloc);
574
575        assert_eq!(
576            ast,
577            build_simple_record(
578                &alloc,
579                vec![("foo", alloc.string("foo")), ("bar", alloc.string("bar")),],
580                false,
581            )
582        );
583    }
584
585    #[test]
586    fn fields_metadata() {
587        let alloc = AstAlloc::new();
588
589        let ast: Ast = Record::new()
590            .fields(
591                &alloc,
592                [
593                    Field::name("foo").optional(true).no_value(),
594                    Field::name("bar").optional(true).no_value(),
595                ],
596            )
597            .build(&alloc);
598
599        assert_eq!(
600            ast,
601            build_record(
602                &alloc,
603                vec![
604                    (
605                        "foo",
606                        FieldMetadata {
607                            opt: true,
608                            ..Default::default()
609                        },
610                        None,
611                    ),
612                    (
613                        "bar",
614                        FieldMetadata {
615                            opt: true,
616                            ..Default::default()
617                        },
618                        None,
619                    ),
620                ],
621                false,
622            )
623        );
624    }
625
626    #[test]
627    fn overriding() {
628        let alloc = AstAlloc::new();
629
630        let ast: Ast = Record::new()
631            .path(vec!["terraform", "required_providers"])
632            .value(
633                &alloc,
634                Record::from_iterator(
635                    &alloc,
636                    [
637                        Field::name("foo").value(Node::Null),
638                        Field::name("bar").value(Node::Null),
639                    ],
640                )
641                .build(&alloc),
642            )
643            .path(vec!["terraform", "required_providers", "foo"])
644            .value(&alloc, alloc.string("hello world!"))
645            .build(&alloc);
646
647        eprintln!("{ast:?}");
648
649        assert_eq!(
650            ast,
651            alloc
652                .record(record::Record {
653                    field_defs: alloc.alloc_many(vec![
654                        record::FieldDef {
655                            path: alloc.alloc_many(vec![
656                                FieldPathElem::Ident("terraform".into()),
657                                FieldPathElem::Ident("required_providers".into())
658                            ]),
659                            metadata: Default::default(),
660                            value: Some(
661                                alloc
662                                    .record(record::Record {
663                                        field_defs: alloc.alloc_many(vec![
664                                            record::FieldDef {
665                                                path: FieldPathElem::single_ident_path(
666                                                    &alloc,
667                                                    "foo".into()
668                                                ),
669                                                metadata: Default::default(),
670                                                value: Some(Node::Null.into()),
671                                                pos: TermPos::None,
672                                            },
673                                            record::FieldDef {
674                                                path: FieldPathElem::single_ident_path(
675                                                    &alloc,
676                                                    "bar".into()
677                                                ),
678                                                metadata: Default::default(),
679                                                value: Some(Node::Null.into()),
680                                                pos: TermPos::None,
681                                            },
682                                        ]),
683                                        ..Default::default()
684                                    })
685                                    .into()
686                            ),
687                            pos: TermPos::None,
688                        },
689                        record::FieldDef {
690                            path: alloc.alloc_many(vec![
691                                FieldPathElem::Ident("terraform".into()),
692                                FieldPathElem::Ident("required_providers".into()),
693                                FieldPathElem::Ident("foo".into())
694                            ]),
695                            metadata: Default::default(),
696                            value: Some(alloc.string("hello world!").into()),
697                            pos: TermPos::None,
698                        }
699                    ]),
700                    ..Default::default()
701                })
702                .into()
703        );
704    }
705
706    #[test]
707    fn open_record() {
708        let alloc = AstAlloc::new();
709
710        let ast: Ast = Record::new().open().build(&alloc);
711
712        assert_eq!(ast, alloc.record(record::Record::empty().open()).into());
713    }
714
715    #[test]
716    fn prio_metadata() {
717        let alloc = AstAlloc::new();
718
719        let ast: Ast = Record::new()
720            .field("foo")
721            .priority(MergePriority::Top)
722            .no_value(&alloc)
723            .build(&alloc);
724
725        assert_eq!(
726            ast,
727            build_record(
728                &alloc,
729                iter::once((
730                    "foo",
731                    FieldMetadata {
732                        priority: MergePriority::Top,
733                        ..Default::default()
734                    },
735                    None,
736                )),
737                false
738            )
739        );
740    }
741
742    #[test]
743    fn contract() {
744        let alloc = AstAlloc::new();
745
746        let ast: Ast = Record::new()
747            .field("foo")
748            .contract(TypeF::String)
749            .no_value(&alloc)
750            .build(&alloc);
751
752        assert_eq!(
753            ast,
754            build_record(
755                &alloc,
756                iter::once((
757                    "foo",
758                    record::FieldMetadata::from(Annotation {
759                        contracts: alloc.alloc_singleton(Type {
760                            typ: TypeF::String,
761                            pos: TermPos::None
762                        }),
763                        ..Default::default()
764                    }),
765                    None
766                )),
767                false
768            )
769        );
770    }
771
772    #[test]
773    fn exercise_metadata() {
774        let alloc = AstAlloc::new();
775
776        let ast: Ast = Record::new()
777            .field("foo")
778            .priority(MergePriority::Bottom)
779            .doc("foo?")
780            .contract(TypeF::String)
781            .types(TypeF::Number)
782            .optional(true)
783            .not_exported(true)
784            .no_value(&alloc)
785            .build(&alloc);
786
787        assert_eq!(
788            ast,
789            build_record(
790                &alloc,
791                iter::once((
792                    "foo",
793                    FieldMetadata {
794                        doc: Some(alloc.alloc_str("foo?")),
795                        opt: true,
796                        priority: MergePriority::Bottom,
797                        not_exported: true,
798                        annotation: Annotation {
799                            typ: Some(Type {
800                                typ: TypeF::Number,
801                                pos: TermPos::None,
802                            }),
803                            contracts: alloc.alloc_singleton(Type {
804                                typ: TypeF::String,
805                                pos: TermPos::None
806                            }),
807                        },
808                    },
809                    None,
810                )),
811                Default::default()
812            )
813        );
814    }
815}