@@ -410,6 +410,14 @@ impl ParameterItem {
410410 self . serde . is_some ( ) && !self . get_serde_skip ( )
411411 }
412412
413+ /// Returns true if this field has a CLI or env source (not just serde)
414+ pub fn has_cli_or_env_source ( & self ) -> bool {
415+ self . short_switch . is_some ( )
416+ || self . long_switch . is_some ( )
417+ || self . env_name . is_some ( )
418+ || self . is_positional
419+ }
420+
413421 pub fn gen_push_program_options (
414422 & self ,
415423 program_options_ident : & Ident ,
@@ -631,6 +639,58 @@ impl ParameterItem {
631639 & self ,
632640 conf_context_ident : & Ident ,
633641 ) -> Result < ( TokenStream , bool ) , syn:: Error > {
642+ // If there's no CLI or env source, this parameter wasn't registered with clap,
643+ // so we handle it specially
644+ if !self . has_cli_or_env_source ( ) {
645+ // Check for default value first, before checking if optional
646+ // This ensures that optional fields with defaults get Some(default) not None
647+ if let Some ( default_value) = & self . default_value {
648+ // Serde-only field with default: use gen_initializer_helper with callbacks
649+ // that provide the default value instead of reading from conf_context
650+ let default_value_str = default_value. value ( ) ;
651+
652+ let if_no_conf_context_val = |req : ExprRequest | -> TokenStream {
653+ match req {
654+ ExprRequest :: Str => quote ! {
655+ ( :: conf:: ConfValueSource :: Default , #default_value_str)
656+ } ,
657+ ExprRequest :: OsStr => quote ! {
658+ ( :: conf:: ConfValueSource :: Default , :: std:: ffi:: OsStr :: new( #default_value_str) )
659+ } ,
660+ }
661+ } ;
662+
663+ return self . gen_initializer_helper ( conf_context_ident, & if_no_conf_context_val, None ) ;
664+ } else if self . is_optional_type . is_some ( ) {
665+ // Serde-only optional field without default: return None
666+ return Ok ( (
667+ quote ! {
668+ {
669+ let _ = #conf_context_ident;
670+ Ok ( None )
671+ }
672+ } ,
673+ false ,
674+ ) ) ;
675+ } else {
676+ // Serde-only required field with no default: this is an error
677+ // This field can only be satisfied from a serde document, and it's not optional,
678+ // so if we're here it means either no document was provided or the field was missing
679+ let id = self . field_name . to_string ( ) ;
680+ return Ok ( (
681+ quote ! {
682+ {
683+ // Get the program option for this field to use in the error
684+ let opt = #conf_context_ident. get_program_option_by_id( #id)
685+ . expect( "internal error: program option should exist for this field" ) ;
686+ Err ( #conf_context_ident. missing_required_parameter_error( opt) )
687+ }
688+ } ,
689+ false ,
690+ ) ) ;
691+ }
692+ }
693+
634694 // Default behavior when no conf context value is found
635695 let if_no_conf_context_val = |_req : ExprRequest | {
636696 if self . is_optional_type . is_some ( ) {
@@ -656,6 +716,25 @@ impl ParameterItem {
656716 doc_name : & Ident ,
657717 doc_val : & Ident ,
658718 ) -> Result < ( TokenStream , bool ) , Error > {
719+ // If there's no CLI or env source, this parameter wasn't registered with clap,
720+ // so we just return the doc value directly
721+ if !self . has_cli_or_env_source ( ) {
722+ let id = self . field_name . to_string ( ) ;
723+ return Ok ( (
724+ quote ! {
725+ {
726+ let _ = #conf_context_ident;
727+ #conf_context_ident. log_config_event(
728+ #id,
729+ :: conf:: ConfValueSource :: Document ( #doc_name)
730+ ) ;
731+ Ok ( #doc_val)
732+ }
733+ } ,
734+ false ,
735+ ) ) ;
736+ }
737+
659738 let use_value_parser = self
660739 . serde
661740 . as_ref ( )
0 commit comments