Reputation: 349
I'm writing a daemon's configuration handler and utilizing the yaml
package to do so. Importing my file works like this:
package daemon
import (
"ioutil"
"log"
"gopkg.in/yaml.v2"
)
type daemonConfig struct {
BindAddress string `yaml:"bind_address"`
BindPort int `yaml:"bind_port"`
VerifySSL bool `yaml:"verify_ssl"`
}
I easily unmarshall data from my YAML files like so:
func (config *daemonConfig) getConf() *daemonConfig {
yamlFile, err := ioutil.ReadFile("config.yaml")
if err != nil {
log.Fatal("Unable to open config.yaml:", err)
}
err = yaml.Unmarshal(yamlFile, config)
if err != nil {
log.Fatal("Failed to unmarshall config.yaml:", err)
}
config, err = setDefaults(config)
return config
}
My question is regarding my custom setDefaults function. If a field isn't provided, like bind_port
or bind_address
, I'd like to just set them to defaults:
func setDefaults(config *daemonConfig) (*daemonConfig, error) {
if len(config.BindAddress) <= 0 {
config.BindAddress = "0.0.0.0"
}
if config.BindPort == 0 {
config.BindPort = 9999
}
return config, nil
}
You'll note I'm not setting a default to verify_ssl
; when yaml
unmarshalls this and fails to find the field, it initializes the bool
as false
, which is precisely the opposite of what I want the default behavior to be. I'd prefer the user to explicitly set verification of SSL to be off, instead of having it off by default if not specified. If I have a totally empty config.yaml
, and expect the default values to be set, it will always put verify_ssl
as false (this log is from elsewhere in the application):
2019/02/19 03:56:08 Currently loaded config: {0.0.0.0 9999 false}
How would I go about, if this field is not present in the YAML fields that are being unmarshalled, to checking if the line exists? I could just read the file manually and check for that parameter first, but I was wondering if there's a more elegant way with what I have; otherwise, I'll just use ioutil
and string checking to do it. Thanks!
Upvotes: 4
Views: 6041
Reputation: 349
Thanks to Thomas and mh-cbon I found out I can just use a pointer for this. If I change my VerifySSL
field to utilize a pointer and add omitempty
:
type daemonConfig struct {
BindAddress string `yaml:"bind_address"`
BindPort int `yaml:"bind_port"`
VerifySSL *bool `yaml:"verify_ssl",omitempty`
}
I can simply dereference the field in the struct to get the true value of what yaml
unmarshalled. Setting defaults is as easy as:
func setDefaults(config *daemonConfig) (*daemonConfig, error) {
if len(config.BindAddress) <= 0 {
config.BindAddress = "0.0.0.0"
}
if config.BindPort == 0 {
config.BindPort = 9999
}
if config.VerifySSL == nil {
ssl := true
config.VerifySSL = &ssl
}
return config, nil
}
If it's loaded in the file, config.VerifySSL
will contain the address of the bool
, so it won't be nil
, and if it's truly not in the file at all then the field is simply nil
and I can set it or get it via a pointer.
EDIT:
Just a note, if your VerifySSL
pointer is nil, you can't assign it like this:
*config.VerifySSL = true
That's dereferencing a nil
pointer. I've updated the solution to include something more manageable.
Upvotes: 5