enginefile

package
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package enginefile provides a file-based CRUD engine for r3.

It implements the r3.CRUD interface by reading and writing entities to JSON or YAML files on disk. The engine supports pluggable codecs for serialization, in-memory filtering/sorting/pagination, and two file layout modes:

  • 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]()),
)

The engine supports all r3 query features: filters, sorts, pagination, field selection, soft-delete, and default query merging.

Codecs can be customized to use alternative JSON libraries:

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) },
)

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 returns r3.Querier, providing a compile-time guarantee that the caller cannot perform write operations.

Internally this constructs a full BaseCRUD but only exposes the Querier interface (Get, List).

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 new file-based CRUD instance. An IDGenerator must be provided explicitly.

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 over the entities matching the query, evaluated 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 creates a new encoder writing to w.
	NewEncoder(w io.Writer) Encoder
	// NewDecoder creates a new decoder reading from r.
	NewDecoder(r io.Reader) Decoder
	// FileExtension returns the file extension including the dot (e.g. ".json", ".yaml").
	FileExtension() string
}

Codec handles serialization of entity slices to/from files. Implementations work with io.Reader/io.Writer for streaming I/O.

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 creates a custom Codec from user-provided encoder/decoder factories. This allows plugging in alternative JSON libraries (e.g. goccy/go-json, json-iterator, d3rty/json) or any other serialization 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 generates new unique IDs for entities. The Generate method receives the list of existing IDs so that implementations 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 that generates UUID v4 strings. This uses crypto/rand and does not depend on any external UUID library.

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 for file storage. Each entity type will be stored in a file (or subdirectory) under this path.

func WithCodec

func WithCodec(codec Codec) Option

WithCodec sets the codec used for serialization. Use JSONCodec(), YAMLCodec(), or NewCodec() for a custom codec.

func WithDirectoryMode

func WithDirectoryMode() Option

WithDirectoryMode enables directory mode where each entity is stored in a separate file within a directory named after the resource. For example: cities/1.json, cities/2.json instead of cities.json.

func WithFilePath

func WithFilePath(path string) Option

WithFilePath sets an explicit file path, overriding the auto-derived path from resource name + codec extension. Only valid in single-file mode.

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 resource name and field info from a generic type T.

Tag priority for field names: `r3` tag first, then `json` tag, then `yaml` tag, then `db` tag, then snake_case of the Go field name.

func (*StructMeta) CopyFieldValues

func (m *StructMeta) CopyFieldValues(dst any, 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 any, 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.

Jump to

Keyboard shortcuts

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