feat: switch from errors to fault
32 files changed, 299 insertions(+), 286 deletions(-)
changed files
- .golangci.yaml
- frontend/assets.go
- go.mod
- go.sum
- gomod2nix.toml
- internal/config/config.go
- internal/config/fetcher.go
- internal/config/repository.go
- internal/fetcher/channel.go
- internal/fetcher/download.go
- internal/fetcher/http/buf.go
- internal/fetcher/http/http.go
- internal/fetcher/main.go
- internal/fetcher/nixpkgs-channel.go
- internal/file/utils.go
- internal/importer/importer.go
- internal/importer/main.go
- internal/importer/options.go
- internal/importer/package.go
- internal/importer/utils.go
- internal/index/index_meta.go
- internal/index/indexer.go
- internal/index/search.go
- internal/index/search_test.go
- internal/manpages/manpages.go
- internal/nix/option.go
- internal/programs/programs.go
- internal/server/dev.go
- internal/server/logging.go
- internal/server/mux.go
- internal/server/server.go
- web/searchix.go
M .golangci.yaml → .golangci.yaml
@@ -32,11 +32,9 @@ - .Errorf( - errors.New( - errors.Unwrap( - errors.Join( - - .Wrap( - - .Wrapf( - - .WithMessage( - - .WithMessagef( - - .WithStack( + - fault.New( + - fault.Newf( + - fault.Wrap( - (context.Context).Err( exclusions: generated: lax
M frontend/assets.go → frontend/assets.go
@@ -8,7 +8,8 @@ "hash/fnv" "io" "io/fs" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) var Assets = &AssetCollection{@@ -30,17 +31,17 @@ Stylesheets []*Asset ByPath map[string]*Asset } -func newAsset(filename string) (*Asset, errors.E) { +func newAsset(filename string) (*Asset, error) { file, err := Files.Open(filename) if err != nil { - return nil, errors.WithMessagef(err, "could not open file %s", filename) + return nil, fault.Wrap(err, fmsg.Withf("could not open file %s", filename)) } defer file.Close() shasum := sha256.New() hash := fnv.New64a() if _, err := io.Copy(io.MultiWriter(shasum, hash), file); err != nil { - return nil, errors.WithMessagef(err, "could not hash file %s", filename) + return nil, fault.Wrap(err, fmsg.Withf("could not hash file %s", filename)) } return &Asset{@@ -51,10 +52,10 @@ Base64SHA256: base64.StdEncoding.EncodeToString(shasum.Sum(nil)), }, nil } -func hashScripts() errors.E { +func hashScripts() error { scripts, err := fs.Glob(Files, "static/**.js") if err != nil { - return errors.WithMessage(err, "could not glob files") + return fault.Wrap(err, fmsg.With("could not glob files")) } for _, filename := range scripts { asset, err := newAsset(filename)@@ -68,10 +69,10 @@ return nil } -func hashStyles() errors.E { +func hashStyles() error { styles, err := fs.Glob(Files, "static/**.css") if err != nil { - return errors.WithMessage(err, "could not glob files") + return fault.Wrap(err, fmsg.With("could not glob files")) } for _, filename := range styles { asset, err := newAsset(filename)@@ -85,7 +86,7 @@ return nil } -func Rehash() (err errors.E) { +func Rehash() (err error) { Assets.Scripts = []*Asset{} err = hashScripts() if err != nil {
M go.mod → go.mod
@@ -6,6 +6,7 @@ require ( alin.ovh/gomponents v1.6.0 alin.ovh/x v1.0.0 badc0de.net/pkg/flagutil v1.0.1 + github.com/Southclaws/fault v0.8.2 github.com/andybalholm/brotli v1.1.1 github.com/bcicen/jstream v1.0.1 github.com/blevesearch/bleve/v2 v2.5.2@@ -20,7 +21,6 @@ github.com/pelletier/go-toml/v2 v2.2.4 github.com/stefanfritsch/goldmark-fences v1.0.0 github.com/stoewer/go-strcase v1.3.0 github.com/yuin/goldmark v1.7.12 - gitlab.com/tozd/go/errors v0.10.0 go.uber.org/zap v1.27.0 golang.org/x/net v0.41.0 modernc.org/sqlite v1.38.0
M go.sum → go.sum
@@ -9,6 +9,8 @@ github.com/Code-Hex/dd v1.1.0 h1:VEtTThnS9l7WhpKUIpdcWaf0B8Vp0LeeSEsxA1DZseI= github.com/Code-Hex/dd v1.1.0/go.mod h1:VaMyo/YjTJ3d4qm/bgtrUkT2w+aYwJ07Y7eCWyrJr1w= github.com/RoaringBitmap/roaring/v2 v2.5.0 h1:TJ45qCM7D7fIEBwKd9zhoR0/S1egfnSSIzLU1e1eYLY= github.com/RoaringBitmap/roaring/v2 v2.5.0/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= +github.com/Southclaws/fault v0.8.2 h1:hbQANoRWYVWnQjpwJlNlfaolM+oIihgoFowaY3EBLCs= +github.com/Southclaws/fault v0.8.2/go.mod h1:VUVkAWutC59SL16s6FTqf3I6I2z77RmnaW5XRz4bLOE= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck=@@ -142,8 +144,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -gitlab.com/tozd/go/errors v0.10.0 h1:A98kL+gaDvWnY6ZB/u8zP+sYaWsWUGBHeFMtamvW/74= -gitlab.com/tozd/go/errors v0.10.0/go.mod h1:q3Ugr0C8dCzMEkrzjjlV2qNsm9e0KvqBjwcbcjCpBe4= go.etcd.io/bbolt v1.4.1 h1:5mOV+HWjIPLEAlUGMsveaUvK2+byZMFOzojoi7bh7uI= go.etcd.io/bbolt v1.4.1/go.mod h1:c8zu2BnXWTu2XM4XcICtbGSl9cFwsXtcf9zLt2OncM8= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
M gomod2nix.toml → gomod2nix.toml
@@ -16,6 +16,9 @@ hash = "sha256-9aoekzjMXuJmR0/7bfu4y3SfcWBgdfYybB7gt4sNKfk=" [mod."github.com/RoaringBitmap/roaring/v2"] version = "v2.5.0" hash = "sha256-wyIQtrFZHO3G+1tKPoDpee+frmzDZDqfE581vBnAMYI=" + [mod."github.com/Southclaws/fault"] + version = "v0.8.2" + hash = "sha256-ifMHIRqeMNV2+jhi5jxA42iK1DATNPYI4pN69inRGT4=" [mod."github.com/andybalholm/brotli"] version = "v1.1.1" hash = "sha256-kCt+irK1gvz2lGQUeEolYa5+FbLsfWlJMCd5hm+RPgQ="@@ -151,9 +154,6 @@ hash = "sha256-l5E2DVNyQLmMx8V1CrTQ+v55aC4rhjteOm4fNlND7UI=" [mod."github.com/yuin/goldmark"] version = "v1.7.12" hash = "sha256-thLYBS4woL2X5qRdo7vP+xCvjlGRDU0jXtDCUt6vvWM=" - [mod."gitlab.com/tozd/go/errors"] - version = "v0.10.0" - hash = "sha256-oW37KsieVKJOWk9ZXbGuQvuU4nyJCZzgYrTZHFkoCs4=" [mod."go.etcd.io/bbolt"] version = "v1.4.1" hash = "sha256-FqKrJJxOOfAnvziKKFzI6VUGDp+ga6IgZmuihHp2I2A="
M internal/config/config.go → internal/config/config.go
@@ -1,15 +1,17 @@ package config import ( + "errors" "maps" "net/url" "os" "time" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/creasty/defaults" "github.com/pelletier/go-toml/v2" - "gitlab.com/tozd/go/errors" ) var (@@ -30,7 +32,7 @@ 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 fault.Wrap(err, fmsg.Withf("could not parse URL %s", string(text))) } return nil@@ -57,7 +59,7 @@ 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 fault.Wrap(err, fmsg.Withf("could not parse duration %s", string(text))) } return nil@@ -67,7 +69,7 @@ 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)) + panic(fault.Newf("URL cannot be parsed: %s", in)) } return u@@ -81,7 +83,7 @@ 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 nil, fault.Wrap(err, fmsg.With("could not marshal time value")) } return b, nil@@ -90,7 +92,7 @@ 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 fault.Wrap(err, fmsg.With("could not parse time value")) } return nil@@ -99,19 +101,19 @@ func mustLocalTime(in string) (time LocalTime) { err := time.UnmarshalText([]byte(in)) if err != nil { - panic(errors.Errorf("Could not parse time: %s", in)) + panic(fault.Newf("Could not parse time: %s", in)) } return } -func GetConfig(filename string, log *log.Logger) (*Config, errors.E) { +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") + return nil, fault.Wrap(err, fmsg.With("reading config failed")) } defer f.Close()@@ -121,14 +123,14 @@ err = dec.Decode(&config) if err != nil { var tomlError *toml.DecodeError if errors.As(err, &tomlError) { - return nil, errors.WithMessage(err, tomlError.Error()) + return nil, fault.Wrap(err, fmsg.With(tomlError.Error())) } var missingConfigError *toml.StrictMissingError if errors.As(err, &missingConfigError) { - return nil, errors.Errorf("unexpected config: %s", missingConfigError.String()) + return nil, fault.Newf("unexpected config: %s", missingConfigError.String()) } - return nil, errors.Wrap(err, "config error") + return nil, fault.Wrap(err, fmsg.With("config error")) } }@@ -148,7 +150,7 @@ if v.Key == "" { v.Key = k } if err := defaults.Set(v); err != nil { - return nil, errors.Wrap(err, "setting defaults failed") + return nil, fault.Wrap(err, fmsg.With("setting defaults failed")) } }
M internal/config/fetcher.go → internal/config/fetcher.go
@@ -3,8 +3,8 @@ import ( "fmt" + "github.com/Southclaws/fault" "github.com/stoewer/go-strcase" - "gitlab.com/tozd/go/errors" ) type Fetcher int@@ -38,7 +38,7 @@ return ChannelNixpkgs, nil case "download": return Download, nil default: - return UnknownFetcher, errors.Errorf("unsupported fetcher %s", name) + return UnknownFetcher, fault.Newf("unsupported fetcher %s", name) } }
M internal/config/repository.go → internal/config/repository.go
@@ -5,7 +5,8 @@ "fmt" "net/url" "strings" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) type RepoType int@@ -22,7 +23,7 @@ Repo string Revision string `toml:"-"` } -func (r *Repository) GetRawFileURL(path string) (string, errors.E) { +func (r *Repository) GetRawFileURL(path string) (string, error) { switch r.Type { case GitHub: ref := r.Revision@@ -31,19 +32,19 @@ ref = "master" } u, err := url.JoinPath("https://github.com/", r.Owner, r.Repo, "raw", ref, path) if err != nil { - return "", errors.Wrap(err, "failed to join path") + return "", fault.Wrap(err, fmsg.With("failed to join path")) } return u, nil default: - return "", errors.Errorf( + return "", fault.Newf( "don't know how to generate a repository URL for %s", r.Type.String(), ) } } -func (r *Repository) GetFileURL(path string, line ...string) (string, errors.E) { +func (r *Repository) GetFileURL(path string, line ...string) (string, error) { switch r.Type { case GitHub: ref := r.Revision@@ -52,7 +53,7 @@ ref = "master" } u, err := url.JoinPath("https://github.com/", r.Owner, r.Repo, "blob", ref, path) if err != nil { - return "", errors.Wrap(err, "failed to join path") + return "", fault.Wrap(err, fmsg.With("failed to join path")) } if len(line) > 0 { u += fmt.Sprintf("#L%s", line[0])@@ -60,7 +61,7 @@ } return u, nil default: - return "", errors.Errorf( + return "", fault.Newf( "don't know how to generate a repository URL for %s", r.Type.String(), )@@ -90,12 +91,12 @@ return fmt.Sprintf("RepoType(%d)", f) } } -func parseRepoType(name string) (RepoType, errors.E) { +func parseRepoType(name string) (RepoType, error) { switch strings.ToLower(name) { case "github": return GitHub, nil default: - return UnknownRepoType, errors.Errorf("unsupported repo type %s", name) + return UnknownRepoType, fault.Newf("unsupported repo type %s", name) } }
M internal/fetcher/channel.go → internal/fetcher/channel.go
@@ -13,8 +13,8 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/index" "alin.ovh/x/log" - - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) type ChannelFetcher struct {@@ -26,7 +26,7 @@ func NewChannelFetcher( source *config.Source, logger *log.Logger, -) (*ChannelFetcher, errors.E) { +) (*ChannelFetcher, error) { switch source.Importer { case config.Options: return &ChannelFetcher{@@ -34,14 +34,14 @@ Source: source, Logger: logger, }, nil default: - return nil, errors.Errorf("unsupported importer type %s", source.Importer) + return nil, fault.Newf("unsupported importer type %s", source.Importer) } } func (i *ChannelFetcher) FetchIfNeeded( ctx context.Context, sourceMeta *index.SourceMeta, -) (*FetchedFiles, errors.E) { +) (*FetchedFiles, error) { args := []string{ "--no-build-output", "--timeout",@@ -60,7 +60,7 @@ i.Logger.Debug("nix-build command", "args", args) cmd := exec.CommandContext(ctx, "nix-build", args...) out, err := cmd.Output() if err != nil { - return nil, errors.WithMessage(err, "failed to run nix-build (--dry-run)") + return nil, fault.Wrap(err, fmsg.With("failed to run nix-build (--dry-run)")) } outPath := path.Join(strings.TrimSpace(string(out)), i.Source.OutputPath, "options.json")@@ -77,7 +77,7 @@ } file, err := os.Open(outPath) if err != nil { - return nil, errors.WithMessage(err, "failed to open options.json") + return nil, fault.Wrap(err, fmsg.With("failed to open options.json")) } return &FetchedFiles{
M internal/fetcher/download.go → internal/fetcher/download.go
@@ -7,9 +7,10 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/fetcher/http" "alin.ovh/searchix/internal/index" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" ) type DownloadFetcher struct {@@ -21,7 +22,7 @@ func NewDownloadFetcher( source *config.Source, logger *log.Logger, -) (*DownloadFetcher, errors.E) { +) (*DownloadFetcher, error) { switch source.Importer { case config.Options, config.Packages: return &DownloadFetcher{@@ -29,7 +30,7 @@ Source: source, Logger: logger, }, nil default: - return nil, errors.Errorf("unsupported importer type %s", source.Importer) + return nil, fault.Newf("unsupported importer type %s", source.Importer) } }@@ -42,7 +43,7 @@ func (i *DownloadFetcher) FetchIfNeeded( ctx context.Context, sourceMeta *index.SourceMeta, -) (*FetchedFiles, errors.E) { +) (*FetchedFiles, error) { f := &FetchedFiles{} sourceUpdated := sourceMeta.Updated@@ -58,11 +59,13 @@ for _, filename := range filesToFetch { fetchURL, baseErr := url.JoinPath(i.Source.URL, filename) if baseErr != nil { - return nil, errors.WithMessagef( + return nil, fault.Wrap( baseErr, - "could not build URL with elements %s and %s", - i.Source.URL, - filename, + fmsg.Withf( + "could not build URL with elements %s and %s", + i.Source.URL, + filename, + ), ) }@@ -72,7 +75,7 @@ body, mtime, err := http.FetchFileIfNeeded(ctx, i.Logger, sourceUpdated, fetchURL) if err != nil { i.Logger.Warn("failed to fetch file", "url", fetchURL, "error", err) - return nil, err + return nil, fault.Wrap(err, fmsg.Withf("could not fetch file %s", filename)) } // don't bother to issue requests for the later files if mtime.Before(sourceUpdated) {@@ -88,7 +91,7 @@ f.Options = body case files["packages"]: f.Packages = body default: - return f, errors.Errorf("unknown filename %s", filename) + return f, fault.Newf("unknown filename %s", filename) } }
M internal/fetcher/http/buf.go → internal/fetcher/http/buf.go
@@ -4,7 +4,7 @@ import ( "bufio" "io" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" ) type Reader struct {@@ -20,5 +20,5 @@ } } func (r *Reader) Close() error { - return errors.WithStack(r.closer.Close()) + return fault.Wrap(r.closer.Close()) }
M internal/fetcher/http/http.go → internal/fetcher/http/http.go
@@ -11,8 +11,9 @@ "alin.ovh/searchix/internal/config" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/andybalholm/brotli" - "gitlab.com/tozd/go/errors" ) type brotliReadCloser struct {@@ -28,7 +29,7 @@ } } func (r *brotliReadCloser) Close() error { - return errors.Wrap(r.src.Close(), "failed to call close on underlying reader") + return fault.Wrap(r.src.Close(), fmsg.With("failed to call close on underlying reader")) } func FetchFileIfNeeded(@@ -36,7 +37,7 @@ ctx context.Context, log *log.Logger, mtime time.Time, url string, -) (io.ReadCloser, time.Time, errors.E) { +) (io.ReadCloser, time.Time, error) { var newMtime time.Time var ifModifiedSince string if !mtime.IsZero() {@@ -45,10 +46,9 @@ } req, baseErr := http.NewRequestWithContext(ctx, "GET", url, http.NoBody) if baseErr != nil { - return nil, newMtime, errors.WithMessagef( + return nil, newMtime, fault.Wrap( baseErr, - "could not create HTTP request for %s", - url, + fmsg.Withf("could not create HTTP request for %s", url), ) }@@ -59,11 +59,14 @@ req.Header.Set("If-Modified-Since", ifModifiedSince) } res, baseErr := http.DefaultClient.Do(req) if baseErr != nil { - return nil, newMtime, errors.WithMessagef(baseErr, "could not make HTTP request to %s", url) + return nil, newMtime, fault.Wrap( + baseErr, + fmsg.Withf("could not make HTTP request to %s", url), + ) } var body io.ReadCloser - var err errors.E + var err error switch res.StatusCode { case http.StatusNotModified: newMtime = mtime@@ -88,10 +91,10 @@ body = newBrotliReader(res.Body) case "", "identity", "gzip": body = res.Body default: - err = errors.Errorf("cannot handle a body with content-encoding %s", ce) + err = fault.Newf("cannot handle a body with content-encoding %s", ce) } default: - err = errors.Errorf("got response code %d, don't know what to do", res.StatusCode) + err = fault.Newf("got response code %d, don't know what to do", res.StatusCode) } return NewReadCloser(body), newMtime, err
M internal/fetcher/main.go → internal/fetcher/main.go
@@ -7,8 +7,7 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/index" "alin.ovh/x/log" - - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" ) type FetchedFiles struct {@@ -18,13 +17,13 @@ Packages io.ReadCloser } type Fetcher interface { - FetchIfNeeded(context.Context, *index.SourceMeta) (*FetchedFiles, errors.E) + FetchIfNeeded(context.Context, *index.SourceMeta) (*FetchedFiles, error) } func New( source *config.Source, logger *log.Logger, -) (fetcher Fetcher, err errors.E) { +) (fetcher Fetcher, err error) { switch source.Fetcher { case config.ChannelNixpkgs: fetcher, err = NewNixpkgsChannelFetcher(source, logger)@@ -33,7 +32,7 @@ fetcher, err = NewChannelFetcher(source, logger) case config.Download: fetcher, err = NewDownloadFetcher(source, logger) default: - err = errors.Errorf("unsupported fetcher type %s", source.Fetcher.String()) + err = fault.Newf("unsupported fetcher type %s", source.Fetcher.String()) } return
M internal/fetcher/nixpkgs-channel.go → internal/fetcher/nixpkgs-channel.go
@@ -9,7 +9,8 @@ "alin.ovh/searchix/internal/fetcher/http" "alin.ovh/searchix/internal/index" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) type NixpkgsChannelFetcher struct {@@ -17,16 +18,16 @@ Source *config.Source Logger *log.Logger } -func makeChannelURL(channel string, subPath string) (string, errors.E) { +func makeChannelURL(channel string, subPath string) (string, error) { url, err := url.JoinPath("https://channels.nixos.org/", channel, subPath) - return url, errors.WithMessagef(err, "error creating URL") + return url, fault.Wrap(err, fmsg.Withf("error creating URL")) } func NewNixpkgsChannelFetcher( source *config.Source, logger *log.Logger, -) (*NixpkgsChannelFetcher, errors.E) { +) (*NixpkgsChannelFetcher, error) { switch source.Importer { case config.Options, config.Packages: return &NixpkgsChannelFetcher{@@ -34,7 +35,7 @@ Source: source, Logger: logger, }, nil default: - return nil, errors.Errorf("unsupported importer type %s", source.Importer) + return nil, fault.Newf("unsupported importer type %s", source.Importer) } }@@ -47,7 +48,7 @@ func (i *NixpkgsChannelFetcher) FetchIfNeeded( ctx context.Context, sourceMeta *index.SourceMeta, -) (f *FetchedFiles, err errors.E) { +) (f *FetchedFiles, err error) { f = &FetchedFiles{} filesToFetch := make([]string, 2)@@ -69,9 +70,7 @@ i.Logger.Debug("attempting to fetch file", "url", fetchURL) body, mtime, err := http.FetchFileIfNeeded(ctx, i.Logger, sourceMeta.Updated, fetchURL) if err != nil { - i.Logger.Warn("failed to fetch file", "url", fetchURL, "error", err) - - return f, err + return f, fault.Wrap(err, fmsg.Withf("failed to fetch file with url %s", fetchURL)) } // don't bother to issue requests for the later files if mtime.Before(sourceMeta.Updated) {@@ -87,7 +86,7 @@ f.Options = body case packagesFileName: f.Packages = body default: - return f, errors.Errorf("unknown file kind %s", filename) + return f, fault.Newf("unknown file kind %s", filename) } }
M internal/file/utils.go → internal/file/utils.go
@@ -1,52 +1,54 @@ package file import ( + "errors" "io" "io/fs" "os" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) -func Mkdirp(dir string) errors.E { +func Mkdirp(dir string) error { err := os.MkdirAll(dir, os.ModeDir|os.ModePerm) if err != nil { - return errors.WithMessagef(err, "could not create directory %s", dir) + return fault.Wrap(err, fmsg.Withf("could not create directory %s", dir)) } return nil } -func NeedNotExist(err error) errors.E { +func NeedNotExist(err error) error { if err != nil && !errors.Is(err, fs.ErrNotExist) { - return errors.WithStack(err) + return fault.Wrap(err) } return nil } -func StatIfExists(file string) (fs.FileInfo, errors.E) { +func StatIfExists(file string) (fs.FileInfo, error) { stat, err := os.Stat(file) return stat, NeedNotExist(err) } -func Exists(file string) (bool, errors.E) { +func Exists(file string) (bool, error) { stat, err := StatIfExists(file) return stat != nil, err } -func WriteToFile(path string, body io.Reader) errors.E { +func WriteToFile(path string, body io.Reader) error { file, err := os.Create(path) if err != nil { - return errors.WithMessagef(err, "error creating file at %s", path) + return fault.Wrap(err, fmsg.Withf("error creating file at %s", path)) } defer file.Close() _, err = io.Copy(file, body) if err != nil { - return errors.WithMessagef(err, "error downloading file %s", path) + return fault.Wrap(err, fmsg.Withf("error downloading file %s", path)) } return nil
M internal/importer/importer.go → internal/importer/importer.go
@@ -7,11 +7,10 @@ "alin.ovh/searchix/internal/index" "alin.ovh/searchix/internal/nix" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" ) type Processor interface { - Process(context.Context) (<-chan nix.Importable, <-chan errors.E) + Process(context.Context) (<-chan nix.Importable, <-chan error) } func process(@@ -19,7 +18,7 @@ ctx context.Context, indexer *index.WriteIndex, processor Processor, logger *log.Logger, -) (bool, errors.E) { +) (bool, error) { wg := sync.WaitGroup{} wg.Add(1)@@ -29,7 +28,7 @@ wg.Add(1) iErrs := indexer.Import(ctx, objects) var hadObjectErrors bool - var criticalError errors.E + var criticalError error go func() { for { select {
M internal/importer/main.go → internal/importer/main.go
@@ -2,6 +2,7 @@ package importer import ( "context" + "errors" "fmt" "maps" "math"@@ -18,7 +19,8 @@ "alin.ovh/searchix/internal/programs" "alin.ovh/x/log" "github.com/getsentry/sentry-go" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) type Options struct {@@ -58,14 +60,14 @@ func (imp *Importer) createSourceImporter( parent context.Context, meta *index.Meta, forceUpdate bool, -) func(*config.Source) errors.E { - return func(source *config.Source) errors.E { +) func(*config.Source) error { + return func(source *config.Source) error { logger := imp.log.With("name", source.Key) logger.Debug("starting fetcher") fetcher, err := fetcher.New(source, logger) if err != nil { - return errors.WithMessage(err, "error creating fetcher") + return fault.Wrap(err, fmsg.With("error creating fetcher")) } sourceMeta := meta.GetSourceMeta(source.Key)@@ -93,7 +95,7 @@ ) } } - return errors.WithMessage(err, "importer fetch failed") + return fault.Wrap(err, fmsg.With("importer fetch failed")) } logger.Info( "importer fetch succeeded",@@ -147,12 +149,12 @@ pdb, ) } if err != nil { - return errors.WithMessagef(err, "failed to create processor") + return fault.Wrap(err, fmsg.Withf("failed to create processor")) } hadWarnings, err := process(ctx, imp.indexer, processor, logger) if err != nil { - return errors.WithMessagef(err, "failed to process source") + return fault.Wrap(err, fmsg.Withf("failed to process source")) } if source.Manpages.Enable {@@ -187,7 +189,7 @@ func (imp *Importer) Start( ctx context.Context, forceUpdate bool, onlyUpdateSources *[]string, -) errors.E { +) error { if len(imp.config.Importer.Sources) == 0 { imp.log.Info("No sources enabled")@@ -220,7 +222,7 @@ } err := imp.indexer.SaveMeta() if err != nil { - return errors.Wrap(err, "failed to save metadata") + return fault.Wrap(err, fmsg.With("failed to save metadata")) } SetLastUpdated(time.Now())@@ -231,7 +233,7 @@ func New( cfg *config.Config, options *Options, -) (*Importer, errors.E) { +) (*Importer, error) { return &Importer{ config: cfg, log: options.Logger,@@ -243,13 +245,13 @@ func (imp *Importer) EnsureSourcesIndexed( ctx context.Context, read *index.ReadIndex, -) errors.E { +) error { cfgEnabledSources := slices.Collect(maps.Keys(imp.config.Importer.Sources)) slices.Sort(cfgEnabledSources) indexedSources, err := read.GetEnabledSources() if err != nil { - return errors.Wrap(err, "Failed to get enabled sources from index") + return fault.Wrap(err, fmsg.With("Failed to get enabled sources from index")) } slices.Sort(indexedSources) if !slices.Equal(cfgEnabledSources, indexedSources) {@@ -267,7 +269,7 @@ false, &newSources, ) if err != nil { - return errors.Wrap(err, "Failed to update index with new sources") + return fault.Wrap(err, fmsg.With("Failed to update index with new sources")) } } if len(retiredSources) > 0 {@@ -275,7 +277,7 @@ imp.log.Info("removing retired sources", "sources", retiredSources) for _, s := range retiredSources { err := imp.indexer.DeleteBySource(s) if err != nil { - return errors.Wrapf(err, "Failed to remove retired source %s", s) + return fault.Wrap(err, fmsg.Withf("Failed to remove retired source %s", s)) } } }@@ -290,7 +292,7 @@ localHub *sentry.Hub, ) { const monitorSlug = "import" localHub.WithScope(func(scope *sentry.Scope) { - var err errors.E + var err error scope.SetContext("monitor", sentry.Context{"slug": monitorSlug}) monitorConfig := &sentry.MonitorConfig{ Schedule: sentry.IntervalSchedule(1, sentry.MonitorScheduleUnitDay),
M internal/importer/options.go → internal/importer/options.go
@@ -9,9 +9,10 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/nix" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/bcicen/jstream" "github.com/mitchellh/mapstructure" - "gitlab.com/tozd/go/errors" ) type nixValueJSON struct {@@ -68,7 +69,7 @@ func NewOptionProcessor( infile io.ReadCloser, source *config.Source, log *log.Logger, -) (*OptionIngester, errors.E) { +) (*OptionIngester, error) { i := OptionIngester{ dec: jstream.NewDecoder(infile, source.JSONDepth).EmitKV(), log: log,@@ -87,16 +88,16 @@ }) if err != nil { defer infile.Close() - return nil, errors.WithMessage(err, "could not create mapstructure decoder") + return nil, fault.Wrap(err, fmsg.With("could not create mapstructure decoder")) } i.ms = ms return &i, nil } -func (i *OptionIngester) Process(ctx context.Context) (<-chan nix.Importable, <-chan errors.E) { +func (i *OptionIngester) Process(ctx context.Context) (<-chan nix.Importable, <-chan error) { results := make(chan nix.Importable) - errs := make(chan errors.E) + errs := make(chan error) go func() { defer i.infile.Close()@@ -111,12 +112,12 @@ break outer default: } if err := i.dec.Err(); err != nil { - errs <- errors.WithMessage(err, "could not decode JSON") + errs <- fault.Wrap(err, fmsg.With("could not decode JSON")) continue } if mv.ValueType != jstream.Object { - errs <- errors.Errorf("unexpected object type %s", ValueTypeToString(mv.ValueType)) + errs <- fault.Newf("unexpected object type %s", ValueTypeToString(mv.ValueType)) continue }@@ -130,10 +131,9 @@ case reflect.String: s := decl.String() link, err := MakeChannelLink(i.source.Repo, s) if err != nil { - errs <- errors.WithMessagef(err, - "could not make a channel link for channel %s, revision %s and subpath %s", + errs <- fault.Wrap(err, fmsg.Withf("could not make a channel link for channel %s, revision %s and subpath %s", i.source.Channel, i.source.Repo.Revision, s, - ) + )) continue }@@ -146,7 +146,7 @@ URL: v["url"].(string), } decls = append(decls, &link) default: - errs <- errors.Errorf("unexpected declaration type %s", decl.Kind().String()) + errs <- fault.Newf("unexpected declaration type %s", decl.Kind().String()) continue }@@ -158,7 +158,7 @@ i.optJSON = nixOptionJSON{} err := i.ms.Decode(x) // stores in optJSON if err != nil { - errs <- errors.WithMessagef(err, "failed to decode option %#v", x) + errs <- fault.Wrap(err, fmsg.Withf("failed to decode option %#v", x)) continue }
M internal/importer/package.go → internal/importer/package.go
@@ -13,9 +13,10 @@ "alin.ovh/searchix/internal/nix" "alin.ovh/searchix/internal/programs" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/bcicen/jstream" "github.com/mitchellh/mapstructure" - "gitlab.com/tozd/go/errors" ) type packageJSON struct {@@ -70,7 +71,7 @@ infile io.ReadCloser, source *config.Source, log *log.Logger, programsDB *programs.DB, -) (*PackageIngester, errors.E) { +) (*PackageIngester, error) { i := &PackageIngester{ dec: jstream.NewDecoder(infile, source.JSONDepth).EmitKV(), log: log,@@ -89,7 +90,7 @@ }) if err != nil { defer infile.Close() - return nil, errors.WithMessage(err, "could not create mapstructure decoder") + return nil, fault.Wrap(err, fmsg.With("could not create mapstructure decoder")) } i.ms = ms@@ -117,14 +118,14 @@ return l } -func (i *PackageIngester) Process(ctx context.Context) (<-chan nix.Importable, <-chan errors.E) { +func (i *PackageIngester) Process(ctx context.Context) (<-chan nix.Importable, <-chan error) { results := make(chan nix.Importable) - errs := make(chan errors.E) + errs := make(chan error) if i.programs != nil { err := i.programs.Open() if err != nil { - errs <- errors.WithMessage(err, "could not open programs database") + errs <- fault.Wrap(err, fmsg.With("could not open programs database")) i.programs = nil } }@@ -140,7 +141,7 @@ } outer: for mv := range i.dec.Stream() { - var err errors.E + var err error var programs []string select { case <-ctx.Done():@@ -148,12 +149,12 @@ break outer default: } if err := i.dec.Err(); err != nil { - errs <- errors.WithMessage(err, "could not decode JSON") + errs <- fault.Wrap(err, fmsg.With("could not decode JSON")) continue } if mv.ValueType != jstream.Object { - errs <- errors.Errorf("unexpected object type %s", ValueTypeToString(mv.ValueType)) + errs <- fault.Newf("unexpected object type %s", ValueTypeToString(mv.ValueType)) continue }@@ -176,7 +177,7 @@ licenses[i] = makeAdhocLicense(v.String()) case reflect.Map: licenses[i] = *convertToLicense(v.Interface().(map[string]interface{})) default: - errs <- errors.Errorf( + errs <- fault.Newf( "don't know how to handle sublicense of type %s: %v", v.Kind().String(), v,@@ -186,7 +187,7 @@ } case reflect.String: licenses = append(licenses, makeAdhocLicense(v.String())) default: - errs <- errors.Errorf( + errs <- fault.Newf( "don't know how to handle license of type %s: %v", v.Kind().String(), meta["license"],@@ -211,7 +212,7 @@ ps[j] = item.(string) } plats = append(plats, ps...) default: - errs <- errors.Errorf( + errs <- fault.Newf( "don't know how to convert platform type %s: %v", v.Kind().String(), v.Interface(),@@ -228,7 +229,7 @@ meta["homepage"] = []string{v.String()} case reflect.Slice: // already fine default: - errs <- errors.Errorf( + errs <- fault.Newf( "don't know how to interpret homepage type %s'", v.Kind().String(), )@@ -256,7 +257,7 @@ if m["github"] != nil && m["github"].(string) != "" { maints[i].Github = m["github"].(string) } default: - errs <- errors.Errorf( + errs <- fault.Newf( "don't know how to handle maintainer entry of type %s: %v", v.Kind().String(), v,@@ -264,7 +265,7 @@ ) } } default: - errs <- errors.Errorf( + errs <- fault.Newf( "don't know how to interpret maintainers type %s'", maint.Kind().String(), )@@ -274,7 +275,7 @@ } i.pkg = packageJSON{} if err := i.ms.Decode(x); err != nil { // stores in i.pkg - errs <- errors.WithMessagef(err, "failed to decode package %#v", x) + errs <- fault.Wrap(err, fmsg.Withf("failed to decode package %#v", x)) continue }@@ -282,7 +283,7 @@ if i.programs != nil { programs, err = i.programs.GetPackagePrograms(ctx, kv.Key) if err != nil { - errs <- errors.WithMessagef(err, "failed to get programs for package %s", i.pkg.Name) + errs <- fault.Wrap(err, fmsg.Withf("failed to get programs for package %s", i.pkg.Name)) } }@@ -303,7 +304,7 @@ var definition string if i.pkg.Meta.Position != "" { defURL, err := url.Parse(i.pkg.Meta.Position) if err != nil { - errs <- errors.WithMessagef(err, "failed to parse source URL %s", definition) + errs <- fault.Wrap(err, fmsg.Withf("failed to parse source URL %s", definition)) } if defURL.IsAbs() { definition = i.pkg.Meta.Position@@ -311,7 +312,7 @@ } else { subpath, line, _ := strings.Cut(i.pkg.Meta.Position, ":") definition, err = i.source.Repo.GetFileURL(subpath, line) if err != nil { - errs <- errors.WithMessagef(err, "failed to make repo URL for package %s", i.pkg.Name) + errs <- fault.Wrap(err, fmsg.Withf("failed to make repo URL for package %s", i.pkg.Name)) } } }
M internal/importer/utils.go → internal/importer/utils.go
@@ -8,8 +8,9 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/nix" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/bcicen/jstream" - "gitlab.com/tozd/go/errors" ) func ValueTypeToString(valueType jstream.ValueType) string {@@ -33,10 +34,10 @@ return "very strange" } -func MakeChannelLink(repo config.Repository, subPath string) (*nix.Link, errors.E) { +func MakeChannelLink(repo config.Repository, subPath string) (*nix.Link, error) { url, err := repo.GetFileURL(subPath) if err != nil { - return nil, err + return nil, fault.Wrap(err, fmsg.Withf("failed to generate channel link for %s", subPath)) } return &nix.Link{@@ -45,16 +46,14 @@ URL: url, }, nil } -func setRepoRevision(file io.ReadCloser, source *config.Source) errors.E { +func setRepoRevision(file io.ReadCloser, source *config.Source) error { if file != nil { defer file.Close() var str strings.Builder _, err := io.Copy(&str, file) if err != nil { - return errors.WithMessagef( - err, - "unable to read revision file", - ) + return fault.Wrap( + err, fmsg.Withf("unable to read revision file")) } source.Repo.Revision = strings.TrimSpace(str.String())
M internal/index/index_meta.go → internal/index/index_meta.go
@@ -8,7 +8,8 @@ "alin.ovh/searchix/internal/file" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) const CurrentSchemaVersion = 5@@ -30,13 +31,13 @@ log *log.Logger data } -func createMeta(path string, log *log.Logger) (*Meta, errors.E) { +func createMeta(path string, log *log.Logger) (*Meta, error) { exists, err := file.Exists(path) if err != nil { - return nil, errors.WithMessage(err, "could not check for existence of index metadata") + return nil, fault.Wrap(err, fmsg.With("could not check for existence of index metadata")) } if exists { - return nil, errors.New("index metadata already exists") + return nil, fault.New("index metadata already exists") } return &Meta{@@ -48,10 +49,10 @@ }, }, nil } -func openMeta(path string, log *log.Logger) (*Meta, errors.E) { +func openMeta(path string, log *log.Logger) (*Meta, error) { exists, err := file.Exists(path) if err != nil { - return nil, errors.WithMessage(err, "could not check for existence of index metadata") + return nil, fault.Wrap(err, fmsg.With("could not check for existence of index metadata")) } if !exists { return createMeta(path, log)@@ -59,7 +60,7 @@ } j, baseErr := os.ReadFile(path) if baseErr != nil { - return nil, errors.WithMessage(baseErr, "could not open index metadata file") + return nil, fault.Wrap(baseErr, fmsg.With("could not open index metadata file")) } meta := Meta{ path: path,@@ -67,7 +68,7 @@ log: log, } if err := json.Unmarshal(j, &meta.data); err != nil { - return nil, errors.WithMessage(err, "index metadata is corrupt, try replacing the index") + return nil, fault.Wrap(err, fmsg.With("index metadata is corrupt, try replacing the index")) } return &meta, nil@@ -77,16 +78,16 @@ func (i *Meta) IsSchemaOutdated() bool { return i.SchemaVersion < CurrentSchemaVersion } -func (i *Meta) Save() errors.E { +func (i *Meta) Save() error { i.SchemaVersion = CurrentSchemaVersion j, err := json.Marshal(i.data) if err != nil { - return errors.WithMessage(err, "could not prepare index metadata for saving") + return fault.Wrap(err, fmsg.With("could not prepare index metadata for saving")) } i.log.Debug("saving index metadata", "path", i.path) err = os.WriteFile(i.path, j, 0o600) if err != nil { - return errors.WithMessage(err, "could not save index metadata") + return fault.Wrap(err, fmsg.With("could not save index metadata")) } return nil
M internal/index/indexer.go → internal/index/indexer.go
@@ -18,6 +18,8 @@ "alin.ovh/searchix/internal/nix" "alin.ovh/x/log" "go.uber.org/zap" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis" "github.com/blevesearch/bleve/v2/analysis/analyzer/custom"@@ -32,7 +34,6 @@ "github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode" "github.com/blevesearch/bleve/v2/document" "github.com/blevesearch/bleve/v2/mapping" index "github.com/blevesearch/bleve_index_api" - "gitlab.com/tozd/go/errors" ) var idAnalyzer analysis.Analyzer@@ -51,14 +52,10 @@ Meta *Meta } type BatchError struct { - errors.E -} - -func (e *BatchError) Error() string { - return e.E.Error() + error } -func createIndexMapping() (mapping.IndexMapping, errors.E) { +func createIndexMapping() (mapping.IndexMapping, error) { indexMapping := bleve.NewIndexMapping() indexMapping.StoreDynamic = false indexMapping.IndexDynamic = false@@ -78,7 +75,7 @@ "min": 3.0, "max": 25.0, }) if err != nil { - return nil, errors.WithMessage(err, "failed to add ngram token filter") + return nil, fault.Wrap(err, fmsg.With("failed to add ngram token filter")) } err = indexMapping.AddCustomAnalyzer("c_name", map[string]any{@@ -90,7 +87,7 @@ "ngram", }, }) if err != nil { - return nil, errors.WithMessage(err, "could not add custom analyser") + return nil, fault.Wrap(err, fmsg.With("could not add custom analyser")) } err = indexMapping.AddCustomAnalyzer("loc", map[string]any{@@ -102,7 +99,7 @@ porter.Name, }, }) if err != nil { - return nil, errors.WithMessage(err, "could not add custom analyser") + return nil, fault.Wrap(err, fmsg.With("could not add custom analyser")) } err = indexMapping.AddCustomAnalyzer("dotted_keyword", map[string]any{@@ -113,7 +110,7 @@ nixattr.Name, }, }) if err != nil { - return nil, errors.WithMessage(err, "could not add custom analyser") + return nil, fault.Wrap(err, fmsg.With("could not add custom analyser")) } identityFieldMapping := bleve.NewKeywordFieldMapping()@@ -179,7 +176,7 @@ return indexMapping, nil } -func createIndex(indexPath string, options *Options) (bleve.Index, errors.E) { +func createIndex(indexPath string, options *Options) (bleve.Index, error) { indexMapping, err := createIndexMapping() if err != nil { return nil, err@@ -199,7 +196,7 @@ bleve.Config.DefaultKVStore, kvconfig, ) if baseErr != nil { - return nil, errors.WithMessagef(baseErr, "unable to create index at path %s", indexPath) + return nil, fault.Wrap(baseErr, fmsg.Withf("unable to create index at path %s", indexPath)) } return idx, nil@@ -218,16 +215,16 @@ "nixpkgs-programs.db", "manpage-urls.json", } -func deleteIndex(dataRoot string) errors.E { +func deleteIndex(dataRoot string) error { dir, err := os.ReadDir(dataRoot) if err != nil { - return errors.WithMessagef(err, "could not read data directory %s", dataRoot) + return fault.Wrap(err, fmsg.Withf("could not read data directory %s", dataRoot)) } remainingFiles := slices.DeleteFunc(dir, func(e fs.DirEntry) bool { return slices.Contains(expectedDataFiles, e.Name()) }) if len(remainingFiles) > 0 { - return errors.Errorf( + return fault.Newf( "cowardly refusing to remove data directory %s as it contains unknown files: %v", dataRoot, remainingFiles,@@ -236,7 +233,7 @@ } err = os.RemoveAll(dataRoot) if err != nil { - return errors.WithMessagef(err, "could not remove data directory %s", dataRoot) + return fault.Wrap(err, fmsg.Withf("could not remove data directory %s", dataRoot)) } return nil@@ -246,13 +243,13 @@ func OpenOrCreate( dataRoot string, force bool, options *Options, -) (*ReadIndex, *WriteIndex, bool, errors.E) { - var err errors.E +) (*ReadIndex, *WriteIndex, bool, error) { + var err error bleve.SetLog(zap.NewStdLog(options.Logger.Named("bleve").GetLogger())) if !filepath.IsAbs(dataRoot) { wd, err := os.Getwd() if err != nil { - return nil, nil, false, errors.WithMessagef(err, "could not get working directory") + return nil, nil, false, fault.Wrap(err, fmsg.Withf("could not get working directory")) } dataRoot = filepath.Join(wd, dataRoot) }@@ -262,11 +259,10 @@ metaPath := path.Join(dataRoot, metaBaseName) exists, err := file.Exists(indexPath) if err != nil { - return nil, nil, exists, errors.WithMessagef( - err, - "could not check if index exists at path %s", - indexPath, - ) + return nil, nil, exists, fault.Wrap( + err, fmsg.Withf("could not check if index exists at path %s", + indexPath, + )) } var idx bleve.Index@@ -292,7 +288,7 @@ } else { var baseErr error idx, baseErr = bleve.Open(indexPath) if baseErr != nil { - return nil, nil, exists, errors.WithMessagef(baseErr, "could not open index at path %s", indexPath) + return nil, nil, exists, fault.Wrap(baseErr, fmsg.Withf("could not open index at path %s", indexPath)) } meta, err = openMeta(metaPath, options.Logger)@@ -324,16 +320,16 @@ exists, nil } -func (i *WriteIndex) SaveMeta() errors.E { +func (i *WriteIndex) SaveMeta() error { return i.Meta.Save() } func (i *WriteIndex) Import( ctx context.Context, objects <-chan nix.Importable, -) <-chan errors.E { - var err errors.E - errs := make(chan errors.E) +) <-chan error { + var err error + errs := make(chan error) go func() { defer close(errs)@@ -353,7 +349,7 @@ } doc := document.NewDocument(nix.GetKey(obj)) if err := indexMapping.MapDocument(doc, obj); err != nil { - errs <- errors.WithMessagef(err, "could not map document for object: %s", obj.GetName()) + errs <- fault.Wrap(err, fmsg.Withf("could not map document for object: %s", obj.GetName())) continue }@@ -361,7 +357,7 @@ var data bytes.Buffer enc := gob.NewEncoder(&data) if err := enc.Encode(&obj); err != nil { - errs <- errors.WithMessage(err, "could not store object in search index") + errs <- fault.Wrap(err, fmsg.With("could not store object in search index")) continue }@@ -376,7 +372,7 @@ doc.AddField(idField) // log.Debug("adding object to index", "name", opt.Name) if err := batch.IndexAdvanced(doc); err != nil { - errs <- errors.WithMessagef(err, "could not index object %s", obj.GetName()) + errs <- fault.Wrap(err, fmsg.Withf("could not index object %s", obj.GetName())) continue }@@ -400,11 +396,11 @@ return errs } -func (i *WriteIndex) Flush(batch *bleve.Batch) errors.E { +func (i *WriteIndex) Flush(batch *bleve.Batch) error { size := batch.Size() if size == 0 { return &BatchError{ - E: errors.New("no documents to flush"), + fault.New("no documents to flush"), } } i.log.Debug("flushing batch", "size", size)@@ -412,7 +408,7 @@ err := i.index.Batch(batch) if err != nil { return &BatchError{ - E: errors.WithMessagef(err, "could not flush batch"), + fault.Wrap(err, fmsg.Withf("could not flush batch")), } }@@ -421,20 +417,20 @@ return nil } -func (i *WriteIndex) Close() (err errors.E) { +func (i *WriteIndex) Close() (err error) { if e := i.Meta.Save(); e != nil { // index needs to be closed anyway - err = errors.WithMessage(e, "could not save metadata") + err = fault.Wrap(e, fmsg.With("could not save metadata")) } if e := i.index.Close(); e != nil { - err = errors.WithMessagef(e, "could not close index") + err = fault.Wrap(e, fmsg.Withf("could not close index")) } return err } -func (i *WriteIndex) DeleteBySource(source string) errors.E { +func (i *WriteIndex) DeleteBySource(source string) error { query := bleve.NewTermQuery(source) search := bleve.NewSearchRequest(query) search.Size = math.MaxInt@@ -442,7 +438,7 @@ search.Fields = []string{"_id"} results, err := i.index.Search(search) if err != nil { - return errors.WithMessagef(err, "failed to query documents of retired index %s", source) + return fault.Wrap(err, fmsg.Withf("failed to query documents of retired index %s", source)) } batch := i.index.NewBatch()@@ -458,7 +454,7 @@ } } err = i.Flush(batch) if err != nil { - return errors.WithStack(err) + return fault.Wrap(err) } if uint64(search.Size) < results.Total {
M internal/index/search.go → internal/index/search.go
@@ -12,10 +12,11 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/nix" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/search" "github.com/blevesearch/bleve/v2/search/query" - "gitlab.com/tozd/go/errors" ) const DefaultPageSize = 100@@ -40,7 +41,7 @@ func (index *ReadIndex) LastUpdated() time.Time { return index.meta.LastUpdated() } -func (index *ReadIndex) GetEnabledSources() ([]string, errors.E) { +func (index *ReadIndex) GetEnabledSources() ([]string, error) { facet := bleve.NewFacetRequest("Source", 100) query := bleve.NewMatchAllQuery() search := bleve.NewSearchRequest(query)@@ -48,7 +49,7 @@ search.AddFacet("Source", facet) results, err := index.index.Search(search) if err != nil { - return nil, errors.WithMessage(err, "could not get list of enabled sources from index") + return nil, fault.Wrap(err, fmsg.With("could not get list of enabled sources from index")) } enabledSources := make([]string, results.Facets["Source"].Terms.Len())@@ -77,16 +78,16 @@ func (index *ReadIndex) search( ctx context.Context, request *bleve.SearchRequest, -) (*Result, errors.E) { +) (*Result, error) { request.Fields = []string{"_data", "Source"} bleveResult, err := index.index.SearchInContext(ctx, request) select { case <-ctx.Done(): - return nil, errors.WithStack(ctx.Err()) + return nil, fault.Wrap(ctx.Err()) default: if err != nil { - return nil, errors.WithMessage(err, "failed to execute search query") + return nil, fault.Wrap(err, fmsg.With("failed to execute search query")) } hits := func(yield func(DocumentMatch) bool) {@@ -124,7 +125,7 @@ source *config.Source, keyword string, from int, pageSize int, -) (*Result, errors.E) { +) (*Result, error) { query := bleve.NewBooleanQuery() if strings.ContainsAny(keyword, "+-=&|<>!(){}[]^\"~*?:\\/") {@@ -227,7 +228,7 @@ func (index *ReadIndex) GetDocument( ctx context.Context, source *config.Source, id string, -) (*nix.Importable, errors.E) { +) (*nix.Importable, error) { key := nix.MakeKey(source, id) query := bleve.NewDocIDQuery([]string{key}) search := bleve.NewSearchRequest(query)@@ -254,7 +255,7 @@ func (index *ReadIndex) Close() error { err := index.index.Close() if err != nil { - return errors.WithStack(err) + return fault.Wrap(err) } return nil
M internal/index/search_test.go → internal/index/search_test.go
@@ -25,10 +25,10 @@ Logger: log.Named("index"), BatchSize: cfg.Importer.BatchSize, LowMemory: false, }) - defer read.Close() if err != nil { t.Fatal(err) } + defer read.Close() if !exists { t.Fatal("expected index to exist") }@@ -88,10 +88,10 @@ read, _, exists, err := index.OpenOrCreate(dataRoot, false, &index.Options{ Logger: log.Named("index"), LowMemory: false, }) - defer read.Close() if err != nil { t.Fatal(err) } + defer read.Close() if !exists { t.Fatal("expected index to exist") }
M internal/manpages/manpages.go → internal/manpages/manpages.go
@@ -12,7 +12,8 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/fetcher/http" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) const basename = "manpage-urls.json"@@ -35,29 +36,29 @@ func (m *URLMap) Update( ctx context.Context, source *config.Source, -) errors.E { +) error { if !source.Manpages.Enable { - return errors.New("manpages not enabled for this source") + return fault.New("manpages not enabled for this source") } if source.Manpages.Path == "" { - return errors.New("manpages repo source path not configured") + return fault.New("manpages repo source path not configured") } url, err := makeManpageURL(source) if err != nil { - return errors.WithMessage(err, "failed to join manpages URL") + return fault.Wrap(err, fmsg.With("failed to join manpages URL")) } m.logger.Debug("fetching manpages URL map", "url", url) r, mtime, err := http.FetchFileIfNeeded(ctx, m.logger.Named("http"), m.mtime, url) if err != nil { - return errors.WithMessage(err, "failed to fetch manpages") + return fault.Wrap(err, fmsg.With("failed to fetch manpages")) } defer r.Close() if err := m.save(r); err != nil { - return errors.WithMessage(err, "failed to save manpages") + return fault.Wrap(err, fmsg.With("failed to save manpages")) } m.mtime = mtime@@ -66,24 +67,24 @@ return nil } // Open loads the manpage URLs from the JSON file -func (m *URLMap) Open() errors.E { +func (m *URLMap) Open() error { m.logger.Debug("opening manpages file", "path", m.path) stat, err := os.Stat(m.path) if err != nil { - return errors.WithMessagef(err, "failed to stat manpages file: %s", m.path) + return fault.Wrap(err, fmsg.Withf("failed to stat manpages file: %s", m.path)) } data, err := os.ReadFile(m.path) if err != nil { - return errors.WithMessage(err, "failed to read manpages file") + return fault.Wrap(err, fmsg.With("failed to read manpages file")) } m.mtime = stat.ModTime() m.urlMap = make(map[string]string) if err := json.Unmarshal(data, &m.urlMap); err != nil { - return errors.WithMessage(err, "failed to parse manpages JSON") + return fault.Wrap(err, fmsg.With("failed to parse manpages JSON")) } m.logger.Debug("loaded manpages data", "urls", len(m.urlMap))@@ -91,17 +92,17 @@ return nil } -func (m *URLMap) save(r io.Reader) errors.E { +func (m *URLMap) save(r io.Reader) error { m.logger.Debug("saving manpages file", "path", m.path) f, err := os.Create(m.path) if err != nil { - return errors.WithMessage(err, "failed to create manpages file") + return fault.Wrap(err, fmsg.With("failed to create manpages file")) } defer f.Close() if _, err := io.Copy(f, r); err != nil { - return errors.WithMessage(err, "failed to write manpages file") + return fault.Wrap(err, fmsg.With("failed to write manpages file")) } return nil@@ -119,10 +120,10 @@ return url, true } -func makeManpageURL(source *config.Source) (string, errors.E) { +func makeManpageURL(source *config.Source) (string, error) { url, err := source.Repo.GetRawFileURL(source.Manpages.Path) if err != nil { - return "", errors.WithMessage(err, "failed to join manpage URL") + return "", fault.Wrap(err, fmsg.With("failed to join manpage URL")) } return url, nil
M internal/nix/option.go → internal/nix/option.go
@@ -3,8 +3,8 @@ import ( "io" + "github.com/Southclaws/fault" "github.com/yuin/goldmark" - "gitlab.com/tozd/go/errors" "alin.ovh/searchix/internal/nixdocs" )@@ -51,5 +51,5 @@ ) // implements gomponent.Node func (text Markdown) Render(w io.Writer) error { - return errors.WithStack(md.Convert([]byte(text), w)) + return fault.Wrap(md.Convert([]byte(text), w)) }
M internal/programs/programs.go → internal/programs/programs.go
@@ -9,7 +9,8 @@ "strings" "alin.ovh/searchix/internal/config" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" _ "modernc.org/sqlite" //nolint:blank-imports // sqlite driver needed for database/sql )@@ -26,7 +27,7 @@ func Instantiate( ctx context.Context, source *config.Source, logger *log.Logger, -) (*DB, errors.E) { +) (*DB, error) { // nix-instantiate --eval --json -I nixpkgs=channel:nixos-unstable --expr 'toString <nixpkgs/programs.sqlite>' args := []string{ "--eval",@@ -39,7 +40,7 @@ logger.Debug("nix-instantiate command", "args", args) cmd := exec.CommandContext(ctx, "nix-instantiate", args...) out, err := cmd.Output() if err != nil { - return nil, errors.WithMessage(err, "failed to run nix-instantiate") + return nil, fault.Wrap(err, fmsg.With("failed to run nix-instantiate")) } outPath := strings.Trim(strings.TrimSpace(string(out)), "\"")@@ -53,17 +54,17 @@ logger: logger, }, nil } -func (p *DB) Open() errors.E { +func (p *DB) Open() error { var err error p.db, err = sql.Open("sqlite", p.Path) if err != nil { - return errors.WithMessage(err, "failed to open sqlite database") + return fault.Wrap(err, fmsg.With("failed to open sqlite database")) } p.logger.Debug("opened sqlite database") _, err = p.db.Exec("ATTACH DATABASE ':memory:' AS mem") if err != nil { - return errors.WithMessage(err, "failed to attach in-memory database") + return fault.Wrap(err, fmsg.With("failed to attach in-memory database")) } _, err = p.db.Exec(`@@ -73,13 +74,13 @@ FROM main.Programs GROUP BY name, package `) if err != nil { - return errors.WithMessage(err, "failed to create programs table") + return fault.Wrap(err, fmsg.With("failed to create programs table")) } p.logger.Debug("created programs table") _, err = p.db.Exec(`CREATE INDEX mem.idx_package ON programs(package)`) if err != nil { - return errors.WithMessage(err, "failed to create idx_package index") + return fault.Wrap(err, fmsg.With("failed to create idx_package index")) } p.logger.Debug("created idx_package index")@@ -89,43 +90,43 @@ FROM mem.programs WHERE package = ? `) if err != nil { - return errors.WithMessage(err, "failed to prepare statement") + return fault.Wrap(err, fmsg.With("failed to prepare statement")) } p.logger.Debug("prepared statement") return nil } -func (p *DB) Close() errors.E { +func (p *DB) Close() error { if err := p.db.Close(); err != nil { - return errors.WithMessage(err, "failed to close sqlite database") + return fault.Wrap(err, fmsg.With("failed to close sqlite database")) } return nil } -func (p *DB) GetPackagePrograms(ctx context.Context, pkg string) ([]string, errors.E) { +func (p *DB) GetPackagePrograms(ctx context.Context, pkg string) ([]string, error) { var programs []string if p.db == nil { - return nil, errors.New("database not open") + return nil, fault.New("database not open") } rows, err := p.stmt.QueryContext(ctx, pkg) if err != nil { - return nil, errors.WithMessage(err, "failed to execute query") + return nil, fault.Wrap(err, fmsg.With("failed to execute query")) } defer rows.Close() for rows.Next() { var name string if err := rows.Scan(&name); err != nil { - return nil, errors.WithMessage(err, "failed to scan row") + return nil, fault.Wrap(err, fmsg.With("failed to scan row")) } programs = append(programs, name) } rerr := rows.Close() if rerr != nil { - return nil, errors.WithMessage(rerr, "sql error") + return nil, fault.Wrap(rerr, fmsg.With("sql error")) } return programs, nil
M internal/server/dev.go → internal/server/dev.go
@@ -8,8 +8,9 @@ "path/filepath" "time" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/fsnotify/fsnotify" - "gitlab.com/tozd/go/errors" ) type FileWatcher struct {@@ -17,10 +18,10 @@ watcher *fsnotify.Watcher log *log.Logger } -func NewFileWatcher(log *log.Logger) (*FileWatcher, errors.E) { +func NewFileWatcher(log *log.Logger) (*FileWatcher, error) { watcher, err := fsnotify.NewWatcher() if err != nil { - return nil, errors.WithMessage(err, "could not create watcher") + return nil, fault.Wrap(err, fmsg.With("could not create watcher")) } return &FileWatcher{@@ -29,23 +30,23 @@ log, }, nil } -func (i FileWatcher) AddRecursive(from string) errors.E { +func (i FileWatcher) AddRecursive(from string) error { i.log.Debug(fmt.Sprintf("watching files under %s", from)) err := filepath.WalkDir(from, func(path string, entry fs.DirEntry, err error) error { if err != nil { - return errors.WithMessagef(err, "could not walk directory %s", path) + return fault.Wrap(err, fmsg.Withf("could not walk directory %s", path)) } if entry.IsDir() { i.log.Debug(fmt.Sprintf("adding directory %s to watcher", path)) if err = i.watcher.Add(path); err != nil { - return errors.WithMessagef(err, "could not add directory %s to watcher", path) + return fault.Wrap(err, fmsg.Withf("could not add directory %s to watcher", path)) } } return nil }) - return errors.WithMessage(err, "error walking directory tree") + return fault.Wrap(err, fmsg.With("error walking directory tree")) } func (i FileWatcher) Start(callback func(string)) {
M internal/server/logging.go → internal/server/logging.go
@@ -4,7 +4,8 @@ import ( "net/http" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) type LoggingResponseWriter struct {@@ -29,7 +30,7 @@ } count, err := lrw.ResponseWriter.Write(b) if err != nil { - return count, errors.Wrap(err, "failed to write response") + return count, fault.Wrap(err, fmsg.With("failed to write response")) } return count, nil
M internal/server/mux.go → internal/server/mux.go
@@ -24,8 +24,9 @@ "alin.ovh/searchix/internal/pagination" "alin.ovh/searchix/internal/sentryhttp" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/osdevisnot/sorvor/pkg/livereload" - "gitlab.com/tozd/go/errors" ) type HTTPError struct {@@ -61,12 +62,12 @@ cfg *config.Config, options *Options, log *log.Logger, liveReload bool, -) (*http.ServeMux, errors.E) { +) (*http.ServeMux, error) { if cfg == nil { - return nil, errors.New("cfg is nil") + return nil, fault.New("cfg is nil") } if options.ReadIndex == nil { - return nil, errors.New("read index is nil") + return nil, fault.New("read index is nil") } index := options.ReadIndex sortSources(cfg.Importer.Sources)@@ -361,7 +362,7 @@ mdb := options.ManpagesURLMap err := mdb.Open() if err != nil { - return nil, errors.WithMessage(err, "failed to open manpages URL map") + return nil, fault.Wrap(err, fmsg.With("failed to open manpages URL map")) } mux.HandleFunc("/man/{section}/{page}", func(w http.ResponseWriter, r *http.Request) { section := r.PathValue("section")@@ -385,11 +386,11 @@ liveReload.Start() top.Handle("/livereload", liveReload) fw, err := NewFileWatcher(log.Named("watcher")) if err != nil { - return nil, errors.WithMessage(err, "could not create file watcher") + return nil, fault.Wrap(err, fmsg.With("could not create file watcher")) } err = fw.AddRecursive(path.Join("frontend")) if err != nil { - return nil, errors.WithMessage(err, "could not add directory to file watcher") + return nil, fault.Wrap(err, fmsg.With("could not add directory to file watcher")) } go fw.Start(func(filename string) { log.Debug(fmt.Sprintf("got filename %s", filename))
M internal/server/server.go → internal/server/server.go
@@ -12,7 +12,8 @@ "alin.ovh/searchix/internal/index" "alin.ovh/searchix/internal/manpages" "alin.ovh/x/log" - "gitlab.com/tozd/go/errors" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" )@@ -36,7 +37,7 @@ conf *config.Config, options *Options, log *log.Logger, liveReload bool, -) (*Server, errors.E) { +) (*Server, error) { mux, err := NewMux(conf, options, log, liveReload) if err != nil { return nil, err@@ -57,16 +58,15 @@ }, }, nil } -func (s *Server) Start() errors.E { +func (s *Server) Start() error { listenAddress := net.JoinHostPort(s.cfg.Web.ListenAddress, strconv.Itoa(s.cfg.Web.Port)) l, err := net.Listen("tcp", listenAddress) if err != nil { - return errors.WithMessagef( - err, - "could not listen on address %s and port %d", - s.cfg.Web.ListenAddress, - s.cfg.Web.Port, - ) + return fault.Wrap( + err, fmsg.Withf("could not listen on address %s and port %d", + s.cfg.Web.ListenAddress, + s.cfg.Web.Port, + )) } s.listener = l@@ -79,7 +79,7 @@ ) } if err := s.server.Serve(l); err != nil && err != http.ErrServerClosed { - return errors.WithMessage(err, "could not start server") + return fault.Wrap(err, fmsg.With("could not start server")) } return nil
M web/searchix.go → web/searchix.go
@@ -7,8 +7,9 @@ "alin.ovh/searchix/internal/config" "alin.ovh/searchix/internal/server" "alin.ovh/x/log" + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" "github.com/getsentry/sentry-go" - "gitlab.com/tozd/go/errors" ) type Server struct {@@ -19,7 +20,7 @@ sentryHub *sentry.Hub options *server.Options } -func New(cfg *config.Config, log *log.Logger, options *server.Options) (*Server, errors.E) { +func New(cfg *config.Config, log *log.Logger, options *server.Options) (*Server, error) { err := sentry.Init(sentry.ClientOptions{ EnableTracing: true, TracesSampleRate: 1.0,@@ -38,16 +39,16 @@ options: options, }, nil } -func (s *Server) Start(liveReload bool) errors.E { - var err errors.E +func (s *Server) Start(liveReload bool) error { + var err error s.sv, err = server.New(s.cfg, s.options, s.log.Named("server"), liveReload) if err != nil { - return errors.Wrap(err, "error setting up server") + return fault.Wrap(err, fmsg.With("error setting up server")) } err = s.sv.Start() if err != nil { - return errors.Wrap(err, "error starting server") + return fault.Wrap(err, fmsg.With("error starting server")) } return nil