internal/config/config.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | package config
import (
"maps"
"net/url"
"os"
"time"
"github.com/pelletier/go-toml/v2"
"github.com/pkg/errors"
"go.alanpearce.eu/x/log"
)
var Version string
var DevMode bool
const MaxResultsShowAll = 10_000
type URL struct {
*url.URL
}
func (u *URL) MarshalText() ([]byte, error) {
return []byte(u.URL.String()), nil
}
func (u *URL) UnmarshalText(text []byte) (err error) {
u.URL, err = url.Parse(string(text))
if err != nil {
return errors.WithMessagef(err, "could not parse URL %s", string(text))
}
return nil
}
func (u *URL) JoinPath(elems ...string) *URL {
return &URL{u.URL.JoinPath(elems...)}
}
func (u *URL) AddRawQuery(key, value string) *URL {
u.RawQuery = key + "=" + value
return u
}
type Duration struct {
time.Duration
}
func (d *Duration) MarshalText() ([]byte, error) {
return []byte(d.Duration.String()), nil
}
func (d *Duration) UnmarshalText(text []byte) (err error) {
d.Duration, err = time.ParseDuration(string(text))
if err != nil {
return errors.WithMessagef(err, "could not parse duration %s", string(text))
}
return nil
}
func mustURL(in string) (u URL) {
var err error
u.URL, err = url.Parse(in)
if err != nil {
panic(errors.Errorf("URL cannot be parsed: %s", in))
}
return u
}
// this type is necessary as nix's `fromTOML` doesn't support TOML date/time formats
type LocalTime struct {
toml.LocalTime
}
func (t *LocalTime) MarshalText() ([]byte, error) {
b, err := t.LocalTime.MarshalText()
if err != nil {
return nil, errors.WithMessage(err, "could not marshal time value")
}
return b, nil
}
func (t *LocalTime) UnmarshalText(in []byte) (err error) {
err = t.LocalTime.UnmarshalText(in)
if err != nil {
return errors.WithMessage(err, "could not parse time value")
}
return nil
}
func mustLocalTime(in string) (time LocalTime) {
err := time.UnmarshalText([]byte(in))
if err != nil {
panic(errors.Errorf("Could not parse time: %s", in))
}
return
}
func GetConfig(filename string, log *log.Logger) (*Config, error) {
config := DefaultConfig
if filename != "" {
log.Debug("reading config", "filename", filename)
f, err := os.Open(filename)
if err != nil {
return nil, errors.Wrap(err, "reading config failed")
}
defer f.Close()
dec := toml.NewDecoder(f)
dec.DisallowUnknownFields()
err = dec.Decode(&config)
if err != nil {
var tomlError *toml.DecodeError
if errors.As(err, &tomlError) {
return nil, errors.WithMessage(err, tomlError.Error())
}
var missingConfigError *toml.StrictMissingError
if errors.As(err, &missingConfigError) {
return nil, errors.Errorf("unexpected config: %s", missingConfigError.String())
}
return nil, errors.Wrap(err, "config error")
}
}
DevMode = config.Web.Environment == "development"
config.Web.ContentSecurityPolicy.ScriptSrc = append(
config.Web.ContentSecurityPolicy.ScriptSrc,
config.Web.BaseURL.JoinPath("/static/").String(),
)
maps.DeleteFunc(config.Importer.Sources, func(_ string, v *Source) bool {
return !v.Enable
})
return &config, nil
}
|