enginefile

package
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package enginefile is the filesystem CRUD engine: it reads and writes entities to JSON or YAML files and evaluates every query feature (filters, sorts, pagination, field selection, soft-delete) in memory. Serialization goes through a pluggable Codec; two layout modes are supported:

  • Single-file mode (default): one file per collection (e.g. cities.json)
  • Directory mode: one file per entity (e.g. cities/1.json, cities/2.json)

Usage:

repo, err := enginefile.New[City, int](
    enginefile.WithBaseDir("./data"),
    enginefile.WithCodec(enginefile.JSONCodec()),
    enginefile.WithIDGenerator(enginefile.IncrementIDGen[int]()),
)

A relation is declared by an r3 "rel:" tag (or a gorm association tag), never inferred from the Go type: an untagged slice, map or nested struct is a native JSON/YAML value, so it is an ordinary field of the stored record.

Codecs can wrap an alternative JSON library via NewCodec.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsNotFound

func IsNotFound(err error) bool

IsNotFound returns true if the error indicates that an entity was not found.

func NewQuerier

func NewQuerier[T any, ID comparable](idGen IDGenerator[ID], opts ...Option) (r3.Querier[T, ID], error)

NewQuerier creates a read-only file-based repository: it builds a full BaseCRUD but returns only r3.Querier, so callers cannot write.

Types

type BaseCRUD

type BaseCRUD[T any, ID comparable] struct {
	r3.DefaultsManager

	Meta   StructMeta
	Config r3.Config
	// contains filtered or unexported fields
}

BaseCRUD implements r3.CRUD for file-based storage.

func New

func New[T any, ID comparable](idGen IDGenerator[ID], opts ...Option) (*BaseCRUD[T, ID], error)

New creates a file-based CRUD instance. An IDGenerator must be provided.

func (*BaseCRUD[T, ID]) Aggregate added in v0.0.3

func (r *BaseCRUD[T, ID]) Aggregate(_ context.Context, qarg ...r3.Query) ([]r3.AggregateRow, error)

Aggregate computes grouped aggregates in memory. See r3.Aggregator for the query semantics.

func (*BaseCRUD[T, ID]) Count

func (r *BaseCRUD[T, ID]) Count(_ context.Context, qarg ...r3.Query) (int64, error)

Count returns the number of entities matching the query's filters.

func (*BaseCRUD[T, ID]) Create

func (r *BaseCRUD[T, ID]) Create(_ context.Context, entity T) (T, error)

Create inserts a new entity, assigning a generated ID.

func (*BaseCRUD[T, ID]) Delete

func (r *BaseCRUD[T, ID]) Delete(_ context.Context, id ID) error

Delete removes an entity by ID. Uses soft-delete if configured.

func (*BaseCRUD[T, ID]) Get

func (r *BaseCRUD[T, ID]) Get(_ context.Context, id ID, qarg ...r3.Query) (T, error)

Get retrieves an entity by its primary key.

func (*BaseCRUD[T, ID]) HardDelete

func (r *BaseCRUD[T, ID]) HardDelete(_ context.Context, id ID) error

HardDelete permanently removes an entity regardless of soft-delete configuration.

func (*BaseCRUD[T, ID]) List

func (r *BaseCRUD[T, ID]) List(_ context.Context, qarg ...r3.Query) ([]T, int64, error)

List retrieves entities matching the given query.

func (*BaseCRUD[T, ID]) Patch

func (r *BaseCRUD[T, ID]) Patch(_ context.Context, entity T, fields r3.Fields) (T, error)

Patch performs a partial update, modifying only the specified fields.

func (*BaseCRUD[T, ID]) Restore

func (r *BaseCRUD[T, ID]) Restore(_ context.Context, id ID) error

Restore un-deletes a soft-deleted entity by clearing its soft-delete field.

func (*BaseCRUD[T, ID]) Update

func (r *BaseCRUD[T, ID]) Update(_ context.Context, entity T) (T, error)

Update replaces an existing entity entirely.

type Codec

type Codec interface {
	// NewEncoder returns an encoder writing to w.
	NewEncoder(w io.Writer) Encoder
	// NewDecoder returns a decoder reading from r.
	NewDecoder(r io.Reader) Decoder
	// FileExtension returns the extension including the dot (e.g. ".json").
	FileExtension() string
}

Codec serializes entity slices to and from files over streaming io.Reader/io.Writer.

func JSONCodec

func JSONCodec() Codec

JSONCodec returns a Codec that uses encoding/json with pretty-printed output.

func JSONCodecCompact

func JSONCodecCompact() Codec

JSONCodecCompact returns a Codec that uses encoding/json without indentation.

func NewCodec

func NewCodec(ext string, newEncoder func(w io.Writer) Encoder, newDecoder func(r io.Reader) Decoder) Codec

NewCodec builds a custom Codec from encoder/decoder factories, for plugging in an alternative JSON library (goccy/go-json, json-iterator, ...) or any other format.

Example with goccy/go-json:

import goccy "github.com/goccy/go-json"
codec := enginefile.NewCodec(".json",
    func(w io.Writer) enginefile.Encoder { return goccy.NewEncoder(w) },
    func(r io.Reader) enginefile.Decoder { return goccy.NewDecoder(r) },
)

func YAMLCodec

func YAMLCodec() Codec

YAMLCodec returns a Codec that uses gopkg.in/yaml.v3.

type Decoder

type Decoder interface {
	Decode(v any) error
}

Decoder reads and decodes values from an input stream.

type Encoder

type Encoder interface {
	Encode(v any) error
}

Encoder writes encoded values to an output stream.

type IDGenerator

type IDGenerator[ID comparable] interface {
	Generate(existing []ID) (ID, error)
}

IDGenerator produces a new unique ID. Generate receives the existing IDs so an implementation can avoid collisions (e.g. auto-increment).

func IncrementIDGen

func IncrementIDGen[ID interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}]() IDGenerator[ID]

IncrementIDGen returns an IDGenerator that auto-increments from the highest existing ID.

func UUIDStringIDGen

func UUIDStringIDGen() IDGenerator[string]

UUIDStringIDGen returns an IDGenerator producing UUID v4 strings via crypto/rand (no external UUID dependency).

type IDGeneratorFunc

type IDGeneratorFunc[ID comparable] func(existing []ID) (ID, error)

IDGeneratorFunc is a function adapter for IDGenerator.

func (IDGeneratorFunc[ID]) Generate

func (f IDGeneratorFunc[ID]) Generate(existing []ID) (ID, error)

Generate implements IDGenerator by calling the function.

type Option

type Option func(*config)

Option configures a BaseCRUD instance.

func WithBaseDir

func WithBaseDir(dir string) Option

WithBaseDir sets the base directory; each entity type gets a file (or subdirectory) under it.

func WithCodec

func WithCodec(codec Codec) Option

WithCodec sets the serialization codec (JSONCodec, YAMLCodec, or NewCodec).

func WithDirectoryMode

func WithDirectoryMode() Option

WithDirectoryMode stores each entity in its own file under a resource-named directory (cities/1.json, cities/2.json) instead of a single cities.json.

func WithFilePath

func WithFilePath(path string) Option

WithFilePath overrides the auto-derived path (resource name + codec extension). Single-file mode only.

func WithR3Options

func WithR3Options(opts ...r3.Option) Option

WithR3Options passes framework-level r3.Option values (e.g. r3.WithConfig) to the file-based CRUD constructor.

type StructMeta

type StructMeta struct {
	// ResourceName is the derived collection/resource name (e.g. "cities").
	// Used as the filename stem or directory name.
	ResourceName string

	// Fields holds the storage field names in order (derived from json/yaml/r3/db tags).
	Fields []string
	// FieldIndices holds the corresponding struct field indices for each field.
	FieldIndices []int

	// PKField is the primary key field name (defaults to "id").
	PKField string
	// PKFieldIdx is the index into Fields/FieldIndices for the PK entry.
	PKFieldIdx int

	// SoftDeleteField is the field name used for soft-delete (e.g. "deleted_at").
	// Empty string means soft-delete is not enabled.
	SoftDeleteField string
}

StructMeta holds reflection-based metadata about a struct type T for file storage.

func GetStructMeta

func GetStructMeta[T any]() StructMeta

GetStructMeta derives the resource name and field info for T. Field-name priority: `r3`, then `json`, then `yaml`, then `db`, then snake_case.

func (*StructMeta) CopyFieldValues

func (m *StructMeta) CopyFieldValues(dst, src any, fieldNames []string)

CopyFieldValues copies the specified fields from src to dst (both must be pointers).

func (*StructMeta) GetFieldValue

func (m *StructMeta) GetFieldValue(entity any, fieldName string) (any, bool)

GetFieldValue extracts a field value from an entity by field name (storage name).

func (*StructMeta) PKValue

func (m *StructMeta) PKValue(entity any) any

PKValue extracts the primary key value from an entity.

func (*StructMeta) SetFieldValue

func (m *StructMeta) SetFieldValue(entityPtr any, fieldName string, val any) bool

SetFieldValue sets a field value on an entity (via pointer) by field name.

func (*StructMeta) SetPKValue

func (m *StructMeta) SetPKValue(entityPtr, val any)

SetPKValue sets the primary key value on an entity (via pointer).

func (*StructMeta) SoftDeleteFieldIdx

func (m *StructMeta) SoftDeleteFieldIdx() int

SoftDeleteFieldIdx returns the struct field index for the soft-delete field, or -1.

func (*StructMeta) ValidatePatchFields

func (m *StructMeta) ValidatePatchFields(fields []string) ([]string, error)

ValidatePatchFields checks that the given field names are valid for a Patch operation.

func (*StructMeta) ValidateProjection added in v0.4.0

func (m *StructMeta) ValidateProjection(q r3.Query) error

ValidateProjection checks a query's projection against this type's stored field names. The file engine derives those from `json`/`yaml` tags as often as from `r3`/`db` ones, so the meta - not r3.SchemaOf - is the authority on what a name has to match.

Jump to

Keyboard shortcuts

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