Compare commits
1 Commits
v1.0.38-2-
...
v1.0.40
| Author | SHA1 | Date | |
|---|---|---|---|
| dc9172cc7c |
@@ -15,6 +15,7 @@ var (
|
|||||||
templSourceType string
|
templSourceType string
|
||||||
templSourcePath string
|
templSourcePath string
|
||||||
templSourceConn string
|
templSourceConn string
|
||||||
|
templFromList []string
|
||||||
templTemplatePath string
|
templTemplatePath string
|
||||||
templOutputPath string
|
templOutputPath string
|
||||||
templSchemaFilter string
|
templSchemaFilter string
|
||||||
@@ -78,8 +79,9 @@ Examples:
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
templCmd.Flags().StringVar(&templSourceType, "from", "", "Source format (dbml, pgsql, json, etc.)")
|
templCmd.Flags().StringVar(&templSourceType, "from", "", "Source format (dbml, pgsql, json, etc.)")
|
||||||
templCmd.Flags().StringVar(&templSourcePath, "from-path", "", "Source file path (for file-based sources)")
|
templCmd.Flags().StringVar(&templSourcePath, "from-path", "", "Source file path (for file-based sources, mutually exclusive with --from-list)")
|
||||||
templCmd.Flags().StringVar(&templSourceConn, "from-conn", "", "Source connection string (for database sources)")
|
templCmd.Flags().StringVar(&templSourceConn, "from-conn", "", "Source connection string (for database sources)")
|
||||||
|
templCmd.Flags().StringSliceVar(&templFromList, "from-list", nil, "Comma-separated list of source file paths to read and merge (mutually exclusive with --from-path)")
|
||||||
templCmd.Flags().StringVar(&templTemplatePath, "template", "", "Template file path (required)")
|
templCmd.Flags().StringVar(&templTemplatePath, "template", "", "Template file path (required)")
|
||||||
templCmd.Flags().StringVar(&templOutputPath, "output", "", "Output path (file or directory, empty for stdout)")
|
templCmd.Flags().StringVar(&templOutputPath, "output", "", "Output path (file or directory, empty for stdout)")
|
||||||
templCmd.Flags().StringVar(&templSchemaFilter, "schema", "", "Filter to specific schema")
|
templCmd.Flags().StringVar(&templSchemaFilter, "schema", "", "Filter to specific schema")
|
||||||
@@ -95,9 +97,20 @@ func runTempl(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Fprintf(os.Stderr, "=== RelSpec Template Execution ===\n")
|
fmt.Fprintf(os.Stderr, "=== RelSpec Template Execution ===\n")
|
||||||
fmt.Fprintf(os.Stderr, "Started at: %s\n\n", getCurrentTimestamp())
|
fmt.Fprintf(os.Stderr, "Started at: %s\n\n", getCurrentTimestamp())
|
||||||
|
|
||||||
|
// Validate mutually exclusive flags
|
||||||
|
if templSourcePath != "" && len(templFromList) > 0 {
|
||||||
|
return fmt.Errorf("--from-path and --from-list are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
// Read database using the same function as convert
|
// Read database using the same function as convert
|
||||||
fmt.Fprintf(os.Stderr, "Reading from %s...\n", templSourceType)
|
fmt.Fprintf(os.Stderr, "Reading from %s...\n", templSourceType)
|
||||||
db, err := readDatabaseForConvert(templSourceType, templSourcePath, templSourceConn)
|
var db *models.Database
|
||||||
|
var err error
|
||||||
|
if len(templFromList) > 0 {
|
||||||
|
db, err = readDatabaseListForConvert(templSourceType, templFromList)
|
||||||
|
} else {
|
||||||
|
db, err = readDatabaseForConvert(templSourceType, templSourcePath, templSourceConn)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read source: %w", err)
|
return fmt.Errorf("failed to read source: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
134
cmd/relspec/templ_from_list_test.go
Normal file
134
cmd/relspec/templ_from_list_test.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// writeTestTemplate writes a minimal Go text template file.
|
||||||
|
func writeTestTemplate(t *testing.T, path string) {
|
||||||
|
t.Helper()
|
||||||
|
content := []byte(`{{.Name}}`)
|
||||||
|
if err := os.WriteFile(path, content, 0644); err != nil {
|
||||||
|
t.Fatalf("failed to write template file %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunTempl_FromListMutuallyExclusiveWithFromPath(t *testing.T) {
|
||||||
|
saved := saveTemplState()
|
||||||
|
defer restoreTemplState(saved)
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
file := filepath.Join(dir, "schema.json")
|
||||||
|
tmpl := filepath.Join(dir, "tmpl.tmpl")
|
||||||
|
writeTestJSON(t, file, []string{"users"})
|
||||||
|
writeTestTemplate(t, tmpl)
|
||||||
|
|
||||||
|
templSourceType = "json"
|
||||||
|
templSourcePath = file
|
||||||
|
templFromList = []string{file}
|
||||||
|
templTemplatePath = tmpl
|
||||||
|
templOutputPath = ""
|
||||||
|
templMode = "database"
|
||||||
|
templFilenamePattern = "{{.Name}}.txt"
|
||||||
|
|
||||||
|
err := runTempl(nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error when --from-path and --from-list are both set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunTempl_FromListSingleFile(t *testing.T) {
|
||||||
|
saved := saveTemplState()
|
||||||
|
defer restoreTemplState(saved)
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
file := filepath.Join(dir, "schema.json")
|
||||||
|
tmpl := filepath.Join(dir, "tmpl.tmpl")
|
||||||
|
outFile := filepath.Join(dir, "output.txt")
|
||||||
|
writeTestJSON(t, file, []string{"users"})
|
||||||
|
writeTestTemplate(t, tmpl)
|
||||||
|
|
||||||
|
templSourceType = "json"
|
||||||
|
templSourcePath = ""
|
||||||
|
templSourceConn = ""
|
||||||
|
templFromList = []string{file}
|
||||||
|
templTemplatePath = tmpl
|
||||||
|
templOutputPath = outFile
|
||||||
|
templSchemaFilter = ""
|
||||||
|
templMode = "database"
|
||||||
|
templFilenamePattern = "{{.Name}}.txt"
|
||||||
|
|
||||||
|
if err := runTempl(nil, nil); err != nil {
|
||||||
|
t.Fatalf("runTempl() error = %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(outFile); os.IsNotExist(err) {
|
||||||
|
t.Error("expected output file to be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunTempl_FromListMultipleFiles(t *testing.T) {
|
||||||
|
saved := saveTemplState()
|
||||||
|
defer restoreTemplState(saved)
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
file1 := filepath.Join(dir, "users.json")
|
||||||
|
file2 := filepath.Join(dir, "posts.json")
|
||||||
|
tmpl := filepath.Join(dir, "tmpl.tmpl")
|
||||||
|
outFile := filepath.Join(dir, "output.txt")
|
||||||
|
writeTestJSON(t, file1, []string{"users"})
|
||||||
|
writeTestJSON(t, file2, []string{"posts"})
|
||||||
|
writeTestTemplate(t, tmpl)
|
||||||
|
|
||||||
|
templSourceType = "json"
|
||||||
|
templSourcePath = ""
|
||||||
|
templSourceConn = ""
|
||||||
|
templFromList = []string{file1, file2}
|
||||||
|
templTemplatePath = tmpl
|
||||||
|
templOutputPath = outFile
|
||||||
|
templSchemaFilter = ""
|
||||||
|
templMode = "database"
|
||||||
|
templFilenamePattern = "{{.Name}}.txt"
|
||||||
|
|
||||||
|
if err := runTempl(nil, nil); err != nil {
|
||||||
|
t.Fatalf("runTempl() error = %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(outFile); os.IsNotExist(err) {
|
||||||
|
t.Error("expected output file to be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunTempl_FromListPathWithSpaces(t *testing.T) {
|
||||||
|
saved := saveTemplState()
|
||||||
|
defer restoreTemplState(saved)
|
||||||
|
|
||||||
|
spacedDir := filepath.Join(t.TempDir(), "my schema files")
|
||||||
|
if err := os.MkdirAll(spacedDir, 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
file1 := filepath.Join(spacedDir, "users schema.json")
|
||||||
|
file2 := filepath.Join(spacedDir, "posts schema.json")
|
||||||
|
tmpl := filepath.Join(spacedDir, "my template.tmpl")
|
||||||
|
outFile := filepath.Join(spacedDir, "output file.txt")
|
||||||
|
writeTestJSON(t, file1, []string{"users"})
|
||||||
|
writeTestJSON(t, file2, []string{"posts"})
|
||||||
|
writeTestTemplate(t, tmpl)
|
||||||
|
|
||||||
|
templSourceType = "json"
|
||||||
|
templSourcePath = ""
|
||||||
|
templSourceConn = ""
|
||||||
|
templFromList = []string{file1, file2}
|
||||||
|
templTemplatePath = tmpl
|
||||||
|
templOutputPath = outFile
|
||||||
|
templSchemaFilter = ""
|
||||||
|
templMode = "database"
|
||||||
|
templFilenamePattern = "{{.Name}}.txt"
|
||||||
|
|
||||||
|
if err := runTempl(nil, nil); err != nil {
|
||||||
|
t.Fatalf("runTempl() with spaced paths error = %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(outFile); os.IsNotExist(err) {
|
||||||
|
t.Error("expected output file to be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,6 +110,45 @@ func restoreConvertState(s convertState) {
|
|||||||
convertFlattenSchema = s.flattenSchema
|
convertFlattenSchema = s.flattenSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// templState captures and restores all templ global vars.
|
||||||
|
type templState struct {
|
||||||
|
sourceType string
|
||||||
|
sourcePath string
|
||||||
|
sourceConn string
|
||||||
|
fromList []string
|
||||||
|
templatePath string
|
||||||
|
outputPath string
|
||||||
|
schemaFilter string
|
||||||
|
mode string
|
||||||
|
filenamePattern string
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveTemplState() templState {
|
||||||
|
return templState{
|
||||||
|
sourceType: templSourceType,
|
||||||
|
sourcePath: templSourcePath,
|
||||||
|
sourceConn: templSourceConn,
|
||||||
|
fromList: templFromList,
|
||||||
|
templatePath: templTemplatePath,
|
||||||
|
outputPath: templOutputPath,
|
||||||
|
schemaFilter: templSchemaFilter,
|
||||||
|
mode: templMode,
|
||||||
|
filenamePattern: templFilenamePattern,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreTemplState(s templState) {
|
||||||
|
templSourceType = s.sourceType
|
||||||
|
templSourcePath = s.sourcePath
|
||||||
|
templSourceConn = s.sourceConn
|
||||||
|
templFromList = s.fromList
|
||||||
|
templTemplatePath = s.templatePath
|
||||||
|
templOutputPath = s.outputPath
|
||||||
|
templSchemaFilter = s.schemaFilter
|
||||||
|
templMode = s.mode
|
||||||
|
templFilenamePattern = s.filenamePattern
|
||||||
|
}
|
||||||
|
|
||||||
// mergeState captures and restores all merge global vars.
|
// mergeState captures and restores all merge global vars.
|
||||||
type mergeState struct {
|
type mergeState struct {
|
||||||
targetType string
|
targetType string
|
||||||
|
|||||||
Reference in New Issue
Block a user