package moose
import (
"errors"
"fmt"
"html/template"
"math"
"os"
"path/filepath"
"time"
)
const timestampFormat = "20060102150405"
type tmplVars struct {
Version string
CamelName string
Package string
}
const jsonVersionTemplate = "%05v"
func CreateWithTemplate(dir string, tmpl *template.Template, name string, migrationType MigrationType, sequential bool) (string, error) {
version := time.Now().UTC().Format(timestampFormat)
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return "", err
}
}
if sequential {
// always use osFS here because it's modifying operation
migrations, err := collect(osFS{}, dir, math.MinInt64, math.MaxInt64, registeredGoMigrations)
if err != nil && !errors.Is(err, ErrNoMigrationFiles) {
return "", err
}
vMigrations, err := Migrations(migrations).versioned(timestampFormat)
if err != nil {
return "", err
}
if last, err := vMigrations.Last(); err == nil {
version = fmt.Sprintf(jsonVersionTemplate, last.Version+1)
} else {
version = fmt.Sprintf(jsonVersionTemplate, int64(1))
}
}
filename := fmt.Sprintf("%v_%v.%v", version, snakeCase(name), migrationType)
if tmpl == nil {
switch migrationType {
case MigrationTypeGo:
tmpl = goMigrationTemplate
case MigrationTypeJSON:
tmpl = jsonMigrationTemplate
default:
return "", fmt.Errorf("unsupported migration type %q, use go or json", migrationType)
}
}
path := filepath.Join(dir, filename)
if _, err := os.Stat(path); !os.IsNotExist(err) {
return "", fmt.Errorf("failed to create migration file: %w", err)
}
f, err := os.Create(path)
if err != nil {
return "", fmt.Errorf("failed to create migration file: %w", err)
}
defer func() { _ = f.Close() }()
abs, err := filepath.Abs(path)
if err != nil {
return "", fmt.Errorf("failed to create migration file: %w", err)
}
vars := tmplVars{
Version: version,
CamelName: camelCase(name),
Package: filepath.Base(filepath.Dir(abs)),
}
if err := tmpl.Execute(f, vars); err != nil {
return "", fmt.Errorf("failed to execute tmpl: %w", err)
}
return f.Name(), nil
}
// Create writes a new blank migration file.
func Create(dir, name string, migrationType MigrationType, sequential bool) (string, error) {
return CreateWithTemplate(dir, nil, name, migrationType, sequential)
}
var jsonMigrationTemplate = template.Must(template.New("moose.json-migration").Parse(`{
"up":{ "tx": true, "cmds": [] },
"down": { "tx": true, "cmds": [] }
}
`))
var goMigrationTemplate = template.Must(template.New("moose.go-migration").Parse(`package {{.Package}}
import (
"context"
"tangled.org/softprops.tngl.sh/moose"
"go.mongodb.org/mongo-driver/v2/mongo"
)
func init() {
moose.AddMigration(up{{.CamelName}}, down{{.CamelName}})
}
func up{{.CamelName}}(ctx context.Context, client *mongo.Database) error {
// This code is executed when the migration is applied.
return nil
}
func down{{.CamelName}}(ctx context.Context, client *mongo.Database) error {
// This code is executed when the migration is rolled back.
return nil
}
`))