1package file 2 3import ( 4 "context" 5 "encoding/json" 6 "os" 7 "path/filepath" 8 9 "github.com/BurntSushi/toml" 10 "github.com/heetch/confita/backend" 11 "github.com/pkg/errors" 12 "gopkg.in/yaml.v2" 13) 14 15// Backend that loads a configuration from a file. 16// It supports json and yaml formats. 17type Backend struct { 18 path string 19 name string 20 optional bool 21} 22 23// NewBackend creates a configuration loader that loads from a file. 24// The content will get decoded based on the file extension. 25// If optional parameter is set to true, calling Unmarshal won't return an error if the file doesn't exist. 26func NewBackend(path string) *Backend { 27 name := filepath.Ext(path) 28 if name != "" { 29 name = name[1:] 30 } 31 32 return &Backend{ 33 path: path, 34 name: name, 35 } 36} 37 38// NewOptionalBackend implementation is exactly the same as NewBackend except that 39// if the file is not found, backend.ErrNotFound will be returned. 40func NewOptionalBackend(path string) *Backend { 41 name := filepath.Ext(path) 42 if name != "" { 43 name = name[1:] 44 } 45 46 return &Backend{ 47 path: path, 48 name: name, 49 optional: true, 50 } 51} 52 53// Unmarshal takes a struct pointer and unmarshals the file into it, 54// using either json or yaml based on the file extention. 55func (b *Backend) Unmarshal(ctx context.Context, to interface{}) error { 56 f, err := os.Open(b.path) 57 if err != nil { 58 if b.optional { 59 return backend.ErrNotFound 60 } 61 return errors.Wrapf(err, "failed to open file at path \"%s\"", b.path) 62 } 63 defer f.Close() 64 65 switch ext := filepath.Ext(b.path); ext { 66 case ".json": 67 err = json.NewDecoder(f).Decode(to) 68 case ".yml": 69 fallthrough 70 case ".yaml": 71 err = yaml.NewDecoder(f).Decode(to) 72 case ".toml": 73 _, err = toml.DecodeReader(f, to) 74 default: 75 err = errors.Errorf("unsupported extension \"%s\"", ext) 76 } 77 78 return errors.Wrapf(err, "failed to decode file \"%s\"", b.path) 79} 80 81// Get is not implemented. 82func (b *Backend) Get(ctx context.Context, key string) ([]byte, error) { 83 return nil, errors.New("not implemented") 84} 85 86// Name returns the type of the file. 87func (b *Backend) Name() string { 88 return b.name 89} 90