refactor(api): replace fmt.Errorf and os.* with coreerr.E and coreio.Local

Replace all fmt.Errorf/errors.New in production code with coreerr.E() from
go-log. Replace os.MkdirAll with coreio.Local.EnsureDir and os.Remove with
coreio.Local.Delete. Promote go-io and go-log to direct dependencies in go.mod.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-16 21:33:44 +00:00
parent 5c1f438a48
commit d510af404d
4 changed files with 29 additions and 21 deletions

View file

@ -10,6 +10,9 @@ import (
"forge.lthn.ai/core/cli/pkg/cli"
coreio "forge.lthn.ai/core/go-io"
coreerr "forge.lthn.ai/core/go-log"
goapi "forge.lthn.ai/core/api"
)
@ -23,7 +26,7 @@ func addSDKCommand(parent *cli.Command) {
cmd := cli.NewCommand("sdk", "Generate client SDKs from OpenAPI spec", "", func(cmd *cli.Command, args []string) error {
if lang == "" {
return fmt.Errorf("--lang is required. Supported: %s", strings.Join(goapi.SupportedLanguages(), ", "))
return coreerr.E("sdk.Generate", "--lang is required. Supported: "+strings.Join(goapi.SupportedLanguages(), ", "), nil)
}
// If no spec file provided, generate one to a temp file.
@ -39,13 +42,13 @@ func addSDKCommand(parent *cli.Command) {
tmpFile, err := os.CreateTemp("", "openapi-*.json")
if err != nil {
return fmt.Errorf("create temp spec file: %w", err)
return coreerr.E("sdk.Generate", "create temp spec file", err)
}
defer os.Remove(tmpFile.Name())
defer coreio.Local.Delete(tmpFile.Name())
if err := goapi.ExportSpec(tmpFile, "json", builder, groups); err != nil {
tmpFile.Close()
return fmt.Errorf("generate spec: %w", err)
return coreerr.E("sdk.Generate", "generate spec", err)
}
tmpFile.Close()
specFile = tmpFile.Name()
@ -61,7 +64,7 @@ func addSDKCommand(parent *cli.Command) {
fmt.Fprintln(os.Stderr, "openapi-generator-cli not found. Install with:")
fmt.Fprintln(os.Stderr, " brew install openapi-generator (macOS)")
fmt.Fprintln(os.Stderr, " npm install @openapitools/openapi-generator-cli -g")
return fmt.Errorf("openapi-generator-cli not installed")
return coreerr.E("sdk.Generate", "openapi-generator-cli not installed", nil)
}
// Generate for each language.
@ -72,7 +75,7 @@ func addSDKCommand(parent *cli.Command) {
}
fmt.Fprintf(os.Stderr, "Generating %s SDK...\n", l)
if err := gen.Generate(context.Background(), l); err != nil {
return fmt.Errorf("generate %s: %w", l, err)
return coreerr.E("sdk.Generate", "generate "+l, err)
}
fmt.Fprintf(os.Stderr, " Done: %s/%s/\n", output, l)
}

View file

@ -11,6 +11,9 @@ import (
"os/exec"
"path/filepath"
"slices"
coreio "forge.lthn.ai/core/go-io"
coreerr "forge.lthn.ai/core/go-log"
)
// Supported SDK target languages.
@ -45,16 +48,16 @@ type SDKGenerator struct {
func (g *SDKGenerator) Generate(ctx context.Context, language string) error {
generator, ok := supportedLanguages[language]
if !ok {
return fmt.Errorf("unsupported language %q: supported languages are %v", language, SupportedLanguages())
return coreerr.E("SDKGenerator.Generate", fmt.Sprintf("unsupported language %q: supported languages are %v", language, SupportedLanguages()), nil)
}
if _, err := os.Stat(g.SpecPath); os.IsNotExist(err) {
return fmt.Errorf("spec file not found: %s", g.SpecPath)
return coreerr.E("SDKGenerator.Generate", "spec file not found: "+g.SpecPath, nil)
}
outputDir := filepath.Join(g.OutputDir, language)
if err := os.MkdirAll(outputDir, 0o755); err != nil {
return fmt.Errorf("create output directory: %w", err)
if err := coreio.Local.EnsureDir(outputDir); err != nil {
return coreerr.E("SDKGenerator.Generate", "create output directory", err)
}
args := g.buildArgs(generator, outputDir)
@ -63,7 +66,7 @@ func (g *SDKGenerator) Generate(ctx context.Context, language string) error {
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("openapi-generator-cli failed for %s: %w", language, err)
return coreerr.E("SDKGenerator.Generate", "openapi-generator-cli failed for "+language, err)
}
return nil

View file

@ -4,12 +4,14 @@ package api
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
coreio "forge.lthn.ai/core/go-io"
coreerr "forge.lthn.ai/core/go-log"
)
// ExportSpec generates the OpenAPI spec and writes it to w.
@ -17,7 +19,7 @@ import (
func ExportSpec(w io.Writer, format string, builder *SpecBuilder, groups []RouteGroup) error {
data, err := builder.Build(groups)
if err != nil {
return fmt.Errorf("build spec: %w", err)
return coreerr.E("ExportSpec", "build spec", err)
}
switch format {
@ -28,28 +30,28 @@ func ExportSpec(w io.Writer, format string, builder *SpecBuilder, groups []Route
// Unmarshal JSON then re-marshal as YAML.
var obj any
if err := json.Unmarshal(data, &obj); err != nil {
return fmt.Errorf("unmarshal spec: %w", err)
return coreerr.E("ExportSpec", "unmarshal spec", err)
}
enc := yaml.NewEncoder(w)
enc.SetIndent(2)
if err := enc.Encode(obj); err != nil {
return fmt.Errorf("encode yaml: %w", err)
return coreerr.E("ExportSpec", "encode yaml", err)
}
return enc.Close()
default:
return fmt.Errorf("unsupported format %q: use \"json\" or \"yaml\"", format)
return coreerr.E("ExportSpec", "unsupported format "+format+": use \"json\" or \"yaml\"", nil)
}
}
// ExportSpecToFile writes the spec to the given path.
// The parent directory is created if it does not exist.
func ExportSpecToFile(path, format string, builder *SpecBuilder, groups []RouteGroup) error {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return fmt.Errorf("create directory: %w", err)
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
return coreerr.E("ExportSpecToFile", "create directory", err)
}
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("create file: %w", err)
return coreerr.E("ExportSpecToFile", "create file", err)
}
defer f.Close()
return ExportSpec(f, format, builder, groups)

4
go.mod
View file

@ -4,6 +4,8 @@ go 1.26.0
require (
forge.lthn.ai/core/cli v0.3.1
forge.lthn.ai/core/go-io v0.1.2
forge.lthn.ai/core/go-log v0.0.4
github.com/99designs/gqlgen v0.17.88
github.com/andybalholm/brotli v1.2.0
github.com/casbin/casbin/v2 v2.135.0
@ -40,8 +42,6 @@ require (
forge.lthn.ai/core/go-crypt v0.1.7 // indirect
forge.lthn.ai/core/go-i18n v0.1.4 // indirect
forge.lthn.ai/core/go-inference v0.1.4 // indirect
forge.lthn.ai/core/go-io v0.1.2 // indirect
forge.lthn.ai/core/go-log v0.0.4 // indirect
forge.lthn.ai/core/go-process v0.2.3 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/ProtonMail/go-crypto v1.4.0 // indirect