github.com/haraldrudell/parl@v0.4.176/yamlo/apply-yaml.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 // Package yamlo allows the yamler package to unmarshal unexported types and finds yaml configuration files. 7 package yamlo 8 9 import ( 10 "strings" 11 12 "github.com/haraldrudell/parl" 13 "github.com/haraldrudell/parl/perrors" 14 "github.com/haraldrudell/parl/pflags" 15 ) 16 17 const ( 18 // key name from options or a default ‘options’ 19 defaultTopKey = "options" 20 ) 21 22 // ApplyYaml updates effective options with values read from a yaml file 23 // - program is app name “date” used to construct yaml file name 24 // - yamlFile is a specified file, or for empty string, a scan for files: 25 // - — filename: [program]-[hostname].yaml [program].yaml 26 // - — Directories: ~/apps .. /etc 27 // - — if a specified file is missing, that is error 28 // - — if no default file exists, or file was empty, 29 // no yaml options are loaded 30 // - yamlDictionaryKey is the key read form the top-level dictionary 31 // in yaml, empty string is default “options:” 32 // - genericYaml is a wrapper of unknown types 33 // - optionData is the list of options, containing pointers to effective 34 // option values 35 // - The top entry in the yaml file must be a dictionary 36 // - The value for yamlDictionaryKey must be a dictionary 37 // - the remainder of the yamlDictionaryKey is read when it matches 38 // the YamData struct 39 // - -verbose=yamlo.ApplyYaml “github.com/haraldrudell/parl/yamlo.ApplyYaml” 40 func ApplyYaml( 41 program, yamlFile, yamlDictionaryKey string, doYaml bool, 42 genericYaml GenericYaml, 43 optionData []pflags.OptionData, 44 ) (err error) { 45 if genericYaml == nil { 46 panic(perrors.NewPF("genericYaml cannot be nil")) 47 } else if !doYaml { 48 return 49 } 50 51 // read text from the yaml file 52 var yamlText []byte 53 parl.Debug("Arguments: yamlFile: %q yamlKey: %q", yamlFile, yamlDictionaryKey) 54 if yamlDictionaryKey == "" { 55 yamlDictionaryKey = defaultTopKey 56 } 57 // the filename actually read 58 var filename string 59 if filename, yamlText, err = FindFile(yamlFile, program); err != nil { 60 return // yaml read failure return 61 } else if filename == "" || len(yamlText) == 0 { 62 parl.Debug("no yaml file") 63 return // no default file existed, or file was empty: noop 64 } 65 parl.Debug("filename: %q top-level key: %q bytes: %q", filename, yamlDictionaryKey, string(yamlText)) 66 67 // unmarshal yaml into genericYaml value pointer 68 // - ie. main.y effective yaml values 69 var hasData bool 70 if hasData, err = genericYaml.Unmarshal(yamlText, yamlDictionaryKey); perrors.IsPF(&err, "filename: %q: %w", filename, err) { 71 return // error during unmarshaling return 72 } else if !hasData { 73 parl.Debug("has no data") 74 return // yaml contained no data, eg. no “options:” key 75 } 76 77 // get a map of the field references that is appearing in yaml 78 var yamlVisistedReferences map[any]string 79 if yamlVisistedReferences, err = genericYaml.VisitedReferencesMap(yamlText, yamlDictionaryKey); err != nil { 80 return // unmarshal failure return 81 } 82 yamlText = nil 83 if parl.IsThisDebug() { 84 parl.Log("yamlVisitedKeys: %v\n", yamlVisistedReferences) 85 var oMap = make(map[string]any) 86 for _, o := range optionData { 87 var yamlFieldReference = o.Y 88 if yamlFieldReference == nil { 89 continue 90 } 91 oMap[o.Name] = o.Y 92 } 93 parl.Log("option-references: %v\n", oMap) 94 parl.Log("yaml-y: %s", genericYaml.YDump()) 95 var effectiveValueMap, _ = pflags.OptionValues(optionData) 96 parl.Log("option-values: %v\n", effectiveValueMap) 97 } 98 99 // get map of visited options 100 var visitedOptions = pflags.NewVisitedOptions().Map() 101 if parl.IsThisDebug() { 102 var opts []string 103 for k := range visitedOptions { 104 opts = append(opts, k) 105 } 106 parl.Log("visitedOptions: %s", strings.Join(opts, "\x20")) 107 } 108 109 // iterate over options and apply yaml changes 110 // - ignore if no yaml key 111 // - ignore if yamlVisitedKeys exists and do not have the option 112 for _, optionData := range optionData { 113 if visitedOptions[optionData.Name] || // was specified on command line, overrides yaml 114 optionData.Y == nil || // does not have yaml value 115 yamlVisistedReferences[optionData.Y] == "" { // was not visted by yaml 116 continue 117 } 118 if err = optionData.ApplyYaml(); err != nil { 119 return 120 } 121 } 122 123 if parl.IsThisDebug() { 124 var effectiveValueMap, _ = pflags.OptionValues(optionData) 125 parl.Log("resulting option-values: %v\n", effectiveValueMap) 126 } 127 128 return 129 }