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 ¶
- func IsNotFound(err error) bool
- func NewQuerier[T any, ID comparable](idGen IDGenerator[ID], opts ...Option) (r3.Querier[T, ID], error)
- type BaseCRUD
- func (r *BaseCRUD[T, ID]) Aggregate(_ context.Context, qarg ...r3.Query) ([]r3.AggregateRow, error)
- func (r *BaseCRUD[T, ID]) Count(_ context.Context, qarg ...r3.Query) (int64, error)
- func (r *BaseCRUD[T, ID]) Create(_ context.Context, entity T) (T, error)
- func (r *BaseCRUD[T, ID]) Delete(_ context.Context, id ID) error
- func (r *BaseCRUD[T, ID]) Get(_ context.Context, id ID, qarg ...r3.Query) (T, error)
- func (r *BaseCRUD[T, ID]) HardDelete(_ context.Context, id ID) error
- func (r *BaseCRUD[T, ID]) List(_ context.Context, qarg ...r3.Query) ([]T, int64, error)
- func (r *BaseCRUD[T, ID]) Patch(_ context.Context, entity T, fields r3.Fields) (T, error)
- func (r *BaseCRUD[T, ID]) Restore(_ context.Context, id ID) error
- func (r *BaseCRUD[T, ID]) Update(_ context.Context, entity T) (T, error)
- type Codec
- type Decoder
- type Encoder
- type IDGenerator
- type IDGeneratorFunc
- type Option
- type StructMeta
- func (m *StructMeta) CopyFieldValues(dst any, src any, fieldNames []string)
- func (m *StructMeta) GetFieldValue(entity any, fieldName string) (any, bool)
- func (m *StructMeta) PKValue(entity any) any
- func (m *StructMeta) SetFieldValue(entityPtr any, fieldName string, val any) bool
- func (m *StructMeta) SetPKValue(entityPtr any, val any)
- func (m *StructMeta) SoftDeleteFieldIdx() int
- func (m *StructMeta) ValidatePatchFields(fields []string) ([]string, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsNotFound ¶
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
Aggregate computes grouped aggregates over the entities matching the query, evaluated in memory. See r3.Aggregator for the query semantics.
func (*BaseCRUD[T, ID]) HardDelete ¶
HardDelete permanently removes an entity regardless of soft-delete configuration.
func (*BaseCRUD[T, ID]) Patch ¶
Patch performs a partial update, modifying only the specified fields.
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) },
)
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 ¶
WithBaseDir sets the base directory for file storage. Each entity type will be stored in a file (or subdirectory) under this path.
func WithCodec ¶
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 ¶
WithFilePath sets an explicit file path, overriding the auto-derived path from resource name + codec extension. Only valid in single-file mode.
func WithR3Options ¶
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.