gonfig/gonfig.go

86 lines
2.4 KiB
Go
Raw Permalink Normal View History

2024-12-04 20:37:26 +00:00
// Package gonfig provides utilities for reading, unmarshaling, and validating
// configuration files.
package gonfig
import (
"errors"
"fmt"
"os"
)
2024-12-05 18:20:15 +00:00
// UnmarshalFunc is a function that unmarshals raw bytes into a configuration
// object of type T.
type UnmarshalFunc[T any] func([]byte, T) error
2024-12-11 04:29:46 +00:00
// FinalizeFunc is a function that validates a configuration object of type T
2024-12-05 18:20:15 +00:00
// and returns an error if validation fails.
2024-12-11 04:29:46 +00:00
type FinalizeFunc[T any] func(T) error
2024-12-04 20:37:26 +00:00
// ReadConfig reads a configuration file, unmarshals its content into the given
2024-12-05 18:20:15 +00:00
// configuration object, and validates it.
//
// If the primary path is empty, it searches for the configuration file in the
// fallback paths. Returns the resolved path or an error if the file cannot be
// located, read, unmarshaled, or validated.
2024-12-11 04:29:46 +00:00
func ReadConfig[T any](path string, searchPaths []string, c *T, unmarshal UnmarshalFunc[*T], finalize FinalizeFunc[*T]) (string, error) {
2024-12-04 20:37:26 +00:00
var err error
2024-12-05 18:20:15 +00:00
path, err = FindConfig(path, searchPaths)
2024-12-04 20:37:26 +00:00
if err != nil {
2024-12-05 18:20:15 +00:00
return "", err
2024-12-04 20:37:26 +00:00
}
2024-12-11 04:29:46 +00:00
return path, ReadFoundConfig(path, c, unmarshal, finalize)
2024-12-05 18:20:15 +00:00
}
// ReadFoundConfig reads and processes a configuration file from a known path.
//
// Unmarshals the file's content into the given configuration object and
// validates it. Returns an error if the file cannot be read, unmarshaled, or
// validated.
2024-12-11 04:29:46 +00:00
func ReadFoundConfig[T any](path string, c *T, unmarshal UnmarshalFunc[*T], finalize FinalizeFunc[*T]) error {
2024-12-04 20:37:26 +00:00
content, err := os.ReadFile(path)
if err != nil {
2024-12-05 18:20:15 +00:00
return fmt.Errorf("unable to read configuration file %s: %w", path, err)
2024-12-04 20:37:26 +00:00
}
err = unmarshal(content, c)
if err != nil {
2024-12-05 18:20:15 +00:00
return fmt.Errorf("unable to unmarshal configuration file %s: %w", path, err)
2024-12-04 20:37:26 +00:00
}
2024-12-11 04:29:46 +00:00
return finalize(c)
2024-12-04 20:37:26 +00:00
}
2024-12-05 18:20:15 +00:00
// FindConfig determines the path to the configuration file by using the
2024-12-04 20:37:26 +00:00
// provided primary path or searching through a list of fallback paths if the
2024-12-05 18:20:15 +00:00
// primary path is empty.
//
// Returns the resolved path or an error if no valid file is found or if the
// file is inaccessible.
func FindConfig(path string, paths []string) (string, error) {
2024-12-04 20:37:26 +00:00
var err error
if path == "" {
for _, p := range paths {
_, err = os.Stat(p)
if err != nil {
continue
}
path = p
}
if path == "" {
return "", errors.New("could not locate configuration file")
}
} else {
_, err = os.Stat(path)
if err != nil {
return "", fmt.Errorf("could not stat configuration file %s: %w", path, err)
}
}
return path, nil
}