Documentation
¶
Index ¶
- Variables
- func DomainFrom[T any](ctx context.Context) (T, bool)
- func FIQL(ops ...FIQLOp) fieldOption
- func ParseFIQL[P Predicate](expr string, fields FIQLFields[P]) (P, error)
- func SkipProto() fieldOption
- func VirtualField(name string, ft FieldType, opts ...virtualFieldOption) entityOption
- func WithDomain[T any](ctx context.Context, d T) context.Context
- type ApplyConfig
- type ApplyOption
- type EdgeAnnotation
- type EdgeMode
- type EdgeOption
- type EntityAnnotation
- type Extension
- type ExtensionOption
- type FIQLBool
- type FIQLEnum
- type FIQLField
- type FIQLFields
- type FIQLFloat
- type FIQLInt
- type FIQLOp
- type FIQLString
- type FIQLTime
- type FIQLUUID
- type FieldAnnotation
- type FieldKind
- type FieldType
- type Predicate
- type ProtoConfig
- type ProtoEntityLock
- type ProtoFieldSpec
- type ProtoLockFile
- type ProtoOption
- type ProtoTypeConfig
- type ProtoTypeOpt
- type VirtualFieldConfig
Constants ¶
This section is empty.
Variables ¶
var ( // String is a FieldType for string. String = FieldType{TypeName: "string"} // Bool is a FieldType for bool. Bool = FieldType{TypeName: "bool"} // Int is a FieldType for int. Int = FieldType{TypeName: "int"} // Float64 is a FieldType for float64. Float64 = FieldType{TypeName: "float64"} )
var ( // DomainTemplate generates mapper methods (ToDomain, ApplyDomain) in the ent/ dir. DomainTemplate = parseT("template/domain.tmpl") // FIQLTemplate generates FIQL field registries and entry-point functions in the ent/ dir. FIQLTemplate = parseT("template/fiql.tmpl") // TemplateFuncs contains the extra template functions used by entdomain. TemplateFuncs = template.FuncMap{ "domainNodes": domainNodesFn, "entityAnnotation": entityAnnotationFn, "edgeAnnotation": edgeAnnotationFn, "domainImportPath": domainImportPathFn, "domainPkgName": domainPkgNameFn, "virtualFieldType": virtualFieldTypeFn, "isEnum": func(f *gen.Field) bool { return f.Type.Type == field.TypeEnum }, "isNillable": func(f *gen.Field) bool { return f.Optional || f.Nillable }, "isJSONField": func(f *gen.Field) bool { return f.Type.Type == field.TypeJSON || f.Type.Type == field.TypeBytes }, "hasEnumFields": hasEnumFieldsFn, "hasUpsert": hasUpsertFn, "hasFIQLNodes": hasFIQLNodesFn, "hasFIQLFields": hasFIQLFieldsFn, "fieldFIQLAnnotation": fieldFIQLAnnotationFn, "fieldFIQLKind": fieldFIQLKindFn, "isOp": isOpFn, "lower": strings.ToLower, "singular": func(s string) string { return gen.Funcs["singular"].(func(string) string)(s) }, "pascal": func(s string) string { return gen.Funcs["pascal"].(func(string) string)(s) }, "snake": func(s string) string { return gen.Funcs["snake"].(func(string) string)(s) }, } )
Functions ¶
func DomainFrom ¶
DomainFrom retrieves a domain struct of type T previously stashed via WithDomain. Returns the zero value of T and false when no value of that type is present. Always check ok — bare .Save(ctx) callers (who never called WithDomain) will see ok == false.
Example in an ent hook:
client.User.Use(func(next ent.Mutator) ent.Mutator {
return hook.UserFunc(func(ctx context.Context, m *ent.UserMutation) (ent.Value, error) {
d, ok := entdomain.DomainFrom[*domain.User](ctx)
if !ok {
// Not an entdomain-originated mutation; run default path.
return next.Mutate(ctx, m)
}
// Use d (including virtual fields) here.
return next.Mutate(ctx, m)
})
})
func FIQL ¶
func FIQL(ops ...FIQLOp) fieldOption
FIQL returns a fieldOption that enables FIQL filtering on a field with the given operators. Only the listed operators will be accepted at runtime; others return an error.
Example:
field.String("name").Annotations(entdomain.Field(
entdomain.FIQL(entdomain.EQ, entdomain.NEQ, entdomain.Contains),
))
func ParseFIQL ¶
func ParseFIQL[P Predicate](expr string, fields FIQLFields[P]) (P, error)
ParseFIQL parses a FIQL expression and returns an ent predicate using the provided field registry. Returns an error for unknown fields, disallowed operators, or malformed expressions.
FIQL syntax:
- field==value equality
- field!=value inequality
- field=gt=value greater than (numeric/time fields)
- field=lt=value less than
- field=ge=value greater than or equal
- field=le=value less than or equal
- field=like=value contains substring (string fields)
- field=prefix=value has prefix (string fields)
- expr;expr AND (higher precedence)
- expr,expr OR (lower precedence)
- (expr) grouping
func SkipProto ¶
func SkipProto() fieldOption
SkipProto returns a fieldOption that excludes the field from proto generation.
func VirtualField ¶
VirtualField returns an entityOption that adds a virtual field. Optional virtualFieldOptions (e.g., ProtoType) can be passed.
func WithDomain ¶
WithDomain returns a derived context carrying the domain struct d keyed by its Go type. It is an escape-hatch helper for users who want ent mutation hooks (client.X.Use) to read the full domain struct — including virtual fields — during Save.
The primary path for domain-to-ent encoding remains the typed transformer slots generated in ent/domain.go (see ADR-005). Use WithDomain only when the work must happen at ent-hook time rather than ApplyDomain time, e.g. outbox event emission, cross-cutting audit logging, or tenant-isolation checks that depend on virtual-field values.
Typical use is at the repository boundary, immediately before Save:
ctx = entdomain.WithDomain(ctx, d) builder, err := client.User.Create().ApplyDomain(ctx, d) // ... check err ... created, err := builder.Save(ctx) // hooks see d via DomainFrom[*domain.User](ctx)
Caveats:
- This uses context.Value, which Go's standard guidance reserves for request-scoped data. Static analyzers may flag call sites. That is expected: the smell lives at the opt-in site, not inside the library.
- Hook authors must handle the absent case via the ok return from DomainFrom. Do not assume presence — callers can bypass WithDomain.
- Do not auto-wrap ApplyDomain to call WithDomain internally. The ctx chain must stay visible to the caller.
Types ¶
type ApplyConfig ¶
type ApplyConfig struct {
// contains filtered or unexported fields
}
ApplyConfig holds the runtime configuration for domain apply operations.
func NewApplyConfig ¶
func NewApplyConfig(opts ...ApplyOption) *ApplyConfig
NewApplyConfig creates a new ApplyConfig with the given options applied.
func (*ApplyConfig) IsAppendEdge ¶
func (c *ApplyConfig) IsAppendEdge(field string) bool
IsAppendEdge reports whether the given edge field should use append semantics.
func (*ApplyConfig) ShouldApply ¶
func (c *ApplyConfig) ShouldApply(field string, val any) bool
ShouldApply reports whether a non-pointer field should be applied. It checks onlyFields, omitFields, and OmitZeroVal (via reflect.Value.IsZero).
func (*ApplyConfig) ShouldApplyPtr ¶
func (c *ApplyConfig) ShouldApplyPtr(field string, val any) bool
ShouldApplyPtr reports whether a pointer field should be applied. It checks onlyFields, omitFields, and OmitNil (val == nil).
type ApplyOption ¶
type ApplyOption func(*ApplyConfig)
ApplyOption is a functional option for configuring ApplyConfig.
func AppendEdge ¶
func AppendEdge(field string) ApplyOption
AppendEdge returns an ApplyOption that makes the given edge field use append instead of the default replace semantics.
func OmitFields ¶
func OmitFields(fields ...string) ApplyOption
OmitFields returns an ApplyOption that skips specific fields by name.
func OmitNil ¶
func OmitNil() ApplyOption
OmitNil returns an ApplyOption that skips pointer fields that are nil.
func OmitZeroVal ¶
func OmitZeroVal() ApplyOption
OmitZeroVal returns an ApplyOption that skips fields with zero values.
func OnlyFields ¶
func OnlyFields(fields ...string) ApplyOption
OnlyFields returns an ApplyOption that allowlists specific fields by name.
type EdgeAnnotation ¶
type EdgeAnnotation struct {
Mode EdgeMode
}
EdgeAnnotation is placed on individual edge Annotations() to configure domain inclusion.
func Edge ¶
func Edge(opts ...EdgeOption) EdgeAnnotation
Edge builds an EdgeAnnotation with the given options.
func (*EdgeAnnotation) Decode ¶
func (a *EdgeAnnotation) Decode(v interface{}) error
Decode unmarshals the annotation.
func (EdgeAnnotation) HasIDs ¶
func (a EdgeAnnotation) HasIDs() bool
HasIDs reports whether the IDs mode is set.
func (EdgeAnnotation) HasNest ¶
func (a EdgeAnnotation) HasNest() bool
HasNest reports whether the Nest mode is set.
func (EdgeAnnotation) Merge ¶
func (a EdgeAnnotation) Merge(other schema.Annotation) schema.Annotation
Merge implements schema.Merger.
type EdgeOption ¶
type EdgeOption func(*EdgeAnnotation)
EdgeOption is a functional option for EdgeAnnotation.
func IDs ¶
func IDs() EdgeOption
IDs returns an EdgeOption that includes edge IDs in the domain struct.
func Nest ¶
func Nest() EdgeOption
Nest returns an EdgeOption that includes nested structs in the domain struct.
type EntityAnnotation ¶
type EntityAnnotation struct {
VirtualFields []VirtualFieldConfig
NoBulk bool
}
EntityAnnotation is placed on schema Annotations() to opt an entity into domain generation.
func Entity ¶
func Entity(opts ...entityOption) EntityAnnotation
Entity builds an EntityAnnotation with the given options.
func (*EntityAnnotation) Decode ¶
func (a *EntityAnnotation) Decode(v interface{}) error
Decode unmarshals the annotation.
func (EntityAnnotation) Merge ¶
func (a EntityAnnotation) Merge(other schema.Annotation) schema.Annotation
Merge implements schema.Merger.
func (EntityAnnotation) Name ¶
func (EntityAnnotation) Name() string
Name implements schema.Annotation.
type Extension ¶
type Extension struct {
entc.DefaultExtension
// contains filtered or unexported fields
}
Extension implements entc.Extension for generating a pure Go domain layer.
func NewExtension ¶
func NewExtension(opts ...ExtensionOption) (*Extension, error)
NewExtension creates a new Extension with the given options.
type ExtensionOption ¶
ExtensionOption is a functional option for configuring Extension.
func WithNoBulk ¶
func WithNoBulk(entityNames ...string) ExtensionOption
WithNoBulk disables bulk generation (XxxList type, ToDomain slice method, CreateBulkDomain, UpdateBulkDomain, XxxUpdateOneBulk). Called with no arguments, it disables bulk for all entities. Called with entity names, it disables bulk only for those entities.
func WithPackageName ¶
func WithPackageName(name string) ExtensionOption
WithPackageName sets the Go package name for the generated domain package. Defaults to "domain" if not specified.
func WithPackagePath ¶
func WithPackagePath(path string) ExtensionOption
WithPackagePath sets the output path for the generated domain package, relative to the module root (the parent of the ent directory). Example: "internal/domain"
func WithProto ¶
func WithProto(opts ...ProtoOption) ExtensionOption
WithProto enables proto file generation with the given options. When enabled, the extension generates .proto message files and domain ↔ proto mapper files.
type FIQLBool ¶
FIQLBool handles FIQL filtering for boolean fields. Values must be "true" or "false".
type FIQLEnum ¶
type FIQLEnum[P Predicate] struct { EQ map[string]P NEQ map[string]P IsNil func() P NotNil func() P }
FIQLEnum handles FIQL filtering for enum fields. Predicates are pre-built at generation time; lookups are O(1) map access.
type FIQLField ¶
type FIQLField[P Predicate] interface { // contains filtered or unexported methods }
FIQLField is implemented by all typed FIQL field helpers.
type FIQLFields ¶
FIQLFields maps field names to their FIQL field descriptors.
type FIQLFloat ¶
type FIQLFloat[P Predicate] struct { EQ func(float64) P NEQ func(float64) P GT func(float64) P LT func(float64) P GTE func(float64) P LTE func(float64) P In func(...float64) P NotIn func(...float64) P IsNil func() P NotNil func() P }
FIQLFloat handles FIQL filtering for float fields.
type FIQLInt ¶
type FIQLInt[P Predicate] struct { EQ func(int) P NEQ func(int) P GT func(int) P LT func(int) P GTE func(int) P LTE func(int) P In func(...int) P NotIn func(...int) P IsNil func() P NotNil func() P }
FIQLInt handles FIQL filtering for integer fields.
type FIQLOp ¶
type FIQLOp string
FIQLOp is a FIQL comparison operator.
const ( // EQ matches field == value (all types). EQ FIQLOp = "==" // NEQ matches field != value (all types). NEQ FIQLOp = "!=" // GT matches field > value (int, float, time). GT FIQLOp = "=gt=" // LT matches field < value (int, float, time). LT FIQLOp = "=lt=" // GTE matches field >= value (int, float, time). GTE FIQLOp = "=ge=" // LTE matches field <= value (int, float, time). LTE FIQLOp = "=le=" // Contains matches field LIKE '%value%' (string). Contains FIQLOp = "=like=" // HasPrefix matches field LIKE 'value%' (string). HasPrefix FIQLOp = "=prefix=" // In matches field IN (v1, v2, ...) — value syntax: =in=(a,b,c). // Supported on string, int, float, enum, uuid fields. In FIQLOp = "=in=" // NotIn matches field NOT IN (v1, v2, ...) — value syntax: =out=(a,b,c). // Supported on string, int, float, enum, uuid fields. NotIn FIQLOp = "=out=" // Is is the parser-internal wire form ("=is=") that gets normalized into // IsNull or NotNull based on its value during parseComparison. Never // reaches apply — the normalization is the contract. Kept as a constant // so readOp can recognise the wire form symbolically. Is FIQLOp = "=is=" // IsNull matches field IS NULL — value syntax: field=is=null. // Synthetic op produced by parser normalization (never appears on the wire // literally). Supported on Optional() / Nillable() fields only — codegen // errors otherwise. IsNull FIQLOp = "=is=null" // NotNull matches field IS NOT NULL — value syntax: field=is=notnull. // Synthetic op produced by parser normalization. Same optionality // requirement as IsNull. NotNull FIQLOp = "=is=notnull" )
type FIQLString ¶
type FIQLString[P Predicate] struct { EQ func(string) P NEQ func(string) P Contains func(string) P HasPrefix func(string) P In func(...string) P NotIn func(...string) P IsNil func() P NotNil func() P }
FIQLString handles FIQL filtering for string fields.
type FIQLTime ¶
type FIQLTime[P Predicate] struct { EQ func(time.Time) P NEQ func(time.Time) P GT func(time.Time) P LT func(time.Time) P GTE func(time.Time) P LTE func(time.Time) P IsNil func() P NotNil func() P }
FIQLTime handles FIQL filtering for time.Time fields. Values are parsed as RFC3339 (e.g. "2024-01-15T10:30:00Z").
type FIQLUUID ¶
type FIQLUUID[P Predicate] struct { EQ func(uuid.UUID) P NEQ func(uuid.UUID) P In func(...uuid.UUID) P NotIn func(...uuid.UUID) P IsNil func() P NotNil func() P }
FIQLUUID handles FIQL filtering for UUID fields (github.com/google/uuid). Values are parsed via uuid.Parse, which accepts canonical 36-char hyphenated form, braced ({...}), urn:uuid:..., and 32-char hex without hyphens. Only == and != are supported — UUIDs are opaque identifiers, so ordering and substring operators are intentionally rejected.
type FieldAnnotation ¶
type FieldAnnotation struct {
SkipProto bool
ProtoType *ProtoTypeConfig
FIQLOps []FIQLOp
}
FieldAnnotation is placed on individual field Annotations() to configure domain field behaviour.
func Field ¶
func Field(opts ...fieldOption) FieldAnnotation
Field builds a FieldAnnotation with the given options.
func (*FieldAnnotation) Decode ¶
func (a *FieldAnnotation) Decode(v interface{}) error
Decode unmarshals the annotation.
func (FieldAnnotation) Merge ¶
func (a FieldAnnotation) Merge(other schema.Annotation) schema.Annotation
Merge implements schema.Merger.
func (FieldAnnotation) Name ¶
func (FieldAnnotation) Name() string
Name implements schema.Annotation.
type FieldKind ¶
type FieldKind int
FieldKind is the canonical broad classification of an ent field for the FIQL pipeline. Each generator (FIQL template, proto, domain) dispatches on this kind for the cases that diverge between ent types but converge into one behaviour from FIQL's perspective. Narrow type granularity (e.g. int32 vs int64) stays in each generator's sub-switch; the kind exists to centralise the outer ent-type-to-FIQL-kind mapping and the UUID/GoType gate.
type FieldType ¶
type FieldType struct {
// PkgPath is the import path; empty string means local/stdlib (no import added).
PkgPath string
// TypeName is the Go type name as-is; prefix with "*" for pointer types.
TypeName string
}
FieldType describes the Go type of a virtual field.
type Predicate ¶
Predicate is a constraint for ent predicate types. All ent predicate types have the underlying type func(*sql.Selector), enabling generic AND/OR combination.
type ProtoConfig ¶
type ProtoConfig struct {
// contains filtered or unexported fields
}
ProtoConfig holds configuration for proto file generation.
type ProtoEntityLock ¶
type ProtoEntityLock struct {
Fields map[string]int `json:"fields"` // snake_name → field number
Reserved []int `json:"reserved"` // permanently retired numbers
}
ProtoEntityLock records the field numbers assigned to an entity's fields, plus any field numbers that have been permanently retired (reserved).
type ProtoFieldSpec ¶
type ProtoFieldSpec struct {
// ProtoType is the proto field type string (e.g., "string", "int64", "google.protobuf.Timestamp").
ProtoType string
// ImportPath is the proto import path needed (e.g., "google/protobuf/timestamp.proto"), or "".
ImportPath string
// IsOptional means the proto field should use the proto3 "optional" keyword.
IsOptional bool
// IsRepeated means the proto field is "repeated".
IsRepeated bool
// IsEnum means the field is a proto enum.
IsEnum bool
// IsExcluded means this field should be omitted from proto output.
IsExcluded bool
// ExcludedReason explains why IsExcluded was set — populated by the resolvers
// at every exclusion site so the proto generator can surface a per-message
// summary of skipped fields. Empty when IsExcluded is false.
ExcludedReason string
// ToProtoExpr is a Go fmt format string (with %s for the source expression) that converts
// the domain value to the proto value. Empty string means direct assignment.
ToProtoExpr string
// FromProtoExpr is a Go fmt format string (with %s for the source expression) that converts
// the proto value to the domain value. Empty string means direct assignment.
FromProtoExpr string
// EnumTypeName is the proto enum type name (e.g., "UserStatus"). Only set when IsEnum=true.
EnumTypeName string
// NestEntityName is set for Nest edge specs and holds the referenced entity type name
// (e.g., "Post"). The mapper uses it to build mode-appropriate converter calls
// (e.g., PostToProto / PostFromProto in subpackage mode).
NestEntityName string
}
ProtoFieldSpec describes how a single domain field maps to a proto field.
type ProtoLockFile ¶
type ProtoLockFile struct {
Version int `json:"version"`
Entities map[string]ProtoEntityLock `json:"entities"`
}
ProtoLockFile is the in-memory representation of the .entdomain.lock.json file. It records the field number assignment for each proto entity to ensure stability.
type ProtoOption ¶
type ProtoOption func(*ProtoConfig)
ProtoOption is a functional option for ProtoConfig.
func WithProtoDir ¶
func WithProtoDir(dir string) ProtoOption
WithProtoDir sets the directory for generated .proto files, relative to the module root.
func WithProtoFullPackageName ¶
func WithProtoFullPackageName(name string) ProtoOption
WithProtoFullPackageName overrides the proto `package` declaration written into the generated .proto file. By default the declaration uses pkgName (the directory suffix), e.g. "entpb". Use this when the proto package name differs from the directory name, e.g. WithProtoPackageName("v1") + WithProtoFullPackageName("entminimal.v1").
func WithProtoGoPackage ¶
func WithProtoGoPackage(goPackage string) ProtoOption
WithProtoGoPackage sets the go_package proto option value.
func WithProtoHelpersInDomain ¶
func WithProtoHelpersInDomain() ProtoOption
WithProtoHelpersInDomain places the generated proto helper functions (toInt64Slice, etc.) in the domain package instead of the proto package (the default).
func WithProtoPackageName ¶
func WithProtoPackageName(name string) ProtoOption
WithProtoPackageName sets the proto package name.
type ProtoTypeConfig ¶
type ProtoTypeConfig struct {
TypeName string // e.g. "google.protobuf.Timestamp"
ImportPath string // e.g. "google/protobuf/timestamp.proto"
ToProtoExpr string // optional Go fmt string, e.g. "UserMetadataToProto(%s)"
FromProtoExpr string // optional Go fmt string, e.g. "UserMetadataFromProto(%s)"
}
ProtoTypeConfig specifies an explicit proto type for a field or virtual field.
type ProtoTypeOpt ¶
type ProtoTypeOpt struct {
TypeName string
ImportPath string
ToProtoExpr string
FromProtoExpr string
}
ProtoTypeOpt is the value returned by ProtoType(). It implements both virtualFieldOption (for use with VirtualField()) and fieldOption (for use with Field()), so the same call works in both contexts.
func ProtoType ¶
func ProtoType(typeName string, importPath ...string) ProtoTypeOpt
ProtoType sets an explicit proto type. Usable with both VirtualField() and Field(). importPath is optional; if omitted, no import is added.
func (ProtoTypeOpt) WithConversion ¶
func (p ProtoTypeOpt) WithConversion(toExpr, fromExpr string) ProtoTypeOpt
WithConversion returns a copy of ProtoTypeOpt with explicit Go conversion expressions. toExpr and fromExpr are Go fmt strings where %s is the source expression (e.g. "UserMetadataToProto(%s)" / "UserMetadataFromProto(%s)").
type VirtualFieldConfig ¶
type VirtualFieldConfig struct {
Name string
FieldType FieldType
ProtoType *ProtoTypeConfig
}
VirtualFieldConfig represents a single virtual field entry on EntityAnnotation.