adder

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2025 License: MIT Imports: 9 Imported by: 0

README

Adder

🐍 Documentation-driven CLI generator for Cobra based Go applications

Adder generates type-safe CLI commands from markdown documentation, providing a clean separation between command structure and business logic.

Test Lint Release Go Reference Go Report Card Release

✨ Features

  • 📝 Documentation-First - Commands defined in readable markdown
  • 🔒 Type-Safe - Compile-time validation and structured requests
  • 🏗️ Clean Architecture - Separation of CLI structure and business logic
  • ⚡ Performance - No runtime parsing overhead
  • 🎯 Handler Interfaces - Easy testing and dependency injection
  • 📁 Organized Output - Preserves directory structure to avoid naming conflicts
  • 🧪 Comprehensive Testing - Unit, integration, golden file, and example tests
  • 🚀 Production Ready - Full CI/CD pipeline with automated releases
  • 🔧 Self-Dogfooding - Adder generates its own CLI commands

🚀 Quick Start

1. Install

Via Go Install:

go install github.com/jrschumacher/adder/cmd@latest

Via GitHub Releases:

# Download binary for your platform from:
# https://github.com/jrschumacher/adder/releases
2. Define Command

Create docs/man/hello.md:

---
title: Say hello to someone
command:
  name: hello [name]
  arguments:
    - name: name
      description: Name of the person to greet
      required: true
      type: string
  flags:
    - name: capitalize
      description: Capitalize the greeting
      type: bool
---

# Say hello to someone

Greet someone with a friendly hello message.
3. Generate Code
adder generate -i docs/man -o generated -p generated
4. Implement Handler
func (h *HelloHandler) HandleHello(cmd *cobra.Command, req *generated.HelloRequest) error {
    greeting := fmt.Sprintf("Hello, %s!", req.Arguments.Name)
    if req.Flags.Capitalize {
        greeting = strings.ToUpper(greeting)
    }
    fmt.Println(greeting)
    return nil
}
5. Wire It Up
handler := &HelloHandler{}
helloCmd := generated.NewHelloCommand(handler)
rootCmd.AddCommand(helloCmd)

🏗️ Generated Structure

Adder creates clean, type-safe structures:

// Separate arguments from flags for clarity
type HelloRequestArguments struct {
    Name string `json:"name" validate:"required"`
}

type HelloRequestFlags struct {
    Capitalize bool `json:"capitalize"`
}

type HelloRequest struct {
    Arguments HelloRequestArguments `json:"arguments"`
    Flags     HelloRequestFlags     `json:"flags"`
}

// Handler receives full command access
type HelloHandler interface {
    HandleHello(cmd *cobra.Command, req *HelloRequest) error
}

📁 Directory Organization

Adder preserves your documentation structure:

docs/man/              generated/
├── auth/              ├── auth/
│   └── login.md  →    │   └── login_generated.go
└── policy/            └── policy/
    └── create.md  →       └── create_generated.go

This prevents naming conflicts between commands like auth create and policy create.

🎯 Key Benefits

Feature Benefit
Type Safety Compile-time validation prevents runtime errors
Documentation-First Single source of truth in readable format
Performance No runtime parsing of embedded docs
Clean Architecture Handler interfaces promote testability
Organized Output Directory structure prevents naming conflicts
Command Access Full *cobra.Command capabilities available

🏆 Production Ready

Adder is built with production use in mind:

✅ Quality Assurance
  • 19 Active Linters - golangci-lint with comprehensive checks
  • 4 Test Categories - Unit, integration, golden file, and example tests
  • 90%+ Test Coverage - Comprehensive test suite
  • Automated Quality Gates - CI/CD pipeline enforces standards
✅ Reliability
  • Multi-Platform Support - Linux, macOS, Windows (AMD64 + ARM64)
  • Semantic Versioning - Automated releases with conventional commits
  • Backward Compatibility - Careful API evolution
✅ Developer Experience
  • Self-Dogfooding - Tool generates its own CLI commands
  • Comprehensive Documentation - Examples, guides, and API reference
  • Local Development Tools - Makefile with all common tasks
  • IDE Integration - Works with any Go-compatible editor

🧪 Testing

Adder includes comprehensive testing:

# Run all tests
make test

# Run with coverage
make test-coverage

# Run linting
make lint

# Run all CI checks locally
make ci-test

Test Categories:

  • Unit Tests - Core parser and generator logic
  • Integration Tests - CLI command testing
  • Golden File Tests - Generated code validation
  • Example Tests - Handler testing patterns

📚 Documentation

🏗️ Development

Local Development:

# Build the CLI
make build

# Build for all platforms (uses GitHub Actions)
make build-all

# Generate example commands
make generate-example

# Self-generate CLI commands (dogfooding)
make generate-self

🎬 Example Output

$ hello-example hello "Adder" --capitalize
HELLO, ADDER!

$ adder generate --input docs/man --output generated --package generated
🐍 Generating CLI commands from docs/man to generated...
🔍 Validating documentation...
✅ Code generation completed!
📊 Generated 3 commands with 5 flags and 2 arguments

🚀 Release Process

Adder uses automated releases with release-please and GoReleaser:

  1. Merge to main triggers release PR creation
  2. Merge release PR creates GitHub release
  3. GoReleaser automatically builds and publishes multi-platform binaries

Use conventional commits for automatic versioning:

  • feat: → minor version bump
  • fix: → patch version bump
  • BREAKING CHANGE: → major version bump

🤝 Contributing

We welcome contributions!

Getting Started:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Run make ci-test to validate
  5. Submit a pull request

Code Quality:

  • All tests must pass
  • golangci-lint must pass
  • Include tests for new features
  • Update documentation as needed

📄 License

MIT License - see LICENSE file for details.

Documentation

Overview

Package adder provides a documentation-driven CLI generator for Go applications. It processes markdown files with YAML frontmatter to generate type-safe command interfaces, request/response structures, and handler interfaces for Cobra CLI applications.

Index

Constants

View Source
const (
	TypeString      = "string"
	TypeBool        = "bool"
	TypeInt         = "int"
	TypeStringArray = "stringArray"
	NilValue        = "nil"
)

Type constants for common data types

Variables

View Source
var Templates = struct {
	Command string
	Package string
}{
	Command: commandTemplate,
	Package: packageTemplate,
}

Templates contains all code generation templates

Functions

func GenerateFromDirectory

func GenerateFromDirectory(_, inputDir string) error

GenerateFromDirectory is a convenience function to generate from a directory path

func TemplateFunctions

func TemplateFunctions() template.FuncMap

TemplateFunctions returns the template functions

func Version

func Version() string

Version returns the version of the adder package

Types

type Adder

type Adder struct {
	// contains filtered or unexported fields
}

Adder is the main interface for the code generator

func New

func New(config *Config) *Adder

New creates a new Adder instance with the given configuration

func NewWithDefaults

func NewWithDefaults() *Adder

NewWithDefaults creates a new Adder instance with default configuration

func (*Adder) Clean

func (a *Adder) Clean() error

Clean removes all generated files

func (*Adder) Generate

func (a *Adder) Generate() error

Generate processes the input directory and generates command stubs

func (*Adder) GenerateFromFS

func (a *Adder) GenerateFromFS(ctx context.Context, inputFS fs.FS) error

GenerateFromFS generates code from the provided filesystem

func (*Adder) GenerateWithContext

func (a *Adder) GenerateWithContext(ctx context.Context) error

GenerateWithContext processes the input directory and generates command stubs with context

func (*Adder) GetCommand

func (a *Adder) GetCommand(name string) *Command

GetCommand returns a specific command by name

func (*Adder) GetCommands

func (a *Adder) GetCommands() []*Command

GetCommands returns all parsed commands (requires Generate to be called first)

func (*Adder) GetConfig

func (a *Adder) GetConfig() *Config

GetConfig returns the current configuration

func (*Adder) GetStats

func (a *Adder) GetStats() map[string]int

GetStats returns generation statistics

func (*Adder) SetConfig

func (a *Adder) SetConfig(config *Config)

SetConfig updates the configuration

func (*Adder) Validate

func (a *Adder) Validate() error

Validate validates the configuration and input files

type Argument

type Argument struct {
	Name        string `yaml:"name"`
	Description string `yaml:"description"`
	Required    bool   `yaml:"required"`
	Type        string `yaml:"type"`
}

Argument represents a command argument

func (*Argument) GetGoType

func (a *Argument) GetGoType() string

GetGoType returns the Go type for the argument

func (*Argument) GetValidationTag

func (a *Argument) GetValidationTag() string

GetValidationTag returns the validation tag for the argument

type Command

type Command struct {
	Title       string     `yaml:"title"`
	Name        string     `yaml:"name"`
	Aliases     []string   `yaml:"aliases"`
	Hidden      bool       `yaml:"hidden"`
	Arguments   []Argument `yaml:"arguments"`
	Flags       []Flag     `yaml:"flags"`
	Description string     // Markdown content
	FilePath    string     // Source file path
}

Command represents a command definition from markdown

type Config

type Config struct {
	InputDir   string `yaml:"input_dir"`
	OutputDir  string `yaml:"output_dir"`
	Package    string `yaml:"package"`
	FileSuffix string `yaml:"file_suffix"`
}

Config represents the generator configuration

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the default configuration

type Flag

type Flag struct {
	Name        string      `yaml:"name"`
	Shorthand   string      `yaml:"shorthand"`
	Description string      `yaml:"description"`
	Type        string      `yaml:"type"`
	Default     interface{} `yaml:"default"`
	Required    bool        `yaml:"required"`
	Enum        []string    `yaml:"enum"`
}

Flag represents a command flag

func (*Flag) GetCobraFlagMethod

func (f *Flag) GetCobraFlagMethod() string

GetCobraFlagMethod returns the cobra flag method name

func (*Flag) GetCobraFlagMethodP

func (f *Flag) GetCobraFlagMethodP() string

GetCobraFlagMethodP returns the cobra flag method name with shorthand

func (*Flag) GetDefaultValue

func (f *Flag) GetDefaultValue() string

GetDefaultValue returns the default value as a Go literal

func (*Flag) GetGoType

func (f *Flag) GetGoType() string

GetGoType returns the Go type for the flag

func (*Flag) GetValidationTag

func (f *Flag) GetValidationTag() string

GetValidationTag returns the validation tag for the field

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Generator handles code generation from parsed commands

func NewGenerator

func NewGenerator(config *Config) *Generator

NewGenerator creates a new generator instance

func (*Generator) Generate

func (g *Generator) Generate(_ context.Context, inputFS fs.FS) error

Generate processes the input directory and generates code

func (*Generator) GetCommand

func (g *Generator) GetCommand(name string) *Command

GetCommand returns a command by name

func (*Generator) GetStats

func (g *Generator) GetStats() map[string]int

GetStats returns generation statistics

func (*Generator) ListCommands

func (g *Generator) ListCommands() []*Command

ListCommands returns a list of all parsed commands

func (*Generator) ValidateCommands

func (g *Generator) ValidateCommands() error

ValidateCommands validates all parsed commands

type GeneratorInterface

type GeneratorInterface interface {
	Generate(ctx context.Context) error
}

GeneratorInterface defines the interface for code generators

type HandlerInterface

type HandlerInterface struct {
	Name    string
	Methods []HandlerMethod
}

HandlerInterface represents a handler interface to be generated

type HandlerMethod

type HandlerMethod struct {
	Name        string
	RequestType string
}

HandlerMethod represents a method in a handler interface

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser handles parsing markdown files with YAML frontmatter

func NewParser

func NewParser(config *Config) *Parser

NewParser creates a new parser instance

func (*Parser) GetCommandPath

func (p *Parser) GetCommandPath(filePath string) string

GetCommandPath returns the command path from the file path

func (*Parser) GetFunctionName

func (p *Parser) GetFunctionName(cmd *Command) string

GetFunctionName returns the command constructor function name

func (*Parser) GetHandlerName

func (p *Parser) GetHandlerName(cmd *Command) string

GetHandlerName returns the handler interface name for the command

func (*Parser) GetMethodName

func (p *Parser) GetMethodName(cmd *Command) string

GetMethodName returns the handler method name for the command

func (*Parser) GetPackageName

func (p *Parser) GetPackageName(_ *Command) string

GetPackageName returns the Go package name for the command

func (*Parser) GetStructName

func (p *Parser) GetStructName(cmd *Command) string

GetStructName returns the request struct name for the command

func (*Parser) ParseContent

func (p *Parser) ParseContent(content, filePath string) (*Command, error)

ParseContent parses markdown content with YAML frontmatter

func (*Parser) ParseDirectory

func (p *Parser) ParseDirectory(fsys fs.FS) ([]*Command, error)

ParseDirectory parses all markdown files in the input directory

func (*Parser) ParseFile

func (p *Parser) ParseFile(fsys fs.FS, path string) (*Command, error)

ParseFile parses a single markdown file

Directories

Path Synopsis
cmd
Package main provides the command handlers for the adder CLI application.
Package main provides the command handlers for the adder CLI application.
example module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址