92 lines
2.6 KiB
Go
92 lines
2.6 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"github.com/pelletier/go-toml/v2"
|
|
)
|
|
|
|
// ClientOptions controls platform-specific static validation. Production uses
|
|
// CheckFilesystem; documentation tests may perform syntax-only validation for
|
|
// a non-native target.
|
|
type ClientOptions struct {
|
|
Platform Platform
|
|
CheckFilesystem bool
|
|
}
|
|
|
|
func LoadServer(path string) (*Server, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read server config: %w", err)
|
|
}
|
|
return DecodeServer(data)
|
|
}
|
|
|
|
func DecodeServer(data []byte) (*Server, error) {
|
|
return DecodeServerWithOverrides(data, nil)
|
|
}
|
|
|
|
// DecodeServerWithOverrides applies only explicitly present flag values after
|
|
// TOML decoding. Keys come from the single registry in overrides.go.
|
|
func DecodeServerWithOverrides(data []byte, overrides map[string]string) (*Server, error) {
|
|
var raw = defaultServerFile()
|
|
if err := strictDecode(data, &raw); err != nil {
|
|
return nil, fmt.Errorf("decode server config: %w", err)
|
|
}
|
|
if err := applyServerOverrides(&raw, overrides); err != nil {
|
|
return nil, err
|
|
}
|
|
return validateServer(raw)
|
|
}
|
|
|
|
func LoadClient(path string, platform Platform) (*Client, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read client config: %w", err)
|
|
}
|
|
return DecodeClient(data, ClientOptions{Platform: platform, CheckFilesystem: true})
|
|
}
|
|
|
|
func DecodeClient(data []byte, options ClientOptions) (*Client, error) {
|
|
return DecodeClientWithOverrides(data, options, nil)
|
|
}
|
|
|
|
// DecodeClientWithOverrides applies explicitly present flags after the file.
|
|
func DecodeClientWithOverrides(data []byte, options ClientOptions, overrides map[string]string) (*Client, error) {
|
|
var raw = defaultClientFile()
|
|
if err := strictDecode(data, &raw); err != nil {
|
|
return nil, fmt.Errorf("decode client config: %w", err)
|
|
}
|
|
if err := applyClientOverrides(&raw, overrides); err != nil {
|
|
return nil, err
|
|
}
|
|
return validateClient(raw, options)
|
|
}
|
|
|
|
func strictDecode(data []byte, target any) error {
|
|
if !utf8.Valid(data) {
|
|
return fmt.Errorf("configuration is not valid UTF-8")
|
|
}
|
|
decoder := toml.NewDecoder(bytes.NewReader(data))
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(target); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseDuration(name, value string, allowZero bool) (time.Duration, error) {
|
|
duration, err := time.ParseDuration(value)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: %w", name, err)
|
|
}
|
|
if duration < 0 || (!allowZero && duration == 0) {
|
|
return 0, fmt.Errorf("%s must be %s", name, map[bool]string{true: "non-negative", false: "positive"}[allowZero])
|
|
}
|
|
return duration, nil
|
|
}
|