spellcheck

package module
v0.2.10 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2026 License: MIT Imports: 13 Imported by: 0

README

GoREST Spellcheck Plugin

CI Go Report Card Coverage License

Automatic spelling validation middleware and on-demand spell checking for GoREST applications.

Features

  • Automatic Middleware Validation - Validates request bodies for spelling errors before they reach your handlers
  • On-Demand Spell Checking - HTTP endpoint for explicit spell checking with detailed error reports
  • Struct Tag Annotations - Use spellcheck:"true" tags to mark fields for validation
  • Pure Go Implementation - Uses github.com/sajari/fuzzy library (no external dependencies)
  • Configurable - Customize ignored words, max text length, suggestions, and more
  • Built-in Dictionary - 500+ common English words including tech terms (API, JSON, HTTP, etc.)
  • Custom Dictionaries - Load your own dictionary files
  • Thread-Safe - Caches struct metadata for optimal performance
  • Comprehensive Testing - 82.6% test coverage with integration tests

Installation

go get github.com/nicolasbonnici/gorest-spellcheck

Quick Start

With GoREST Framework
package main

import (
	"github.com/nicolasbonnici/gorest"
	"github.com/nicolasbonnici/gorest/pluginloader"
	spellcheck "github.com/nicolasbonnici/gorest-spellcheck"
)

func init() {
	pluginloader.RegisterPluginFactory("spellcheck", spellcheck.NewPlugin)
}

func main() {
	gorest.Start(gorest.Config{
		ConfigPath: ".",
	})
}
Standalone Usage
package main

import (
	"log"

	"github.com/gofiber/fiber/v2"
	spellcheck "github.com/nicolasbonnici/gorest-spellcheck"
)

func main() {
	app := fiber.New()

	// Initialize plugin
	plugin := spellcheck.NewPlugin()
	config := map[string]interface{}{
		"enabled":          true,
		"default_language": "en",
		"max_text_length":  10000,
	}

	if err := plugin.Initialize(config); err != nil {
		log.Fatal(err)
	}

	// Apply middleware
	app.Use(plugin.Handler())

	// Setup spellcheck endpoint
	spellcheckPlugin := plugin.(*spellcheck.SpellcheckPlugin)
	spellcheckPlugin.SetupEndpoints(app)

	// Your routes here
	app.Post("/articles", createArticle)

	app.Listen(":3000")
}

Configuration

Add to your gorest.yaml:

plugins:
  - name: spellcheck
    enabled: true
    config:
      default_language: "en"
      max_text_length: 10000      # Maximum text length to check
      max_suggestions: 5           # Max suggestions per error
      min_word_length: 2           # Minimum word length to check
      case_sensitive: false        # Case-sensitive checking
      ignored_words:               # Custom words to ignore
        - "API"
        - "JSON"
        - "HTTP"
        - "UUID"
      custom_dictionary: "/path/to/dictionary.txt"  # Optional custom dictionary
Configuration Options
Option Type Default Range Description
enabled bool true - Enable/disable the plugin
default_language string "en" - Default language for spell checking
supported_languages []string ["en"] - List of supported languages
max_text_length int 10000 1-1,000,000 Maximum text length to validate
max_suggestions int 5 1-20 Maximum suggestions per error
min_word_length int 2 1-10 Minimum word length to check
case_sensitive bool false - Enable case-sensitive checking
ignored_words []string [] - Words to always treat as correct
custom_dictionary string "" - Path to custom dictionary file

Usage

1. Automatic Middleware Validation

The middleware automatically validates POST/PUT/PATCH requests with JSON bodies:

type Article struct {
	Title   string `json:"title" spellcheck:"true"`
	Content string `json:"content" spellcheck:"true"`
	Author  string `json:"author"` // Not checked
}

Request with spelling errors:

POST /api/articles
Content-Type: application/json

{
  "title": "Teh quik brown fox",
  "content": "This has speling erors"
}

Response (400 Bad Request):

{
  "error": "Spelling errors found",
  "errors": [
    {
      "field": "title",
      "word": "Teh",
      "position": 0,
      "suggestions": ["The", "Tea", "Ten"]
    },
    {
      "field": "title",
      "word": "quik",
      "position": 4,
      "suggestions": ["quick", "quit", "quiz"]
    },
    {
      "field": "content",
      "word": "speling",
      "position": 9,
      "suggestions": ["spelling", "spieling"]
    },
    {
      "field": "content",
      "word": "erors",
      "position": 17,
      "suggestions": ["errors", "eros"]
    }
  ]
}
2. On-Demand Spell Checking

Use the /api/spellcheck endpoint for explicit validation:

POST /api/spellcheck
Content-Type: application/json

{
  "text": "Teh quik brown fox jumps over the lazy dog",
  "language": "en",
  "context": ["API", "JSON"],
  "options": {
    "case_sensitive": false,
    "max_suggestions": 3
  }
}

Response (400 Bad Request):

{
  "valid": false,
  "errors": [
    {
      "word": "Teh",
      "position": 0,
      "suggestions": ["The", "Tea", "Ten"]
    },
    {
      "word": "quik",
      "position": 4,
      "suggestions": ["quick", "quit", "quiz"]
    }
  ],
  "suggestions": {
    "Teh": ["The", "Tea", "Ten"],
    "quik": ["quick", "quit", "quiz"]
  },
  "text": "Teh quik brown fox jumps over the lazy dog"
}

Response with correct spelling (200 OK):

{
  "valid": true,
  "errors": [],
  "text": "The quick brown fox jumps over the lazy dog"
}
3. Manual Struct Validation
middleware, _ := spellcheck.NewMiddleware(&config, spellchecker)

article := &Article{
	Title:   "Teh test",
	Content: "This has erors",
}

errors, err := middleware.ValidateStruct(article)
if err != nil {
	log.Fatal(err)
}

if errors.HasErrors() {
	for _, e := range errors.Errors {
		fmt.Printf("Field %s: '%s' at position %d, suggestions: %v\n",
			e.Field, e.Word, e.Position, e.Suggestions)
	}
}

Field Checking Behavior

With Struct Tags

When using ValidateStruct(), only fields marked with spellcheck:"true" are validated:

type Article struct {
	Title   string `json:"title" spellcheck:"true"`   // ✓ Checked
	Content string `json:"content" spellcheck:"true"` // ✓ Checked
	Slug    string `json:"slug"`                      // ✗ Not checked
}
With Middleware (JSON Requests)

The middleware uses a heuristic based on common field names:

Checked fields: title, content, description, body, text, message, comment, note, summary, excerpt, caption, bio, about

Not checked: id, slug, email, password, username, etc.

{
  "title": "Test article",    // ✓ Checked
  "content": "Body text",      // ✓ Checked
  "slug": "test-article",      // ✗ Not checked
  "email": "user@example.com"  // ✗ Not checked
}

Custom Dictionary

Create a text file with one word per line:

# Custom dictionary
customword
techterm
brandname

Configure in gorest.yaml:

plugins:
  - name: spellcheck
    config:
      custom_dictionary: "./custom-words.txt"

Error Types

The plugin provides detailed error types:

// SpellingError - Single word error
type SpellingError struct {
	Field       string   // Field name (empty for on-demand checks)
	Word        string   // Misspelled word
	Position    int      // Character position in text
	Suggestions []string // Correction suggestions
}

// SpellingErrors - Collection of errors
type SpellingErrors struct {
	Errors []*SpellingError
}

// TextTooLongError - Text exceeds max length
type TextTooLongError struct {
	Length    int
	MaxLength int
}

// UnsupportedLanguageError - Language not supported
type UnsupportedLanguageError struct {
	Language           string
	SupportedLanguages []string
}

// ValidationError - Request validation failed
type ValidationError struct {
	Message string
	Fields  map[string]string
}

Development

Prerequisites
  • Go 1.23+ or later
  • Make (optional)
Setup
# Clone and install dependencies
git clone https://github.com/nicolasbonnici/gorest-spellcheck.git
cd gorest-spellcheck
make install  # Installs golangci-lint and git hooks

# Run tests
make test

# Run tests with coverage
make test-coverage

# Run linter
make lint
Available Make Targets
make install        # Install development tools and git hooks
make test           # Run tests with race detector
make test-coverage  # Generate HTML coverage report
make lint           # Run linter
make lint-fix       # Run linter with auto-fix
make build          # Verify build
make audit          # Run all quality checks
make clean          # Clean artifacts
make all            # Run lint, test, and build
Running Tests
# All tests
go test -v ./...

# Specific test
go test -v -run TestSpellchecker_Check

# With coverage
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
go tool cover -html=coverage.out

# Integration tests
go test -v -run TestPluginIntegration

Architecture

gorest-spellcheck/
├── plugin.go              # Plugin interface implementation
├── config.go              # Configuration with validation
├── models.go              # Request/Response structs
├── errors.go              # Custom error types
├── spellchecker.go        # Core spell checking logic
├── tag_parser.go          # Struct tag parsing with caching
├── spellcheck_middleware.go # Automatic validation middleware
├── handlers.go            # HTTP endpoint handler
├── *_test.go              # Unit tests (82.6% coverage)
└── examples/              # Usage examples
Key Components
  • Spellchecker - Core logic using github.com/sajari/fuzzy
  • TagParser - Thread-safe reflection-based tag parsing with caching
  • Middleware - Fiber middleware for automatic validation
  • Handler - HTTP endpoint for on-demand checking
  • Plugin - GoREST plugin interface implementation

Performance Considerations

  • Tag Parsing Caching - Struct metadata is parsed once and cached per type
  • Concurrent Access - Uses sync.Map for thread-safe caching
  • Dictionary Loading - Dictionary loaded once at initialization
  • Request Filtering - Only validates POST/PUT/PATCH with JSON content
  • Fast Word Matching - Uses Levenshtein distance with configurable depth

Security

  • Text Length Limits - Prevents resource exhaustion with max_text_length
  • Input Sanitization - Skips numbers and special characters
  • No Sensitive Data - Never logs or exposes checked text content
  • Configurable Validation - Can be disabled per environment

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass and coverage remains high
  5. Run linter (make lint)
  6. Submit a pull request

Troubleshooting

Common words flagged as errors

The built-in dictionary contains 500+ common words. If you encounter false positives, add words to ignored_words or use a custom dictionary.

Performance issues with large texts

Set max_text_length appropriately for your use case. The default 10,000 characters is suitable for most applications.

Middleware not validating

Ensure:

  • Plugin is enabled in configuration
  • Content-Type is application/json
  • Request method is POST, PUT, or PATCH
  • Field names match the heuristic or struct tags are used

License

MIT License - See LICENSE file for details.

Support

Changelog

v0.1.0 (2026-03-03)
  • Initial release
  • Automatic middleware validation
  • On-demand spell checking endpoint
  • Struct tag annotations
  • Built-in English dictionary (500+ words)
  • Custom dictionary support
  • Thread-safe tag parsing with caching
  • 82.6% test coverage
  • Comprehensive integration tests

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewPlugin

func NewPlugin() plugin.Plugin

Types

type CheckOptions

type CheckOptions struct {
	CaseSensitive  *bool `json:"case_sensitive,omitempty"`
	MaxSuggestions *int  `json:"max_suggestions,omitempty"`
}

CheckOptions contains optional parameters for spell checking

type CheckRequest

type CheckRequest struct {
	Text     string        `json:"text" validate:"required"`
	Language string        `json:"language,omitempty"`
	Context  []string      `json:"context,omitempty"` // Additional words to ignore for this request
	Options  *CheckOptions `json:"options,omitempty"`
}

CheckRequest represents a request to check spelling

func (*CheckRequest) Validate

func (r *CheckRequest) Validate() error

Validate validates the CheckRequest

type CheckResponse

type CheckResponse struct {
	Valid       bool                `json:"valid"`
	Errors      []*SpellingError    `json:"errors,omitempty"`
	Suggestions map[string][]string `json:"suggestions,omitempty"`
	Text        string              `json:"text,omitempty"`
}

CheckResponse represents the response from spell checking

type Config

type Config struct {
	Enabled            bool
	DefaultLanguage    string
	SupportedLanguages []string
	MaxTextLength      int
	MaxSuggestions     int
	IgnoredWords       []string
	CustomDictionary   string
	CaseSensitive      bool
	MinWordLength      int
}

func DefaultConfig

func DefaultConfig() Config

func (*Config) Validate

func (c *Config) Validate() error

type FieldInfo

type FieldInfo struct {
	Name          string // Go field name
	JSONName      string // JSON tag name
	SpellCheck    bool   // Whether to spellcheck this field
	IsStringField bool   // Whether the field is a string type
}

FieldInfo contains metadata about a struct field for spellchecking

type Handler

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

Handler handles HTTP requests for the spellcheck endpoint

func NewHandler

func NewHandler(config *Config, spellchecker *Spellchecker) *Handler

NewHandler creates a new Handler instance

func (*Handler) Check

func (h *Handler) Check(c fiber.Ctx) error

Check handles POST /api/spellcheck requests for on-demand spell checking

type Middleware

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

Middleware returns a Fiber middleware that validates request bodies for spelling errors

func NewMiddleware

func NewMiddleware(config *Config, spellchecker *Spellchecker) (*Middleware, error)

NewMiddleware creates a new Middleware instance

func (*Middleware) IsEnabled

func (m *Middleware) IsEnabled() bool

Enable/disable middleware at runtime

func (*Middleware) Validate

func (m *Middleware) Validate() fiber.Handler

Validate is the middleware handler that checks spelling in request bodies

func (*Middleware) ValidateStruct

func (m *Middleware) ValidateStruct(v interface{}) (*SpellingErrors, error)

ValidateStruct validates a specific struct instance for spelling errors This can be called manually from handlers if you want explicit validation

type SpellcheckPlugin

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

func (*SpellcheckPlugin) Config

func (p *SpellcheckPlugin) Config() *Config

func (*SpellcheckPlugin) Handler

func (p *SpellcheckPlugin) Handler() fiber.Handler

func (*SpellcheckPlugin) Initialize

func (p *SpellcheckPlugin) Initialize(config map[string]interface{}) error

func (*SpellcheckPlugin) Name

func (p *SpellcheckPlugin) Name() string

func (*SpellcheckPlugin) SetupEndpoints

func (p *SpellcheckPlugin) SetupEndpoints(app *fiber.App) error

type Spellchecker

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

Spellchecker provides spell checking functionality using fuzzy matching

func NewSpellchecker

func NewSpellchecker(config *Config) (*Spellchecker, error)

NewSpellchecker creates a new spellchecker with the given configuration

func (*Spellchecker) Check

func (s *Spellchecker) Check(text string) (*SpellingErrors, error)

Check checks the spelling of the given text and returns any errors found

func (*Spellchecker) CheckField

func (s *Spellchecker) CheckField(fieldName, text string) (*SpellingErrors, error)

CheckField checks the spelling of text in a specific field (for middleware)

type SpellingError

type SpellingError struct {
	Field       string   `json:"field,omitempty"`       // Field name (for middleware validation)
	Word        string   `json:"word"`                  // Misspelled word
	Position    int      `json:"position"`              // Position in text
	Suggestions []string `json:"suggestions,omitempty"` // Suggested corrections
}

SpellingError represents a single spelling error with suggestions

func (*SpellingError) Error

func (e *SpellingError) Error() string

type SpellingErrors

type SpellingErrors struct {
	Errors []*SpellingError `json:"errors"`
}

SpellingErrors is a collection of spelling errors

func (*SpellingErrors) Add

func (e *SpellingErrors) Add(err *SpellingError)

func (*SpellingErrors) Error

func (e *SpellingErrors) Error() string

func (*SpellingErrors) HasErrors

func (e *SpellingErrors) HasErrors() bool

type TagParser

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

TagParser parses struct tags and caches the results

func NewTagParser

func NewTagParser() *TagParser

NewTagParser creates a new TagParser

func (*TagParser) CacheSize

func (p *TagParser) CacheSize() int

CacheSize returns the approximate number of types cached Note: This is O(n) as sync.Map doesn't expose size directly

func (*TagParser) ClearCache

func (p *TagParser) ClearCache()

ClearCache clears the tag parser cache Useful for testing or if types are redefined at runtime

func (*TagParser) ExtractFieldsFromMap

func (p *TagParser) ExtractFieldsFromMap(data map[string]interface{}, fieldInfos []FieldInfo) map[string]string

ExtractFieldsFromMap extracts fields from a map[string]interface{} This is useful for checking JSON request bodies before they're unmarshaled

func (*TagParser) GetFieldValue

func (p *TagParser) GetFieldValue(v interface{}, fieldName string) (string, bool)

GetFieldValue extracts the string value of a field from a struct

func (*TagParser) Parse

func (p *TagParser) Parse(v interface{}) []FieldInfo

Parse extracts spellcheck metadata from a struct Returns a slice of FieldInfo for fields that should be spellchecked

func (*TagParser) ParseValue

func (p *TagParser) ParseValue(v reflect.Value) []FieldInfo

ParseValue extracts spellcheck metadata from a reflect.Value This is useful when you already have a reflect.Value

type TextTooLongError

type TextTooLongError struct {
	Length    int
	MaxLength int
}

TextTooLongError indicates text exceeds maximum length

func (*TextTooLongError) Error

func (e *TextTooLongError) Error() string

type UnsupportedLanguageError

type UnsupportedLanguageError struct {
	Language           string
	SupportedLanguages []string
}

UnsupportedLanguageError indicates requested language is not supported

func (*UnsupportedLanguageError) Error

func (e *UnsupportedLanguageError) Error() string

type ValidationError

type ValidationError struct {
	Message string
	Details map[string]string
}

ValidationError represents errors during request validation

func NewValidationError

func NewValidationError(message string, details map[string]string) *ValidationError

func (*ValidationError) Error

func (e *ValidationError) Error() string

Jump to

Keyboard shortcuts

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