From f258f8baeb575adb573e43dbdb633f15b5b1a6bb Mon Sep 17 00:00:00 2001 From: Hein Date: Sat, 10 Jan 2026 13:32:33 +0200 Subject: [PATCH] =?UTF-8?q?feat(writer):=20=F0=9F=8E=89=20Add=20filename?= =?UTF-8?q?=20sanitization=20for=20DBML=20identifiers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implement SanitizeFilename function to clean identifiers * Remove quotes, comments, and invalid characters from filenames * Update filename generation in writers to use sanitized names --- pkg/readers/dbml/reader.go | 12 +++++++++++- pkg/writers/bun/writer.go | 5 ++++- pkg/writers/drizzle/writer.go | 4 +++- pkg/writers/gorm/writer.go | 5 ++++- pkg/writers/writer.go | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/pkg/readers/dbml/reader.go b/pkg/readers/dbml/reader.go index 24a0302..36145b5 100644 --- a/pkg/readers/dbml/reader.go +++ b/pkg/readers/dbml/reader.go @@ -128,9 +128,19 @@ func (r *Reader) readDirectoryDBML(dirPath string) (*models.Database, error) { return db, nil } -// stripQuotes removes surrounding quotes from an identifier +// stripQuotes removes surrounding quotes and comments from an identifier func stripQuotes(s string) string { s = strings.TrimSpace(s) + + // Remove DBML comments in brackets (e.g., [note: 'description']) + // This handles inline comments like: "table_name" [note: 'comment'] + commentRegex := regexp.MustCompile(`\s*\[.*?\]\s*`) + s = commentRegex.ReplaceAllString(s, "") + + // Trim again after removing comments + s = strings.TrimSpace(s) + + // Remove surrounding quotes (double or single) if len(s) >= 2 && ((s[0] == '"' && s[len(s)-1] == '"') || (s[0] == '\'' && s[len(s)-1] == '\'')) { return s[1 : len(s)-1] } diff --git a/pkg/writers/bun/writer.go b/pkg/writers/bun/writer.go index 7cbae80..a60daf5 100644 --- a/pkg/writers/bun/writer.go +++ b/pkg/writers/bun/writer.go @@ -207,7 +207,10 @@ func (w *Writer) writeMultiFile(db *models.Database) error { } // Generate filename: sql_{schema}_{table}.go - filename := fmt.Sprintf("sql_%s_%s.go", schema.Name, table.Name) + // Sanitize schema and table names to remove quotes, comments, and invalid characters + safeSchemaName := writers.SanitizeFilename(schema.Name) + safeTableName := writers.SanitizeFilename(table.Name) + filename := fmt.Sprintf("sql_%s_%s.go", safeSchemaName, safeTableName) filepath := filepath.Join(w.options.OutputPath, filename) // Write file diff --git a/pkg/writers/drizzle/writer.go b/pkg/writers/drizzle/writer.go index 03d95a8..e82bb67 100644 --- a/pkg/writers/drizzle/writer.go +++ b/pkg/writers/drizzle/writer.go @@ -196,7 +196,9 @@ func (w *Writer) writeTableFile(table *models.Table, schema *models.Schema, db * } // Generate filename: {tableName}.ts - filename := filepath.Join(w.options.OutputPath, table.Name+".ts") + // Sanitize table name to remove quotes, comments, and invalid characters + safeTableName := writers.SanitizeFilename(table.Name) + filename := filepath.Join(w.options.OutputPath, safeTableName+".ts") return os.WriteFile(filename, []byte(code), 0644) } diff --git a/pkg/writers/gorm/writer.go b/pkg/writers/gorm/writer.go index 3ac36ef..ee232f4 100644 --- a/pkg/writers/gorm/writer.go +++ b/pkg/writers/gorm/writer.go @@ -201,7 +201,10 @@ func (w *Writer) writeMultiFile(db *models.Database) error { } // Generate filename: sql_{schema}_{table}.go - filename := fmt.Sprintf("sql_%s_%s.go", schema.Name, table.Name) + // Sanitize schema and table names to remove quotes, comments, and invalid characters + safeSchemaName := writers.SanitizeFilename(schema.Name) + safeTableName := writers.SanitizeFilename(table.Name) + filename := fmt.Sprintf("sql_%s_%s.go", safeSchemaName, safeTableName) filepath := filepath.Join(w.options.OutputPath, filename) // Write file diff --git a/pkg/writers/writer.go b/pkg/writers/writer.go index f2f6c23..4a2810d 100644 --- a/pkg/writers/writer.go +++ b/pkg/writers/writer.go @@ -1,6 +1,9 @@ package writers import ( + "regexp" + "strings" + "git.warky.dev/wdevs/relspecgo/pkg/models" ) @@ -28,3 +31,33 @@ type WriterOptions struct { // Additional options can be added here as needed Metadata map[string]interface{} } + +// SanitizeFilename removes quotes, comments, and invalid characters from identifiers +// to make them safe for use in filenames. This handles: +// - Double and single quotes: "table_name" or 'table_name' -> table_name +// - DBML comments: table [note: 'description'] -> table +// - Invalid filename characters: replaced with underscores +func SanitizeFilename(name string) string { + // Remove DBML/DCTX style comments in brackets (e.g., [note: 'description']) + commentRegex := regexp.MustCompile(`\s*\[.*?\]\s*`) + name = commentRegex.ReplaceAllString(name, "") + + // Remove quotes (both single and double) + name = strings.ReplaceAll(name, `"`, "") + name = strings.ReplaceAll(name, `'`, "") + + // Remove backticks (MySQL style identifiers) + name = strings.ReplaceAll(name, "`", "") + + // Replace invalid filename characters with underscores + // Invalid chars: / \ : * ? " < > | and control characters + invalidChars := regexp.MustCompile(`[/\\:*?"<>|\x00-\x1f\x7f]`) + name = invalidChars.ReplaceAllString(name, "_") + + // Trim whitespace and consecutive underscores + name = strings.TrimSpace(name) + name = regexp.MustCompile(`_+`).ReplaceAllString(name, "_") + name = strings.Trim(name, "_") + + return name +}