Files
relspecgo/CLAUDE.md
Hein 7c7054d2e2
Some checks are pending
CI / Test (1.23) (push) Waiting to run
CI / Test (1.24) (push) Waiting to run
CI / Test (1.25) (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Build (push) Waiting to run
So far so good
2025-12-16 18:10:40 +02:00

5.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

RelSpec is a database relations specification tool that provides bidirectional conversion between various database schema formats. It reads database schemas from multiple sources (live databases, DBML, DCTX, DrawDB, etc.) and writes them to various formats (GORM, Bun, JSON, YAML, SQL, etc.).

Build Commands

# Build the binary
make build              # Outputs to build/relspec
go build -o build/relspec ./cmd/relspec

# Run tests
make test              # Run all tests with race detection and coverage
go test ./...          # Run tests without coverage

# Run a single test
go test -run TestName ./pkg/readers/dbml

# Linting
make lint              # Requires golangci-lint installed

# Coverage report
make coverage          # Generates coverage.html

# Clean build artifacts
make clean

# Install binary
make install           # Installs to $GOPATH/bin

Architecture

Core Data Model (pkg/models/)

The central data structure is the Database model, which represents a complete database schema with this hierarchy:

Database
└── Schemas ([]Schema)
    └── Tables ([]Table)
        ├── Columns (map[string]Column)
        ├── Constraints (map[string]Constraint)
        ├── Indexes (map[string]Index)
        └── Relationships (map[string]Relationship)

Key architectural decisions:

  • Tables use maps for Columns, Constraints, Indexes, and Relationships (keyed by name for O(1) lookup)
  • Schemas use slices for Tables (order matters for generation)
  • All model types implement SQLNamer interface (returns lowercase SQL-safe names)
  • Use Init* functions (e.g., InitTable(), InitSchema()) to create properly initialized models with empty maps/slices

Model Views:

  • flatview.go: Provides denormalized views with fully qualified names (e.g., database.schema.table.column)
  • summaryview.go: Lightweight summary views with counts and essential metadata
  • Use .ToFlatColumns(), .ToSummary() methods to convert between views

Reader/Writer Pattern (pkg/readers/, pkg/writers/)

All readers and writers implement consistent interfaces with three granularity levels:

// Reader interface
type Reader interface {
    ReadDatabase() (*models.Database, error)
    ReadSchema() (*models.Schema, error)
    ReadTable() (*models.Table, error)
}

// Writer interface
type Writer interface {
    WriteDatabase(db *models.Database) error
    WriteSchema(schema *models.Schema) error
    WriteTable(table *models.Table) error
}

Important patterns:

  • Each format (dbml, dctx, drawdb, etc.) has its own pkg/readers/<format>/ and pkg/writers/<format>/ subdirectories
  • Use ReaderOptions and WriterOptions structs for configuration (file paths, connection strings, metadata)
  • Schema reading typically returns the first schema when reading from Database
  • Table reading typically returns the first table when reading from Schema

Transformation Layer (pkg/transform/)

The Transformer provides validation and normalization utilities. Note: validation methods are currently stubs (return nil) and need implementation when used.

Database-Specific Utilities (pkg/pgsql/)

Contains PostgreSQL-specific helpers:

  • keywords.go: SQL reserved keywords validation
  • datatypes.go: PostgreSQL data type mappings and conversions

Development Patterns

Adding a New Reader

  1. Create pkg/readers/<format>/ directory
  2. Implement the Reader interface with all three methods
  3. Create a NewReader(options *readers.ReaderOptions) constructor
  4. Parse format-specific data into the canonical models.Database structure
  5. Use models.Init*() functions to create properly initialized structs

Adding a New Writer

  1. Create pkg/writers/<format>/ directory
  2. Implement the Writer interface with all three methods
  3. Create a NewWriter(options *writers.WriterOptions) constructor
  4. Transform canonical models into format-specific output
  5. Handle file writing or other I/O in the writer implementation

Working with Models

// Creating models - ALWAYS use Init functions
db := models.InitDatabase("mydb")
schema := models.InitSchema("public")
table := models.InitTable("users", "public")
column := models.InitColumn("id", "users", "public")

// Adding to parent structures
schema.Tables = append(schema.Tables, table)
table.Columns["id"] = column  // Use map key access for columns
db.Schemas = append(db.Schemas, schema)

// Accessing primary keys and foreign keys
pk := table.GetPrimaryKey()  // Returns *Column or nil
fks := table.GetForeignKeys()  // Returns []*Constraint

CLI Implementation Status

The CLI in cmd/relspec/main.go is currently a placeholder showing usage examples. It will be implemented using the Cobra framework (already in dependencies).

Testing

  • Test files should be in the same package as the code they test
  • Use table-driven tests for multiple test cases
  • All tests run with race detection via make test
  • Coverage reports available via make coverage

Module Information

  • Module path: git.warky.dev/wdevs/relspecgo
  • Go version: 1.25.5
  • Uses Cobra for CLI, Viper for configuration