Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions cfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import (
"fmt"
"sort"

"github.com/gofoji/foji/errs"
"github.com/gofoji/foji/stringlist"
)

type (
Error string
FileHandler func(string) error // Used for post generation processing
ParamMap map[string]any // Generic bucket of params passed to templates
Processes map[string]Process
Expand Down Expand Up @@ -100,7 +100,7 @@ func (pp Processes) String() string {
}

const (
errProcess = Error("Process")
errProcess = errs.Error("Process")
missingBundleFormat = "%w '%s' referenced by bundle `%s` not found. Possible options: %s"
missingFormat = "%w '%s' not found. Possible options: %s"
)
Expand Down Expand Up @@ -176,7 +176,3 @@ func (o Output) All() stringlist.StringMap {
func (f FileInput) IsEmpty() bool {
return len(f.Files) == 0
}

func (e Error) Error() string {
return string(e)
}
2 changes: 1 addition & 1 deletion cfg/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
func Version() string {
info, ok := debug.ReadBuildInfo()
if !ok {
panic("Failed to read build info")
return "(dev build)"
}

if info.Main.Version != "" {
Expand Down
27 changes: 2 additions & 25 deletions cmd/copy_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/rs/zerolog"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -42,12 +40,12 @@ func writeTemplate(l zerolog.Logger, dir, filename string, useStdout, overwrite
}

if dir != "" {
filename = changeDirectory(dir, "foji", filename)
filename = ChangeDirectory(dir, "foji", filename)
}

l = l.With().Str("template", filename).Logger()

if useStdout || overwrite || !fileExists(filename) {
if useStdout || overwrite || !FileExists(filename) {
l.Debug().Msg("Writing")

err = WriteToFile(b, filename)
Expand All @@ -60,24 +58,3 @@ func writeTemplate(l zerolog.Logger, dir, filename string, useStdout, overwrite

return nil
}

func fileExists(filename string) bool {
fileInfo, err := os.Stat(filename)

return err == nil && fileInfo.Mode().IsRegular()
}

func changeDirectory(dir, swapDir, filename string) string {
path := strings.Split(filename, string(os.PathSeparator))
if len(path) == 0 {
return filename
}

if path[0] == swapDir {
path[0] = dir
} else {
path = append([]string{dir}, path...)
}

return filepath.Join(path...)
}
65 changes: 65 additions & 0 deletions cmd/fileutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cmd

import (
"fmt"
"os"
"path/filepath"
"strings"
)

// File permission constants
const (
DirPerm = 0o700 // rwx------
FilePerm = 0o600 // rw-------
)

// WriteToFile writes data to a file, creating directories as needed.
// It creates parent directories with DirPerm and the file with FilePerm.
func WriteToFile(source []byte, file string) error {
err := os.MkdirAll(filepath.Dir(file), DirPerm)
if err != nil {
return fmt.Errorf("create output directory:%w", err)
}

f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, FilePerm)
if err != nil {
return fmt.Errorf("open file:%w", err)
}

_, err = f.Write(source)

closeErr := f.Close()
if closeErr != nil {
return fmt.Errorf("closing file:%w", closeErr)
}

if err != nil {
return fmt.Errorf("writing file:%w", err)
}

return nil
}

// FileExists checks if a file exists and is a regular file (not a directory).
func FileExists(filename string) bool {
fileInfo, err := os.Stat(filename)
return err == nil && fileInfo.Mode().IsRegular()
}

// ChangeDirectory modifies a file path by changing its directory prefix.
// If the filename starts with swapDir, it replaces it with dir.
// Otherwise, it prepends dir to the path.
func ChangeDirectory(dir, swapDir, filename string) string {
path := strings.Split(filename, string(os.PathSeparator))
if len(path) == 0 {
return filename
}

if path[0] == swapDir {
path[0] = dir
} else {
path = append([]string{dir}, path...)
}

return filepath.Join(path...)
}
32 changes: 0 additions & 32 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package cmd

import (
"fmt"
"os"
"path/filepath"

"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -54,33 +52,3 @@ func writeConfig() {

l.Info().Msg("wrote sample foji config file")
}

const (
permRWXUser = 0o700
permRWUser = 0o600
)

func WriteToFile(source []byte, file string) error {
err := os.MkdirAll(filepath.Dir(file), permRWXUser)
if err != nil {
return fmt.Errorf("create output directory:%w", err)
}

f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, permRWUser)
if err != nil {
return fmt.Errorf("open file:%w", err)
}

_, err = f.Write(source)

closeErr := f.Close()
if closeErr != nil {
return fmt.Errorf("closing file:%w", closeErr)
}

if err != nil {
return fmt.Errorf("writing file:%w", err)
}

return nil
}
33 changes: 33 additions & 0 deletions errs/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Package errs provides unified error types and constants used throughout foji.
package errs

// Error is a simple error string type used for custom error definitions.
type Error string

// Error implements the error interface for Error type.
func (e Error) Error() string {
return string(e)
}

const (
// ErrRuntime indicates an error occurred in template runtime functions.
ErrRuntime = Error("runtime")

// ErrWeld indicates an error occurred during the welding process.
ErrWeld = Error("welding error")

// ErrMissingRequirement indicates a required condition was not met.
ErrMissingRequirement = Error("requires")

// ErrNotNeeded indicates the output generation should be skipped.
ErrNotNeeded = Error("not needed")

// ErrPermExists indicates a permanent file (prefixed with !) already exists.
ErrPermExists = Error("file exists")

// ErrInvalidDictParams indicates invalid parameters in WithParams call.
ErrInvalidDictParams = Error("invalid dict params in call to WithParams, must be key and value pairs")

// ErrInvalidDictKey indicates an invalid dictionary key in WithParams call.
ErrInvalidDictKey = Error("invalid dict params in call to WithParams, must be key and value pairs")
)
2 changes: 1 addition & 1 deletion foji/openapi/model.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (e {{ $enumType }}) Value() (driver.Value, error) {
return json.Marshal(e.String())
}

func (e *{{ $enumType }}) Scan(src interface{}) error {
func (e *{{ $enumType }}) Scan(src any) error {
s, ok := src.(string)
if !ok {
return fmt.Errorf("{{ $enumType }}.scan: scanned a %T, not []byte", src) //nolint
Expand Down
2 changes: 1 addition & 1 deletion input/openapi/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func Parse(_ context.Context, logger zerolog.Logger, inGroups []input.FileGroup)

swagger, err := loader.LoadFromData(f.Content)
if err != nil {
panic(err)
return nil, err
}

d := File{Input: f, API: swagger}
Expand Down
22 changes: 13 additions & 9 deletions output/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/rs/zerolog"

"github.com/gofoji/foji/cfg"
"github.com/gofoji/foji/errs"
"github.com/gofoji/foji/runtime"
"github.com/gofoji/foji/stringlist"
)
Expand All @@ -26,6 +27,14 @@ type Context struct {
AbortError error
}

// NewContext creates a new base context with the given process and logger.
func NewContext(p cfg.Process, l zerolog.Logger) Context {
return Context{
Process: p,
Logger: l,
}
}

// Funcs defaults the default case funcs based on the Process.Case.
func (c *Context) Funcs() plates.FuncMap {
return runtime.CaseFuncs(c.Case)
Expand All @@ -39,7 +48,7 @@ func (c *Context) Aborted() error {
// NotNeededIf given bool is true the execution is aborted, and can be used to prevent generation of a file.
func (c *Context) NotNeededIf(t bool, reason string) (string, error) {
if t {
c.AbortError = fmt.Errorf("%w: %s", ErrNotNeeded, reason)
c.AbortError = fmt.Errorf("%w: %s", errs.ErrNotNeeded, reason)

return "", c.AbortError
}
Expand All @@ -50,24 +59,19 @@ func (c *Context) NotNeededIf(t bool, reason string) (string, error) {
// ErrorIf if given bool is true the execution is fatally aborted, and stops processing.
func (c *Context) ErrorIf(t bool, reason string) (string, error) {
if t {
c.AbortError = fmt.Errorf("%w: %s", ErrMissingRequirement, reason)
c.AbortError = fmt.Errorf("%w: %s", errs.ErrMissingRequirement, reason)

return "", c.AbortError
}

return "", nil
}

const (
ErrInvalidDictParams = Error("invalid dict params in call to WithParams, must be key and value pairs")
ErrInvalidDictKey = Error("invalid dict params in call to WithParams, must be key and value pairs")
)

// WithParams Clones the current context and adds runtime params for each pair of key, value provided.
// Used for executing sub templates that still need access to the context.
func (c *Context) WithParams(values ...any) (*Context, error) {
if len(values)%2 != 0 {
return nil, ErrInvalidDictParams
return nil, errs.ErrInvalidDictParams
}

out := *c
Expand All @@ -76,7 +80,7 @@ func (c *Context) WithParams(values ...any) (*Context, error) {
for i := 0; i < len(values); i += 2 {
key, ok := values[i].(string)
if !ok {
return nil, ErrInvalidDictKey
return nil, errs.ErrInvalidDictKey
}

out.RuntimeParams[key] = values[i+1]
Expand Down
26 changes: 2 additions & 24 deletions output/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func HasDBOutput(o cfg.Output) bool {

func DB(p cfg.Process, fn cfg.FileHandler, logger zerolog.Logger, schemas db.DB, simulate bool) error {
ctx := SchemasContext{
Context: Context{Process: p, Logger: logger},
Context: NewContext(p, logger),
DB: schemas,
}

Expand Down Expand Up @@ -139,29 +139,7 @@ func (s *SchemasContext) Parameterize(cc db.Columns, format, pkg string) string
}

func (s SchemasContext) GetType(c *db.Column, pkg string) string {
pp := strings.Split(c.Path(), ".")
for i := range pp {
p := strings.Join(pp[i:], ".")

t, ok := s.Maps.Type["."+p]
if ok {
return s.CheckPackage(t, pkg)
}
}

if c.Nullable {
t, ok := s.Maps.Nullable[c.Type]
if ok {
return s.CheckPackage(t, pkg)
}
}

t, ok := s.Maps.Type[c.Type]
if ok {
return s.CheckPackage(t, pkg)
}

return fmt.Sprintf("UNKNOWN:path(%s):type(%s)", c.Path(), c.Type)
return ResolveType(s.Maps, func(t string) string { return s.CheckPackage(t, pkg) }, c.Type, c.Nullable, c.Path())
}

const ValidTypeElems = 2
Expand Down
Loading