init
This commit is contained in:
5
.claude/commands/build.md
Normal file
5
.claude/commands/build.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description: Build the RelSpec binary
|
||||||
|
---
|
||||||
|
|
||||||
|
Build the RelSpec project by running `go build -o relspec ./cmd/relspec`. Report the build status and any errors encountered.
|
||||||
9
.claude/commands/coverage.md
Normal file
9
.claude/commands/coverage.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
description: Generate test coverage report
|
||||||
|
---
|
||||||
|
|
||||||
|
Generate and display test coverage for RelSpec:
|
||||||
|
1. Run `go test -cover ./...` to get coverage percentage
|
||||||
|
2. If detailed coverage is needed, run `go test -coverprofile=coverage.out ./...` and then `go tool cover -html=coverage.out` to generate HTML report
|
||||||
|
|
||||||
|
Show coverage statistics and identify areas needing more tests.
|
||||||
10
.claude/commands/lint.md
Normal file
10
.claude/commands/lint.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
description: Run Go linters on the codebase
|
||||||
|
---
|
||||||
|
|
||||||
|
Run linting tools on the RelSpec codebase:
|
||||||
|
1. First run `gofmt -l .` to check formatting
|
||||||
|
2. If golangci-lint is available, run `golangci-lint run ./...`
|
||||||
|
3. Run `go vet ./...` to check for suspicious constructs
|
||||||
|
|
||||||
|
Report any issues found and suggest fixes if needed.
|
||||||
5
.claude/commands/test.md
Normal file
5
.claude/commands/test.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description: Run all tests for the RelSpec project
|
||||||
|
---
|
||||||
|
|
||||||
|
Run `go test ./...` to execute all unit tests in the project. Show a summary of the results and highlight any failures.
|
||||||
25
.claude/settings.json
Normal file
25
.claude/settings.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"name": "RelSpec",
|
||||||
|
"description": "Database Relations Specification Tool for Go",
|
||||||
|
"language": "go"
|
||||||
|
},
|
||||||
|
"agent": {
|
||||||
|
"preferred": "Explore",
|
||||||
|
"description": "Use Explore agent for fast codebase navigation and Go project exploration"
|
||||||
|
},
|
||||||
|
"codeStyle": {
|
||||||
|
"useGofmt": true,
|
||||||
|
"lineLength": 100,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": true
|
||||||
|
},
|
||||||
|
"testing": {
|
||||||
|
"framework": "go test",
|
||||||
|
"runOnChange": false
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"command": "go build -o relspec ./cmd/relspec",
|
||||||
|
"outputBinary": "relspec"
|
||||||
|
}
|
||||||
|
}
|
||||||
39
.editorconfig
Normal file
39
.editorconfig
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# Go files
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# YAML files
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# JSON files
|
||||||
|
[*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# Markdown files
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# Makefile
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# Shell scripts
|
||||||
|
[*.sh]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
85
.github/workflows/ci.yml
vendored
Normal file
85
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version: ['1.23', '1.24', '1.25']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
|
- name: Cache Go modules
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Download dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
file: ./coverage.out
|
||||||
|
flags: unittests
|
||||||
|
name: codecov-umbrella
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.25'
|
||||||
|
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v6
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: --config=.golangci.json
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.25'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v ./cmd/relspec
|
||||||
|
|
||||||
|
- name: Check mod tidiness
|
||||||
|
run: |
|
||||||
|
go mod tidy
|
||||||
|
git diff --exit-code go.mod go.sum
|
||||||
26
.gitignore
vendored
26
.gitignore
vendored
@@ -21,3 +21,29 @@
|
|||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
|
|
||||||
|
# RelSpec specific
|
||||||
|
relspec
|
||||||
|
*.test
|
||||||
|
coverage.out
|
||||||
|
coverage.html
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Test outputs
|
||||||
|
test_output/
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|||||||
114
.golangci.json
Normal file
114
.golangci.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"formatters": {
|
||||||
|
"enable": [
|
||||||
|
"gofmt",
|
||||||
|
"goimports"
|
||||||
|
],
|
||||||
|
"exclusions": {
|
||||||
|
"generated": "lax",
|
||||||
|
"paths": [
|
||||||
|
"third_party$",
|
||||||
|
"builtin$",
|
||||||
|
"examples$"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"gofmt": {
|
||||||
|
"simplify": true
|
||||||
|
},
|
||||||
|
"goimports": {
|
||||||
|
"local-prefixes": [
|
||||||
|
"git.warky.dev/wdevs/relspecgo"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"max-issues-per-linter": 0,
|
||||||
|
"max-same-issues": 0
|
||||||
|
},
|
||||||
|
"linters": {
|
||||||
|
"enable": [
|
||||||
|
"gocritic",
|
||||||
|
"misspell",
|
||||||
|
"revive"
|
||||||
|
],
|
||||||
|
"exclusions": {
|
||||||
|
"generated": "lax",
|
||||||
|
"paths": [
|
||||||
|
"third_party$",
|
||||||
|
"builtin$",
|
||||||
|
"examples$",
|
||||||
|
"mocks?",
|
||||||
|
"tests?"
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"linters": [
|
||||||
|
"dupl",
|
||||||
|
"errcheck",
|
||||||
|
"gocritic",
|
||||||
|
"gosec"
|
||||||
|
],
|
||||||
|
"path": "_test\\.go"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"linters": [
|
||||||
|
"errcheck"
|
||||||
|
],
|
||||||
|
"text": "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "_test\\.go",
|
||||||
|
"text": "cognitive complexity|cyclomatic complexity"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"errcheck": {
|
||||||
|
"check-blank": false,
|
||||||
|
"check-type-assertions": false
|
||||||
|
},
|
||||||
|
"gocritic": {
|
||||||
|
"enabled-checks": [
|
||||||
|
"boolExprSimplify",
|
||||||
|
"builtinShadow",
|
||||||
|
"emptyFallthrough",
|
||||||
|
"equalFold",
|
||||||
|
"indexAlloc",
|
||||||
|
"initClause",
|
||||||
|
"methodExprCall",
|
||||||
|
"nilValReturn",
|
||||||
|
"rangeExprCopy",
|
||||||
|
"rangeValCopy",
|
||||||
|
"stringXbytes",
|
||||||
|
"typeAssertChain",
|
||||||
|
"unlabelStmt",
|
||||||
|
"unnamedResult",
|
||||||
|
"unnecessaryBlock",
|
||||||
|
"weakCond",
|
||||||
|
"yodaStyleExpr"
|
||||||
|
],
|
||||||
|
"disabled-checks": [
|
||||||
|
"ifElseChain"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"revive": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"disabled": true,
|
||||||
|
"name": "exported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disabled": true,
|
||||||
|
"name": "package-comments"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run": {
|
||||||
|
"tests": true
|
||||||
|
},
|
||||||
|
"version": "2"
|
||||||
|
}
|
||||||
145
CONTRIBUTING.md
Normal file
145
CONTRIBUTING.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# Contributing to RelSpec
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to RelSpec.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Go 1.21 or higher
|
||||||
|
- Git
|
||||||
|
- (Optional) golangci-lint for linting
|
||||||
|
- (Optional) Docker for database testing
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/wdevs/relspecgo.git
|
||||||
|
cd relspecgo
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
go mod download
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run tests:
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Build the project:
|
||||||
|
```bash
|
||||||
|
go build -o relspec ./cmd/relspec
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
relspecgo/
|
||||||
|
├── cmd/ # CLI application entry point
|
||||||
|
├── pkg/
|
||||||
|
│ ├── readers/ # Input format readers (XML, JSON, DCTX, DB, GORM, Bun)
|
||||||
|
│ ├── writers/ # Output format writers (GORM, Bun, JSON, YAML)
|
||||||
|
│ ├── models/ # Internal data models for relations
|
||||||
|
│ └── transform/ # Transformation and validation logic
|
||||||
|
├── examples/ # Usage examples and sample files
|
||||||
|
├── tests/ # Integration tests
|
||||||
|
└── .claude/ # Claude Code configuration and commands
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding New Readers
|
||||||
|
|
||||||
|
To add a new input format reader:
|
||||||
|
|
||||||
|
1. Create a new file in `pkg/readers/` (e.g., `myformat_reader.go`)
|
||||||
|
2. Implement the `Reader` interface:
|
||||||
|
```go
|
||||||
|
type Reader interface {
|
||||||
|
Read(source string) (*models.Schema, error)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Add tests in `pkg/readers/myformat_reader_test.go`
|
||||||
|
4. Register the reader in the CLI
|
||||||
|
|
||||||
|
## Adding New Writers
|
||||||
|
|
||||||
|
To add a new output format writer:
|
||||||
|
|
||||||
|
1. Create a new file in `pkg/writers/` (e.g., `myformat_writer.go`)
|
||||||
|
2. Implement the `Writer` interface:
|
||||||
|
```go
|
||||||
|
type Writer interface {
|
||||||
|
Write(schema *models.Schema, destination string) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Add tests in `pkg/writers/myformat_writer_test.go`
|
||||||
|
4. Register the writer in the CLI
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- Follow standard Go conventions
|
||||||
|
- Use `gofmt` for formatting
|
||||||
|
- Run `go vet` to check for issues
|
||||||
|
- Use meaningful variable and function names
|
||||||
|
- Add comments for exported functions and types
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Write unit tests for all new functionality
|
||||||
|
- Aim for >80% code coverage
|
||||||
|
- Use table-driven tests where appropriate
|
||||||
|
- Include both positive and negative test cases
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# With coverage
|
||||||
|
go test -cover ./...
|
||||||
|
|
||||||
|
# Verbose output
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
|
# Specific package
|
||||||
|
go test ./pkg/readers/...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Committing Changes
|
||||||
|
|
||||||
|
- Write clear, descriptive commit messages
|
||||||
|
- Follow conventional commits format: `type(scope): description`
|
||||||
|
- Types: feat, fix, docs, test, refactor, chore
|
||||||
|
- Example: `feat(readers): add PostgreSQL support`
|
||||||
|
- Keep commits focused and atomic
|
||||||
|
- Reference issues in commit messages when applicable
|
||||||
|
|
||||||
|
## Pull Request Process
|
||||||
|
|
||||||
|
1. Create a feature branch from `master`
|
||||||
|
2. Make your changes
|
||||||
|
3. Add tests for new functionality
|
||||||
|
4. Ensure all tests pass
|
||||||
|
5. Update documentation if needed
|
||||||
|
6. Submit a pull request with a clear description
|
||||||
|
|
||||||
|
## Claude Code Commands
|
||||||
|
|
||||||
|
This project includes Claude Code slash commands for common tasks:
|
||||||
|
|
||||||
|
- `/test` - Run all tests
|
||||||
|
- `/build` - Build the binary
|
||||||
|
- `/lint` - Run linters
|
||||||
|
- `/coverage` - Generate coverage report
|
||||||
|
|
||||||
|
## Questions or Issues?
|
||||||
|
|
||||||
|
- Open an issue for bugs or feature requests
|
||||||
|
- Start a discussion for questions or ideas
|
||||||
|
- Check existing issues before creating new ones
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
By contributing to RelSpec, you agree that your contributions will be licensed under the Apache License 2.0.
|
||||||
62
Makefile
Normal file
62
Makefile
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
.PHONY: all build test lint coverage clean install help
|
||||||
|
|
||||||
|
# Binary name
|
||||||
|
BINARY_NAME=relspec
|
||||||
|
|
||||||
|
# Build directory
|
||||||
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
# Go parameters
|
||||||
|
GOCMD=go
|
||||||
|
GOBUILD=$(GOCMD) build
|
||||||
|
GOTEST=$(GOCMD) test
|
||||||
|
GOGET=$(GOCMD) get
|
||||||
|
GOMOD=$(GOCMD) mod
|
||||||
|
GOCLEAN=$(GOCMD) clean
|
||||||
|
|
||||||
|
all: lint test build ## Run linting, tests, and build
|
||||||
|
|
||||||
|
build: ## Build the binary
|
||||||
|
@echo "Building $(BINARY_NAME)..."
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
$(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/relspec
|
||||||
|
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)"
|
||||||
|
|
||||||
|
test: ## Run tests
|
||||||
|
@echo "Running tests..."
|
||||||
|
$(GOTEST) -v -race -coverprofile=coverage.out ./...
|
||||||
|
|
||||||
|
coverage: test ## Run tests with coverage report
|
||||||
|
@echo "Generating coverage report..."
|
||||||
|
$(GOCMD) tool cover -html=coverage.out -o coverage.html
|
||||||
|
@echo "Coverage report generated: coverage.html"
|
||||||
|
|
||||||
|
lint: ## Run linter
|
||||||
|
@echo "Running linter..."
|
||||||
|
@if command -v golangci-lint > /dev/null; then \
|
||||||
|
golangci-lint run --config=.golangci.json; \
|
||||||
|
else \
|
||||||
|
echo "golangci-lint not installed. Install with: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
clean: ## Clean build artifacts
|
||||||
|
@echo "Cleaning..."
|
||||||
|
$(GOCLEAN)
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
rm -f coverage.out coverage.html
|
||||||
|
@echo "Clean complete"
|
||||||
|
|
||||||
|
install: ## Install the binary to $GOPATH/bin
|
||||||
|
@echo "Installing $(BINARY_NAME)..."
|
||||||
|
$(GOCMD) install ./cmd/relspec
|
||||||
|
@echo "Install complete"
|
||||||
|
|
||||||
|
deps: ## Download dependencies
|
||||||
|
@echo "Downloading dependencies..."
|
||||||
|
$(GOMOD) download
|
||||||
|
$(GOMOD) tidy
|
||||||
|
@echo "Dependencies updated"
|
||||||
|
|
||||||
|
help: ## Display this help screen
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||||
94
README.md
94
README.md
@@ -1,3 +1,93 @@
|
|||||||
# relspecgo
|
# RelSpec
|
||||||
|
|
||||||
Resolve Spec Go
|
> Database Relations Specification Tool for Go
|
||||||
|
|
||||||
|
RelSpec is a comprehensive database relations management tool that reads, transforms, and writes database table specifications across multiple formats and ORMs.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
RelSpec provides bidirectional conversion between various database specification formats, allowing you to:
|
||||||
|
- Inspect live databases and extract their structure
|
||||||
|
- Convert between different ORM models (GORM, Bun)
|
||||||
|
- Transform legacy schema definitions (Clarion DCTX, XML, JSON)
|
||||||
|
- Generate standardized specification files (JSON, YAML)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Input Formats
|
||||||
|
- **XML** - Generic XML schema definitions
|
||||||
|
- **JSON** - JSON-based schema specifications
|
||||||
|
- **Clarion DCTX** - Clarion database dictionary format
|
||||||
|
- **Database Inspection** - Direct database introspection
|
||||||
|
- **GORM Models** - Read existing GORM Go structs
|
||||||
|
- **Bun Models** - Read existing Bun Go structs
|
||||||
|
|
||||||
|
### Output Formats
|
||||||
|
- **GORM Models** - Generate GORM-compatible Go structs
|
||||||
|
- **Bun Models** - Generate Bun-compatible Go structs
|
||||||
|
- **JSON** - Standard JSON schema output
|
||||||
|
- **YAML** - Human-readable YAML format
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/wdevs/relspecgo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Inspect database and generate GORM models
|
||||||
|
relspec --input db --conn "postgres://..." --output gorm --out-file models.go
|
||||||
|
|
||||||
|
# Convert GORM models to Bun
|
||||||
|
relspec --input gorm --in-file existing.go --output bun --out-file bun_models.go
|
||||||
|
|
||||||
|
# Export database schema to JSON
|
||||||
|
relspec --input db --conn "mysql://..." --output json --out-file schema.json
|
||||||
|
|
||||||
|
# Convert Clarion DCTX to YAML
|
||||||
|
relspec --input dctx --in-file legacy.dctx --output yaml --out-file schema.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
relspecgo/
|
||||||
|
├── cmd/ # CLI application
|
||||||
|
├── pkg/
|
||||||
|
│ ├── readers/ # Input format readers
|
||||||
|
│ ├── writers/ # Output format writers
|
||||||
|
│ ├── models/ # Internal data models
|
||||||
|
│ └── transform/ # Transformation logic
|
||||||
|
├── examples/ # Usage examples
|
||||||
|
└── tests/ # Test files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Go 1.21 or higher
|
||||||
|
- Access to test databases (optional)
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go build -o relspec ./cmd/relspec
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Apache License 2.0 - See [LICENSE](LICENSE) for details.
|
||||||
|
|
||||||
|
Copyright 2025 wdevs
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions welcome. Please open an issue or submit a pull request.
|
||||||
120
TODO.md
Normal file
120
TODO.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# RelSpec - TODO List
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
- [ ] Initialize Go module (`go.mod`)
|
||||||
|
- [ ] Set up project directory structure
|
||||||
|
- [ ] Create `.editorconfig` for consistent formatting
|
||||||
|
- [ ] Add GitHub Actions for CI/CD
|
||||||
|
- [ ] Set up pre-commit hooks
|
||||||
|
|
||||||
|
## Core Infrastructure
|
||||||
|
- [ ] Define internal data model for database relations
|
||||||
|
- [ ] Implement relation types (one-to-one, one-to-many, many-to-many)
|
||||||
|
- [ ] Create validation framework for specifications
|
||||||
|
- [ ] Design plugin architecture for readers/writers
|
||||||
|
|
||||||
|
## Input Readers
|
||||||
|
- [ ] **Database Inspector**
|
||||||
|
- [ ] PostgreSQL driver
|
||||||
|
- [ ] MySQL driver
|
||||||
|
- [ ] SQLite driver
|
||||||
|
- [ ] MSSQL driver
|
||||||
|
- [ ] Foreign key detection
|
||||||
|
- [ ] Index extraction
|
||||||
|
|
||||||
|
- [ ] **XML Reader**
|
||||||
|
- [ ] XML schema parser
|
||||||
|
- [ ] Validation against XSD
|
||||||
|
|
||||||
|
- [ ] **JSON Reader**
|
||||||
|
- [ ] JSON schema parser
|
||||||
|
- [ ] Schema validation
|
||||||
|
|
||||||
|
- [ ] **Clarion DCTX Reader**
|
||||||
|
- [ ] DCTX file parser
|
||||||
|
- [ ] Legacy format support
|
||||||
|
|
||||||
|
- [ ] **GORM Model Reader**
|
||||||
|
- [ ] Go AST parser for GORM tags
|
||||||
|
- [ ] Struct field analysis
|
||||||
|
- [ ] Relation tag extraction
|
||||||
|
|
||||||
|
- [ ] **Bun Model Reader**
|
||||||
|
- [ ] Go AST parser for Bun tags
|
||||||
|
- [ ] Struct field analysis
|
||||||
|
- [ ] Relation tag extraction
|
||||||
|
|
||||||
|
## Output Writers
|
||||||
|
- [ ] **GORM Writer**
|
||||||
|
- [ ] Go struct generation
|
||||||
|
- [ ] GORM tag formatting
|
||||||
|
- [ ] Relation definitions
|
||||||
|
- [ ] gofmt integration
|
||||||
|
|
||||||
|
- [ ] **Bun Writer**
|
||||||
|
- [ ] Go struct generation
|
||||||
|
- [ ] Bun tag formatting
|
||||||
|
- [ ] Relation definitions
|
||||||
|
- [ ] gofmt integration
|
||||||
|
|
||||||
|
- [ ] **JSON Writer**
|
||||||
|
- [ ] Schema serialization
|
||||||
|
- [ ] Pretty printing
|
||||||
|
|
||||||
|
- [ ] **YAML Writer**
|
||||||
|
- [ ] Schema serialization
|
||||||
|
- [ ] Comment preservation
|
||||||
|
|
||||||
|
## CLI Application
|
||||||
|
- [ ] Command-line interface using cobra
|
||||||
|
- [ ] Input format flags
|
||||||
|
- [ ] Output format flags
|
||||||
|
- [ ] Connection string handling
|
||||||
|
- [ ] File I/O operations
|
||||||
|
- [ ] Progress indicators
|
||||||
|
- [ ] Error handling and reporting
|
||||||
|
- [ ] Configuration file support
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- [ ] Unit tests for each reader
|
||||||
|
- [ ] Unit tests for each writer
|
||||||
|
- [ ] Integration tests for conversion pipelines
|
||||||
|
- [ ] Test fixtures for all formats
|
||||||
|
- [ ] Database test containers
|
||||||
|
- [ ] Benchmark tests for large schemas
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- [ ] API documentation (godoc)
|
||||||
|
- [ ] Usage examples for each format combination
|
||||||
|
- [ ] Migration guides
|
||||||
|
- [ ] Architecture documentation
|
||||||
|
- [ ] Contributing guidelines
|
||||||
|
|
||||||
|
## Advanced Features
|
||||||
|
- [ ] Dry-run mode for validation
|
||||||
|
- [ ] Diff tool for comparing specifications
|
||||||
|
- [ ] Migration script generation
|
||||||
|
- [ ] Custom type mapping configuration
|
||||||
|
- [ ] Batch processing support
|
||||||
|
- [ ] Watch mode for auto-regeneration
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- [ ] Concurrent processing for multiple tables
|
||||||
|
- [ ] Streaming for large databases
|
||||||
|
- [ ] Memory optimization
|
||||||
|
- [ ] Caching layer for repeated operations
|
||||||
|
|
||||||
|
## Quality & Maintenance
|
||||||
|
- [ ] Linting with golangci-lint
|
||||||
|
- [ ] Code coverage > 80%
|
||||||
|
- [ ] Security scanning
|
||||||
|
- [ ] Dependency updates automation
|
||||||
|
- [ ] Release automation
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
- [ ] Web UI for visual editing
|
||||||
|
- [ ] REST API server mode
|
||||||
|
- [ ] Support for NoSQL databases
|
||||||
|
- [ ] GraphQL schema generation
|
||||||
|
- [ ] Prisma schema support
|
||||||
|
- [ ] TypeORM support
|
||||||
22
go.mod
Normal file
22
go.mod
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
module git.warky.dev/wdevs/relspecgo
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/cobra v1.10.2 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/spf13/viper v1.21.0 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.28.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
36
go.sum
Normal file
36
go.sum
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
71
make_release.sh
Normal file
71
make_release.sh
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Ask if the user wants to make a release version
|
||||||
|
read -p "Do you want to make a release version? (y/n): " make_release
|
||||||
|
|
||||||
|
if [[ $make_release =~ ^[Yy]$ ]]; then
|
||||||
|
# Get the latest tag from git
|
||||||
|
latest_tag=$(git describe --tags --abbrev=0 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$latest_tag" ]; then
|
||||||
|
# No tags exist yet, start with v1.0.0
|
||||||
|
suggested_version="v1.0.0"
|
||||||
|
echo "No existing tags found. Starting with $suggested_version"
|
||||||
|
else
|
||||||
|
echo "Latest tag: $latest_tag"
|
||||||
|
|
||||||
|
# Remove 'v' prefix if present
|
||||||
|
version_number="${latest_tag#v}"
|
||||||
|
|
||||||
|
# Split version into major.minor.patch
|
||||||
|
IFS='.' read -r major minor patch <<< "$version_number"
|
||||||
|
|
||||||
|
# Increment patch version
|
||||||
|
patch=$((patch + 1))
|
||||||
|
|
||||||
|
# Construct new version
|
||||||
|
suggested_version="v${major}.${minor}.${patch}"
|
||||||
|
echo "Suggested next version: $suggested_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ask the user for the version number with the suggested version as default
|
||||||
|
read -p "Enter the version number (press Enter for $suggested_version): " version
|
||||||
|
|
||||||
|
# Use suggested version if user pressed Enter without input
|
||||||
|
if [ -z "$version" ]; then
|
||||||
|
version="$suggested_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prepend 'v' to the version if it doesn't start with it
|
||||||
|
if ! [[ $version =~ ^v ]]; then
|
||||||
|
version="v$version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get commit logs since the last tag
|
||||||
|
if [ -z "$latest_tag" ]; then
|
||||||
|
# No previous tag, get all commits
|
||||||
|
commit_logs=$(git log --pretty=format:"- %s" --no-merges)
|
||||||
|
else
|
||||||
|
# Get commits since the last tag
|
||||||
|
commit_logs=$(git log "${latest_tag}..HEAD" --pretty=format:"- %s" --no-merges)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the tag message
|
||||||
|
if [ -z "$commit_logs" ]; then
|
||||||
|
tag_message="Release $version"
|
||||||
|
else
|
||||||
|
tag_message="Release $version
|
||||||
|
|
||||||
|
${commit_logs}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create an annotated tag with the commit logs
|
||||||
|
git tag -a "$version" -m "$tag_message"
|
||||||
|
|
||||||
|
# Push the tag to the remote repository
|
||||||
|
git push origin "$version"
|
||||||
|
|
||||||
|
echo "Tag $version created and pushed to the remote repository."
|
||||||
|
else
|
||||||
|
echo "No release version created."
|
||||||
|
fi
|
||||||
149
pkg/models/models.go
Normal file
149
pkg/models/models.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type DatabaseType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PostgresqlDatabaseType DatabaseType = "pgsql"
|
||||||
|
MSSQLDatabaseType DatabaseType = "mssql"
|
||||||
|
SqlLiteDatabaseType DatabaseType = "sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database represents the complete database schema
|
||||||
|
type Database struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Schemas []*Schema `json:"schemas" yaml:"schemas" xml:"schemas"`
|
||||||
|
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
|
||||||
|
DatabaseType DatabaseType `json:"database_type,omitempty" yaml:"database_type,omitempty" xml:"database_type,omitempty"`
|
||||||
|
DatabaseVersion string `json:"database_version,omitempty" yaml:"database_version,omitempty" xml:"database_version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schema struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Tables []*Table `json:"tables" yaml:"tables" xml:"-"`
|
||||||
|
Owner string `json:"owner" yaml:"owner" xml:"owner"`
|
||||||
|
Permissions map[string]string `json:"permissions,omitempty" yaml:"permissions,omitempty" xml:"-"`
|
||||||
|
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
|
||||||
|
Metadata map[string]interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty" xml:"-"`
|
||||||
|
Scripts []*Script `json:"scripts,omitempty" yaml:"scripts,omitempty" xml:"scripts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Table struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Schema string `json:"schema" yaml:"schema" xml:"schema"`
|
||||||
|
Columns map[string]*Column `json:"columns" yaml:"columns" xml:"-"`
|
||||||
|
Constraints map[string]*Constraint `json:"constraints" yaml:"constraints" xml:"-"`
|
||||||
|
Indexes map[string]*Index `json:"indexes,omitempty" yaml:"indexes,omitempty" xml:"-"`
|
||||||
|
Relationships map[string]*Relationship `json:"relationships,omitempty" yaml:"relationships,omitempty" xml:"-"`
|
||||||
|
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
|
||||||
|
Tablespace string `json:"tablespace,omitempty" yaml:"tablespace,omitempty" xml:"tablespace,omitempty"`
|
||||||
|
Metadata map[string]interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty" xml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Table) GetPrimaryKey() *Column {
|
||||||
|
for _, column := range m.Columns {
|
||||||
|
if column.IsPrimaryKey {
|
||||||
|
return column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Table) GetForeignKeys() []*Constraint {
|
||||||
|
keys := make([]*Constraint, 0)
|
||||||
|
|
||||||
|
for _, c := range m.Constraints {
|
||||||
|
if c.Type == ForeignKeyConstraint {
|
||||||
|
keys = append(keys, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column represents a table column
|
||||||
|
type Column struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Table string `json:"table" yaml:"table" xml:"table"`
|
||||||
|
Schema string `json:"schema" yaml:"schema" xml:"schema"`
|
||||||
|
Type string `json:"type" yaml:"type" xml:"type"`
|
||||||
|
Length int `json:"length,omitempty" yaml:"length,omitempty" xml:"length,omitempty"`
|
||||||
|
Precision int `json:"precision,omitempty" yaml:"precision,omitempty" xml:"precision,omitempty"`
|
||||||
|
Scale int `json:"scale,omitempty" yaml:"scale,omitempty" xml:"scale,omitempty"`
|
||||||
|
NotNull bool `json:"not_null" yaml:"not_null" xml:"not_null"`
|
||||||
|
Default interface{} `json:"default,omitempty" yaml:"default,omitempty" xml:"default,omitempty"`
|
||||||
|
AutoIncrement bool `json:"auto_increment" yaml:"auto_increment" xml:"auto_increment"`
|
||||||
|
IsPrimaryKey bool `json:"is_primary_key" yaml:"is_primary_key" xml:"is_primary_key"`
|
||||||
|
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
|
||||||
|
Collation string `json:"collation,omitempty" yaml:"collation,omitempty" xml:"collation,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Index struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Table string `json:"table,omitempty" yaml:"table,omitempty" xml:"table,omitempty"`
|
||||||
|
Schema string `json:"schema,omitempty" yaml:"schema,omitempty" xml:"schema,omitempty"`
|
||||||
|
Columns []string `json:"columns" yaml:"columns" xml:"columns"`
|
||||||
|
Unique bool `json:"unique" yaml:"unique" xml:"unique"`
|
||||||
|
Type string `json:"type" yaml:"type" xml:"type"` // btree, hash, gin, gist, etc.
|
||||||
|
Where string `json:"where,omitempty" yaml:"where,omitempty" xml:"where,omitempty"` // partial index condition
|
||||||
|
Concurrent bool `json:"concurrent,omitempty" yaml:"concurrent,omitempty" xml:"concurrent,omitempty"`
|
||||||
|
Include []string `json:"include,omitempty" yaml:"include,omitempty" xml:"include,omitempty"` // INCLUDE columns
|
||||||
|
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RelationType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
OneToOne RelationType = "one_to_one"
|
||||||
|
OneToMany RelationType = "one_to_many"
|
||||||
|
ManyToMany RelationType = "many_to_many"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Relationship struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Type RelationType `json:"type" yaml:"type" xml:"type"`
|
||||||
|
FromTable string `json:"from_table" yaml:"from_table" xml:"from_table"`
|
||||||
|
FromSchema string `json:"from_schema" yaml:"from_schema" xml:"from_schema"`
|
||||||
|
ToTable string `json:"to_table" yaml:"to_table" xml:"to_table"`
|
||||||
|
ToSchema string `json:"to_schema" yaml:"to_schema" xml:"to_schema"`
|
||||||
|
ForeignKey string `json:"foreign_key" yaml:"foreign_key" xml:"foreign_key"`
|
||||||
|
Properties map[string]string `json:"properties" yaml:"properties" xml:"-"`
|
||||||
|
ThroughTable string `json:"through_table,omitempty" yaml:"through_table,omitempty" xml:"through_table,omitempty"` // For many-to-many
|
||||||
|
ThroughSchema string `json:"through_schema,omitempty" yaml:"through_schema,omitempty" xml:"through_schema,omitempty"`
|
||||||
|
Description string `json:"description,omitempty" yaml:"description,omitempty" xml:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Constraint struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Type ConstraintType `json:"type" yaml:"type" xml:"type"`
|
||||||
|
Columns []string `json:"columns" yaml:"columns" xml:"columns"`
|
||||||
|
Expression string `json:"expression,omitempty" yaml:"expression,omitempty" xml:"expression,omitempty"`
|
||||||
|
Schema string `json:"schema,omitempty" yaml:"schema,omitempty" xml:"schema,omitempty"`
|
||||||
|
Table string `json:"table,omitempty" yaml:"table,omitempty" xml:"table,omitempty"`
|
||||||
|
ReferencedTable string `json:"referenced_table" yaml:"referenced_table" xml:"referenced_table"`
|
||||||
|
ReferencedSchema string `json:"referenced_schema" yaml:"referenced_schema" xml:"referenced_schema"`
|
||||||
|
ReferencedColumns []string `json:"referenced_columns" yaml:"referenced_columns" xml:"referenced_columns"`
|
||||||
|
OnDelete string `json:"on_delete" yaml:"on_delete" xml:"on_delete"` // CASCADE, SET NULL, RESTRICT, etc.
|
||||||
|
OnUpdate string `json:"on_update" yaml:"on_update" xml:"on_update"`
|
||||||
|
Deferrable bool `json:"deferrable,omitempty" yaml:"deferrable,omitempty" xml:"deferrable,omitempty"`
|
||||||
|
InitiallyDeferred bool `json:"initially_deferred,omitempty" yaml:"initially_deferred,omitempty" xml:"initially_deferred,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConstraintType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrimaryKeyConstraint ConstraintType = "primary_key"
|
||||||
|
ForeignKeyConstraint ConstraintType = "foreign_Key"
|
||||||
|
UniqueConstraint ConstraintType = "unique"
|
||||||
|
CheckConstraint ConstraintType = "check"
|
||||||
|
NotNullConstraint ConstraintType = "not_null"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Script struct {
|
||||||
|
Name string `json:"name" yaml:"name" xml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description" xml:"description"`
|
||||||
|
SQL string `json:"sql" yaml:"sql" xml:"sql"`
|
||||||
|
Rollback string `json:"rollback,omitempty" yaml:"rollback,omitempty" xml:"rollback,omitempty"`
|
||||||
|
RunAfter []string `json:"run_after,omitempty" yaml:"run_after,omitempty" xml:"run_after,omitempty"`
|
||||||
|
Schema string `json:"schema,omitempty" yaml:"schema,omitempty" xml:"schema,omitempty"`
|
||||||
|
Version string `json:"version,omitempty" yaml:"version,omitempty" xml:"version,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty" yaml:"priority,omitempty" xml:"priority,omitempty"`
|
||||||
|
}
|
||||||
24
pkg/readers/reader.go
Normal file
24
pkg/readers/reader.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package readers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reader defines the interface for reading database specifications
|
||||||
|
// from various input formats
|
||||||
|
type Reader interface {
|
||||||
|
// Read reads and parses the input, returning a Database model
|
||||||
|
Read() (*models.Database, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReaderOptions contains common options for readers
|
||||||
|
type ReaderOptions struct {
|
||||||
|
// FilePath is the path to the input file (if applicable)
|
||||||
|
FilePath string
|
||||||
|
|
||||||
|
// ConnectionString is the database connection string (for DB readers)
|
||||||
|
ConnectionString string
|
||||||
|
|
||||||
|
// Additional options can be added here as needed
|
||||||
|
Metadata map[string]interface{}
|
||||||
|
}
|
||||||
32
pkg/transform/transform.go
Normal file
32
pkg/transform/transform.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package transform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transformer provides utilities for transforming database models
|
||||||
|
type Transformer struct{}
|
||||||
|
|
||||||
|
// NewTransformer creates a new Transformer instance
|
||||||
|
func NewTransformer() *Transformer {
|
||||||
|
return &Transformer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates a database model for correctness
|
||||||
|
func (t *Transformer) Validate(db *models.Database) error {
|
||||||
|
// TODO: Implement validation logic
|
||||||
|
// - Check for duplicate table names
|
||||||
|
// - Validate column types
|
||||||
|
// - Ensure foreign keys reference existing tables/columns
|
||||||
|
// - Validate relation integrity
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize normalizes a database model to a standard format
|
||||||
|
func (t *Transformer) Normalize(db *models.Database) (*models.Database, error) {
|
||||||
|
// TODO: Implement normalization logic
|
||||||
|
// - Standardize naming conventions
|
||||||
|
// - Order tables/columns consistently
|
||||||
|
// - Apply default values
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
24
pkg/writers/writer.go
Normal file
24
pkg/writers/writer.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Writer defines the interface for writing database specifications
|
||||||
|
// to various output formats
|
||||||
|
type Writer interface {
|
||||||
|
// Write takes a Database model and writes it to the desired format
|
||||||
|
Write(db *models.Database) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriterOptions contains common options for writers
|
||||||
|
type WriterOptions struct {
|
||||||
|
// OutputPath is the path where the output should be written
|
||||||
|
OutputPath string
|
||||||
|
|
||||||
|
// PackageName is the Go package name (for code generation)
|
||||||
|
PackageName string
|
||||||
|
|
||||||
|
// Additional options can be added here as needed
|
||||||
|
Metadata map[string]interface{}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user