conf_derive 0.4.5

Derive macro crate used with conf
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
use super::StructItem;
use crate::util::type_is_bool;

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
    Error, Expr, Field, Ident, Lifetime, LitStr, Meta, Path, Token, Type, punctuated::Punctuated,
};

mod flag_item;
mod flatten_item;
mod parameter_item;
mod repeat_item;
mod subcommands_item;

use flag_item::FlagItem;
use flatten_item::FlattenItem;
use parameter_item::ParameterItem;
use repeat_item::RepeatItem;
use subcommands_item::SubcommandsItem;

/// Type of value parser - indicates whether it takes &str or &OsStr
pub enum ValueParserExpr {
    /// Parser takes &str (most common case)
    Str(Expr),
    /// Parser takes &OsStr (for PathBuf, OsString, or explicit value_parser_os)
    OsStr(Expr),
}

/// Indicates the type of data that an expr needs to produce in some setting.
pub enum ExprRequest {
    /// We need an expr manipulating str
    Str,
    /// We need an expr manipulating OsStr
    OsStr,
}

/// Indicates the strategy used for serde deserialization
///
/// To perform deserialization of structs with ConfSerde,
/// each field has a "state machine".
///
/// Given a map access, we enter a loop of the form:
///
/// while let Some(key) = ma.next_key::<...>()? {
///   match key.as_str() {
///
///   }
/// }
///
/// During this loop, a local variable whose name matches each field is in scope,
/// and it's value is Option<StateMachine>, initially None.
///
/// In the simplest case, StateMachine is simply Option<#field_type>, and
/// initialization happens in one shot, producing #field_type, or failing and
/// producing an Error, which goes to the error buffer.
/// In the most complex case, it's something implementing InitializationStateMachine.
///
/// After this loop, we must "finalize" any state machines, converting every variable
/// #field_name to have type Option<Option<#field_type>> and gathering errors.
///
/// Then, any values that are still None, meaning they weren't visited as we traversed
/// the serde document, should be initialized using the non-serde code path.
/// This step uses an unwrap-or-else and invokes the codegen from the no-serde version.
/// At that point, every #field_name is Option<#field_type>. We use the same "gather"
/// routine used in the non-serde path to reconstitute the struct, run validations, and
/// handle errors from there.
///
/// For any field, a SerdeStrategy has to tell us:
/// * What is the type of "StateMachine"
/// * What match arm should we use
/// * How do we finalize this state machine
///
/// Finally, if there are errors, we return the errors, otherwise we use a match expression
/// to unwrap all these options and initialize the struct. We also run any validation
/// predicates, and ultimately return it if everything is okay.
///
/// The SerdeStrategy carries all this data in one collection, to help ensure that it is computed
/// in a cohesive way.
pub struct SerdeStrategy {
    /// The state machine type. If omitted, then it falls back to Option<#field_type>,
    /// which is the correct state machine in simple cases.
    /// When present, `InitializationStateMachine::finalize` will be called on the state machine type
    /// at the end of the serde walk.
    pub state_machine_type: Option<Type>,
    /// The state machine initializer. If omitted, it is None
    pub state_machine_init: Option<TokenStream>,
    /// The match expr to use
    pub match_expr: TokenStream,
    /// The serde keys. This is used to generate the match pattern.
    pub serde_keys: SerdeKeys,
}

impl SerdeStrategy {
    /// Used for serde(skip) fields
    pub fn skip() -> Self {
        Self {
            state_machine_type: None,
            state_machine_init: None,
            match_expr: quote! {},
            serde_keys: SerdeKeys::default(),
        }
    }
}

/// Represents the serde keys that a field "subscribes" to.
/// These keys (or expression) are used in the match expression when new serde data comes in.
#[derive(Clone)]
pub enum SerdeKeys {
    /// Literal strings (primary key and aliases) that match to this field
    Lit(Vec<LitStr>),
    /// An arbitrary run-time expression to use for the match pattern
    Expr(TokenStream),
}

impl Default for SerdeKeys {
    fn default() -> Self {
        Self::Lit(Default::default())
    }
}

impl SerdeKeys {
    // Generate match pattern
    pub fn gen_match_pattern(&self) -> Option<TokenStream> {
        match self {
            Self::Lit(v) => {
                if v.is_empty() {
                    None
                } else {
                    Some(quote! { #(#v)|* })
                }
            }
            Self::Expr(e) => Some(quote! { #e }),
        }
    }

    // Get the (primary) key to use in help messages, if any is available
    pub fn help_key(&self) -> Option<&LitStr> {
        match self {
            Self::Lit(v) => v.first(),
            Self::Expr(_) => None,
        }
    }

    // Check if every SerdeKeys instance is SerdeKeys::Lit.
    // If they are then flat-map the whole iterator together.
    // If any are not, then return None.
    pub fn all_literal_keys<'a>(
        mut iter: impl Iterator<Item = &'a SerdeKeys>,
    ) -> Option<Vec<&'a LitStr>> {
        iter.try_fold(vec![], |mut vec, serde_keys| -> Option<Vec<&LitStr>> {
            match serde_keys {
                SerdeKeys::Lit(v) => {
                    vec.extend(v);
                }
                SerdeKeys::Expr(_) => {
                    return None;
                }
            }
            Some(vec)
        })
    }
}

/// #[conf(...)] options listed in a field of a struct which has `#[derive(Conf)]`
#[allow(clippy::large_enum_variant)]
pub enum FieldItem {
    Flag(FlagItem),
    Parameter(ParameterItem),
    Repeat(RepeatItem),
    Flatten(FlattenItem),
    Subcommands(SubcommandsItem),
}

impl FieldItem {
    pub fn new(field: &Field, struct_item: &StructItem) -> Result<Self, Error> {
        // First, inspect the first field attribute.
        // If the first attribute is 'flag', 'parameter', 'repeat', or 'flatten', then that's how
        // we're going to handle it.
        for attr in &field.attrs {
            if attr.path().is_ident("conf") || attr.path().is_ident("arg") {
                let nested =
                    attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
                if let Some(meta) = nested.first() {
                    let path = meta.path();
                    if path.is_ident("flag") {
                        return Ok(Self::Flag(FlagItem::new(field, struct_item)?));
                    } else if path.is_ident("parameter") {
                        return Ok(Self::Parameter(ParameterItem::new(field, struct_item)?));
                    } else if path.is_ident("repeat") {
                        return Ok(Self::Repeat(RepeatItem::new(field, struct_item)?));
                    } else if path.is_ident("flatten") {
                        return Ok(Self::Flatten(FlattenItem::new(field, struct_item)?));
                    } else if path.is_ident("subcommands") {
                        return Ok(Self::Subcommands(SubcommandsItem::new(field, struct_item)?));
                    }
                }
            }
        }

        // We're still not sure, so inspect the type.
        // If it's bool, it's a flag. Otherwise it's a parameter.
        Ok(if type_is_bool(&field.ty) {
            Self::Flag(FlagItem::new(field, struct_item)?)
        } else {
            Self::Parameter(ParameterItem::new(field, struct_item)?)
        })
    }

    /// True if this field represents a single program option
    pub fn is_single_option(&self) -> bool {
        matches!(
            self,
            Self::Flag(..) | Self::Parameter(..) | Self::Repeat(..)
        )
    }

    /// Get the field name
    pub fn get_field_name(&self) -> &Ident {
        match self {
            Self::Flag(item) => item.get_field_name(),
            Self::Parameter(item) => item.get_field_name(),
            Self::Repeat(item) => item.get_field_name(),
            Self::Flatten(item) => item.get_field_name(),
            Self::Subcommands(item) => item.get_field_name(),
        }
    }

    /// Get the field type
    pub fn get_field_type(&self) -> Type {
        match self {
            Self::Flag(item) => item.get_field_type(),
            Self::Parameter(item) => item.get_field_type(),
            Self::Repeat(item) => item.get_field_type(),
            Self::Flatten(item) => item.get_field_type(),
            Self::Subcommands(item) => item.get_field_type(),
        }
    }

    /// Generate a Node<ProgramOption> expression for this field.
    /// Returns None if this field type doesn't contribute to program options (e.g., Subcommands).
    pub fn gen_program_option_node(&self) -> Result<Option<TokenStream>, Error> {
        match self {
            Self::Flag(item) => item.gen_program_option_node(),
            Self::Parameter(item) => item.gen_program_option_node(),
            Self::Repeat(item) => item.gen_program_option_node(),
            Self::Flatten(item) => item.gen_program_option_node(),
            Self::Subcommands(item) => item.gen_program_option_node(),
        }
    }

    /// Generate code that constructs (one or more) subcommands as needed and pushes them onto
    /// subcommands_ident
    pub fn gen_push_subcommands(
        &self,
        subcommands_ident: &Ident,
        parsed_env: &Ident,
    ) -> Result<TokenStream, Error> {
        match self {
            Self::Flag(item) => item.gen_push_subcommands(subcommands_ident, parsed_env),
            Self::Parameter(item) => item.gen_push_subcommands(subcommands_ident, parsed_env),
            Self::Repeat(item) => item.gen_push_subcommands(subcommands_ident, parsed_env),
            Self::Flatten(item) => item.gen_push_subcommands(subcommands_ident, parsed_env),
            Self::Subcommands(item) => item.gen_push_subcommands(subcommands_ident, parsed_env),
        }
    }

    /// Generate code for a struct initializer for this field, reading from conf_context
    ///
    /// Returns:
    /// * a TokenStream for initializer expression, which can use `?` to return errors,
    /// * a bool which is true if the error type is `Vec<InnerError>` and false if it is
    ///   `InnerError`
    fn gen_initializer(&self, conf_context_ident: &Ident) -> Result<(TokenStream, bool), Error> {
        match self {
            Self::Flag(item) => item.gen_initializer(conf_context_ident),
            Self::Parameter(item) => item.gen_initializer(conf_context_ident),
            Self::Repeat(item) => item.gen_initializer(conf_context_ident),
            Self::Flatten(item) => item.gen_initializer(conf_context_ident),
            Self::Subcommands(item) => item.gen_initializer(conf_context_ident),
        }
    }

    /// Generate code for a struct initializer for this field, reading from conf_context
    ///
    /// Returns:
    /// * a TokenStream for initializer expression, which can use `?` to return errors,
    /// * a bool which is true if the error type is `Vec<InnerError>` and false if it is
    ///   `InnerError`
    fn gen_initializer_with_doc_val(
        &self,
        conf_context_ident: &Ident,
        doc_name_ident: &Ident,
        doc_val_ident: &Ident,
    ) -> Result<(TokenStream, bool), Error> {
        match self {
            Self::Flag(item) => {
                item.gen_initializer_with_doc_val(conf_context_ident, doc_name_ident, doc_val_ident)
            }
            Self::Parameter(item) => {
                item.gen_initializer_with_doc_val(conf_context_ident, doc_name_ident, doc_val_ident)
            }
            Self::Repeat(item) => {
                item.gen_initializer_with_doc_val(conf_context_ident, doc_name_ident, doc_val_ident)
            }
            Self::Flatten(_item) => unimplemented!("uses a custom match arm"),
            Self::Subcommands(_item) => unimplemented!("would have to use a custom match arm"),
        }
    }

    /// Generate code of the form
    ///
    /// {
    ///   fn #field_name(conf_context: &...) -> Result<#field_type, InnerError> { .. }
    ///   match field_name(...) {
    ///     Ok(t) => Some(t),
    ///     Err(err) => { errors.push(err); None },
    ///   }
    /// }
    ///
    /// This value can then be assigned to a variable, e.g.
    ///
    /// let #field_name = #initializer;
    ///
    /// The code block reads from a conf context and pushes any errors to given errors buffer.
    ///
    /// Arguments:
    /// * conf_context_ident is a variable of type &ConfContext which is in scope
    /// * errors_ident is a variable of type mut Vec<InnerError> which is in scope, which we can
    ///   push to.
    pub fn gen_initialize_from_conf_context_and_push_errors(
        &self,
        conf_context_ident: &Ident,
        errors_ident: &Ident,
    ) -> Result<TokenStream, Error> {
        let field_name = self.get_field_name();
        let field_type = self.get_field_type();
        let (initializer, returns_multiple_errors) = self.gen_initializer(conf_context_ident)?;
        // The initializer is the portion e.g.
        // fn(conf_context: &...) -> Result<T, conf::InnerError> { ... }
        //
        // It returns `Result<T, Vec<conf::InnerError>>` if returns_multiple_errors is true,
        // otherwise it's Result<T, conf::InnerError> It is allowed to read
        // #conf_context_ident but not modify it We have to put it inside a locally defined
        // fn so that it cannot modify the errors buffer etc.

        let (error_type, extend_fn) = if returns_multiple_errors {
            (
                quote! { ::std::vec::Vec<::conf::InnerError> },
                quote! { extend },
            )
        } else {
            (quote! { ::conf::InnerError }, quote! { push })
        };

        Ok(quote! {
          {
            fn #field_name(
              #conf_context_ident: &::conf::ConfContext<'_>
            ) -> Result<#field_type, #error_type> {
              #initializer
            }
            match #field_name(#conf_context_ident) {
              Ok(val) => Some(val),
              Err(errs) => {
                #errors_ident.#extend_fn(errs);
                None
              }
            }
          }
        })
    }

    /// Generate code of the form
    ///
    /// {
    ///   fn #field_name(c: &ConfContext, d: ...) -> Result<#field_type, Vec<InnerError> {
    ///      ..
    ///   }
    ///   match #field_name(...) {
    ///     Ok(val) => Some(val),
    ///     Err(err) => #errors_ident.extend(err);
    ///   }
    /// }
    ///
    /// which can then be assigned to a variable, e.g. #field_name
    ///
    /// The code block reads from a conf context and a value from document, resolves it, and pushes
    /// any errors to given errors ident.
    ///
    /// Arguments:
    /// * conf_context_ident is a variable of type ConfContext which is in scope, which we won't
    ///   consume
    /// * doc_name_ident is a variable of type &str, which is the name of the document this value
    ///   came from
    /// * doc_val_ident is a variable of type #serde_type, which was parsed from serde successfully.
    /// * errors_ident is a variable of type mut Vec<InnerError> which is in scope, which we can
    ///   push to.
    pub fn gen_initialize_from_conf_context_and_doc_val_and_push_errors(
        &self,
        conf_context_ident: &Ident,
        doc_name_ident: &Ident,
        doc_val_ident: &Ident,
        errors_ident: &Ident,
    ) -> Result<TokenStream, Error> {
        let field_name = self.get_field_name();
        let field_type = self.get_field_type();
        let serde_type = self.get_serde_type();
        let (initializer, returns_multiple_errors) =
            self.gen_initializer_with_doc_val(conf_context_ident, doc_name_ident, doc_val_ident)?;
        // The initializer is the portion e.g.
        // fn(conf_context: &..., doc_val: T) -> Result<T, conf::InnerError> { ... }
        //
        // It returns `Result<T, Vec<conf::InnerError>>` if returns_multiple_errors is true,
        // otherwise it's Result<T, conf::InnerError>
        // It is allowed to read #conf_context_ident but not modify it
        // We have to put it inside a locally defined
        // fn so that it cannot modify the errors buffer etc.

        let (error_type, extend_fn) = if returns_multiple_errors {
            (
                quote! { ::std::vec::Vec<::conf::InnerError> },
                quote! { extend },
            )
        } else {
            (quote! { ::conf::InnerError }, quote! { push })
        };

        Ok(quote! {
          {
            fn #field_name(
              #conf_context_ident: &::conf::ConfContext<'_>,
              #doc_name_ident: &str,
              #doc_val_ident: #serde_type
            ) -> Result<#field_type, #error_type> {
              #initializer
            }
            match #field_name(&#conf_context_ident, #doc_name_ident, #doc_val_ident) {
              Ok(val) => Some(val),
              Err(errs) => {
                #errors_ident.#extend_fn(errs);
                None
              }
            }
          }
        })
    }

    /// Generate (one or more) match arms for iterating a serde MapAccess object.
    /// The match takes place on a `&str` representing the struct key.
    /// The match arm(s) generated here should identify one or more `&str`
    /// and perform initialization appropriately.
    ///
    /// Returns a TokenStream for the match arm, and a list of `serde_name`'s which
    /// we want to advertise in error messages.
    ///
    /// Arguments:
    /// * Ident for &conf_serde_context which is in scope and may be consumed
    /// * Ident for NextValueProducer object which is in scope and may be consumed
    /// * Ident for NextValueProducer type
    /// * Ident for errors buffer which is in scope, to which we may push.
    pub fn gen_serde_strategy(
        &self,
        ct: &Lifetime,
        ctxt: &Ident,
        nvp: &Ident,
        nvp_type: &Ident,
        errors_ident: &Ident,
    ) -> Result<SerdeStrategy, Error> {
        if self.get_serde_skip() {
            return Ok(SerdeStrategy::skip());
        }
        match self {
            Self::Flag(_) | Self::Parameter(_) | Self::Repeat(_) => {
                self.gen_simple_serde_strategy(ct, ctxt, nvp, nvp_type, errors_ident)
            }
            Self::Flatten(item) => item.gen_serde_strategy(ct, ctxt, nvp, nvp_type, errors_ident),
            Self::Subcommands(item) => {
                item.gen_serde_strategy(ct, ctxt, nvp, nvp_type, errors_ident)
            }
        }
    }

    // A simple serde match arm is used at a "terminal", i.e. a flag, parameter, or repeat field.
    // These are fields that represent only a single program option.
    // This is a terminal in the sense that we don't recurse further using `DeserializeSeed` and our
    // own traits.
    //
    // The field has controls on what happens in this match arm via:
    // * get_serde_name()
    // * get_serde_aliases()
    // * get_serde_type()
    // * get_serde_deserialize_with()
    // * gen_initializer_with_doc_val()
    fn gen_simple_serde_strategy(
        &self,
        _ct: &Lifetime,
        ctxt: &Ident,
        nvp: &Ident,
        nvp_type: &Ident,
        errors_ident: &Ident,
    ) -> Result<SerdeStrategy, Error> {
        let field_name = self.get_field_name();
        let field_name_str = field_name.to_string();

        let serde_name_str = self.get_serde_name();
        let serde_aliases = self.get_serde_aliases();
        let serde_type = self.get_serde_type();
        let deserialize_with = self.get_serde_deserialize_with();

        let conf_context_ident = Ident::new("__conf_context__", Span::call_site());
        let doc_name_ident = Ident::new("__doc_name__", Span::call_site());
        let doc_val_ident = Ident::new("__doc_val__", Span::call_site());
        let initializer = self.gen_initialize_from_conf_context_and_doc_val_and_push_errors(
            &conf_context_ident,
            &doc_name_ident,
            &doc_val_ident,
            errors_ident,
        )?;

        // Generate the deserialization call - either using deserialize_with or the default Deserialize impl
        let deserialize_call = if let Some(deserialize_with_path) = deserialize_with {
            quote! {
              {
                struct __DeserializeWith;
                impl<'dedede2> ::serde::de::DeserializeSeed<'dedede2> for __DeserializeWith {
                  type Value = #serde_type;
                  fn deserialize<__D>(self, __deserializer: __D) -> ::std::result::Result<Self::Value, __D::Error>
                  where
                    __D: ::serde::de::Deserializer<'dedede2>
                  {
                    (#deserialize_with_path)(__deserializer)
                  }
                }
                #nvp.next_value_seed(__DeserializeWith)
              }
            }
        } else {
            quote! {
              #nvp.next_value::<#serde_type>()
            }
        };

        let match_expr = quote! {
          {
            if #field_name.is_some() {
              #errors_ident.push(
                InnerError::serde(
                  #ctxt.document_name,
                  #field_name_str,
                  #nvp_type::Error::duplicate_field(#serde_name_str)
                )
              );
            } else {
              #field_name = Some(match #deserialize_call {
                Ok(#doc_val_ident) => {
                  let #conf_context_ident: &::conf::ConfContext = &#ctxt.conf_context;
                  let #doc_name_ident: &str = #ctxt.document_name;
                  #initializer
                }
                Err(__err__) => {
                  #errors_ident.push(
                    InnerError::serde(
                      #ctxt.document_name,
                      #field_name_str,
                      __err__
                    )
                  );
                  None
                }
              });
            }
          },
        };

        // Return all names for error messages
        let mut all_names = vec![serde_name_str];
        all_names.extend(serde_aliases);

        Ok(SerdeStrategy {
            state_machine_type: None,
            state_machine_init: None,
            match_expr,
            serde_keys: SerdeKeys::Lit(all_names),
        })
    }

    /// Get the serde name (only when "is_single_option" is true)
    fn get_serde_name(&self) -> LitStr {
        match self {
            Self::Flag(item) => item.get_serde_name(),
            Self::Parameter(item) => item.get_serde_name(),
            Self::Repeat(item) => item.get_serde_name(),
            Self::Flatten(_item) => unimplemented!(),
            Self::Subcommands(_item) => unimplemented!(),
        }
    }

    /// Get the serde aliases (only when "is_single_option" is true)
    fn get_serde_aliases(&self) -> Vec<LitStr> {
        match self {
            Self::Flag(item) => item.get_serde_aliases(),
            Self::Parameter(item) => item.get_serde_aliases(),
            Self::Repeat(item) => item.get_serde_aliases(),
            Self::Flatten(_item) => unimplemented!(),
            Self::Subcommands(_item) => unimplemented!(),
        }
    }

    /// Get the serde deserialize_with expression (only when "is_single_option" is true)
    fn get_serde_deserialize_with(&self) -> Option<Path> {
        match self {
            Self::Flag(item) => item.get_serde_deserialize_with(),
            Self::Parameter(item) => item.get_serde_deserialize_with(),
            Self::Repeat(item) => item.get_serde_deserialize_with(),
            Self::Flatten(_item) => unimplemented!(),
            Self::Subcommands(_item) => unimplemented!(),
        }
    }

    /// Get the serde type (only when "is_single_option" is true)
    fn get_serde_type(&self) -> Type {
        match self {
            Self::Flag(item) => item.get_serde_type(),
            Self::Parameter(item) => item.get_serde_type(),
            Self::Repeat(item) => item.get_serde_type(),
            Self::Flatten(_item) => unimplemented!(),
            Self::Subcommands(_item) => unimplemented!(),
        }
    }

    /// Get the serde(skip) option
    fn get_serde_skip(&self) -> bool {
        match self {
            Self::Flag(item) => item.get_serde_skip(),
            Self::Parameter(item) => item.get_serde_skip(),
            Self::Repeat(item) => item.get_serde_skip(),
            Self::Flatten(item) => item.get_serde_skip(),
            Self::Subcommands(item) => item.get_serde_skip(),
        }
    }

    /// Generate debug assertions for this field
    pub fn gen_debug_asserts(&self, struct_ident: &Ident) -> Result<TokenStream, Error> {
        match self {
            Self::Flag(item) => item.gen_debug_asserts(struct_ident),
            Self::Parameter(item) => item.gen_debug_asserts(struct_ident),
            Self::Repeat(item) => item.gen_debug_asserts(struct_ident),
            Self::Flatten(item) => item.gen_debug_asserts(struct_ident),
            Self::Subcommands(item) => item.gen_debug_asserts(struct_ident),
        }
    }
}