snapshot of https://github.com/ent/ent.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Atlas CI Public / lint (push) Failing after 13s
Atlas CI Public / sync (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 22:12:36 +05:00
commit 4b3ad0b611
2680 changed files with 719472 additions and 0 deletions

49
schema/edge/annotation.go Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package edge
import "entgo.io/ent/schema"
// Annotation is a builtin schema annotation for
// configuring the edges' behavior in codegen.
type Annotation struct {
// The StructTag option allows overriding the struct-tag
// of the `Edges` field in the generated entity. For example:
//
// edge.Annotation{
// StructTag: `json:"pet_edges"`
// }
//
StructTag string
}
// Name describes the annotation name.
func (Annotation) Name() string {
return "Edges"
}
// Merge implements the schema.Merger interface.
func (a Annotation) Merge(other schema.Annotation) schema.Annotation {
var ant Annotation
switch other := other.(type) {
case Annotation:
ant = other
case *Annotation:
if other != nil {
ant = *other
}
default:
return a
}
if tag := ant.StructTag; tag != "" {
a.StructTag = tag
}
return a
}
var (
_ schema.Annotation = (*Annotation)(nil)
_ schema.Merger = (*Annotation)(nil)
)

276
schema/edge/edge.go Normal file
View File

@@ -0,0 +1,276 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package edge
import (
"reflect"
"entgo.io/ent/schema"
)
// A Descriptor for edge configuration.
type Descriptor struct {
Tag string // struct tag.
Type string // edge type.
Name string // edge name.
Field string // edge field name (e.g. foreign-key).
RefName string // ref name; inverse only.
Ref *Descriptor // edge reference; to/from of the same type.
Through *struct{ N, T string } // through type and name.
Unique bool // unique edge.
Inverse bool // inverse edge.
Required bool // required on creation.
Immutable bool // create only edge.
StorageKey *StorageKey // optional storage-key configuration.
Annotations []schema.Annotation // edge annotations.
Comment string // edge comment.
}
// To defines an association edge between two vertices.
func To(name string, t any) *assocBuilder {
return &assocBuilder{desc: &Descriptor{Name: name, Type: typ(t)}}
}
// From represents a reversed-edge between two vertices that has a back-reference to its source edge.
func From(name string, t any) *inverseBuilder {
return &inverseBuilder{desc: &Descriptor{Name: name, Type: typ(t), Inverse: true}}
}
func typ(t any) string {
if rt := reflect.TypeOf(t); rt.NumIn() > 0 {
return rt.In(0).Name()
}
return ""
}
// assocBuilder is the builder for assoc edges.
type assocBuilder struct {
desc *Descriptor
}
// Unique sets the edge type to be unique. Basically, it limits the edge to be one of the two:
// one2one or one2many. one2one applied if the inverse-edge is also unique.
func (b *assocBuilder) Unique() *assocBuilder {
b.desc.Unique = true
return b
}
// Required indicates that this edge is a required field on creation.
// Unlike fields, edges are optional by default.
func (b *assocBuilder) Required() *assocBuilder {
b.desc.Required = true
return b
}
// Immutable indicates that this edge cannot be updated.
func (b *assocBuilder) Immutable() *assocBuilder {
b.desc.Immutable = true
return b
}
// StructTag sets the struct tag of the assoc edge.
func (b *assocBuilder) StructTag(s string) *assocBuilder {
b.desc.Tag = s
return b
}
// From creates an inverse-edge with the same type.
func (b *assocBuilder) From(name string) *inverseBuilder {
return &inverseBuilder{desc: &Descriptor{Name: name, Type: b.desc.Type, Inverse: true, Ref: b.desc}}
}
// Field is used to bind an edge (with a foreign-key) to a field in the schema.
//
// field.Int("owner_id").
// Optional()
//
// edge.To("owner", User.Type).
// Field("owner_id").
// Unique(),
func (b *assocBuilder) Field(f string) *assocBuilder {
b.desc.Field = f
return b
}
// Through allows setting an "edge schema" to interact explicitly with M2M edges.
//
// edge.To("friends", User.Type).
// Through("friendships", Friendship.Type)
func (b *assocBuilder) Through(name string, t any) *assocBuilder {
b.desc.Through = &struct{ N, T string }{N: name, T: typ(t)}
return b
}
// Comment used to put annotations on the schema.
func (b *assocBuilder) Comment(c string) *assocBuilder {
b.desc.Comment = c
return b
}
// StorageKey sets the storage key of the edge.
//
// edge.To("groups", Group.Type).
// StorageKey(edge.Table("user_groups"), edge.Columns("user_id", "group_id"))
func (b *assocBuilder) StorageKey(opts ...StorageOption) *assocBuilder {
if b.desc.StorageKey == nil {
b.desc.StorageKey = &StorageKey{}
}
for i := range opts {
opts[i](b.desc.StorageKey)
}
return b
}
// Annotations adds a list of annotations to the edge object to be used by
// codegen extensions.
//
// edge.To("pets", Pet.Type).
// Annotations(entgql.Bind())
func (b *assocBuilder) Annotations(annotations ...schema.Annotation) *assocBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Descriptor interface.
func (b *assocBuilder) Descriptor() *Descriptor {
return b.desc
}
// inverseBuilder is the builder for inverse edges.
type inverseBuilder struct {
desc *Descriptor
}
// Ref sets the referenced-edge of this inverse edge.
func (b *inverseBuilder) Ref(ref string) *inverseBuilder {
b.desc.RefName = ref
return b
}
// Unique sets the edge type to be unique. Basically, it limits the edge to be one of the two:
// one-2-one or one-2-many. one-2-one applied if the inverse-edge is also unique.
func (b *inverseBuilder) Unique() *inverseBuilder {
b.desc.Unique = true
return b
}
// Required indicates that this edge is a required field on creation.
// Unlike fields, edges are optional by default.
func (b *inverseBuilder) Required() *inverseBuilder {
b.desc.Required = true
return b
}
// Immutable indicates that this edge cannot be updated.
func (b *inverseBuilder) Immutable() *inverseBuilder {
b.desc.Immutable = true
return b
}
// StructTag sets the struct tag of the inverse edge.
func (b *inverseBuilder) StructTag(s string) *inverseBuilder {
b.desc.Tag = s
return b
}
// Comment used to put annotations on the schema.
func (b *inverseBuilder) Comment(c string) *inverseBuilder {
b.desc.Comment = c
return b
}
// Field is used to bind an edge (with a foreign-key) to a field in the schema.
//
// field.Int("owner_id").
// Optional()
//
// edge.From("owner", User.Type).
// Ref("pets").
// Field("owner_id").
// Unique(),
func (b *inverseBuilder) Field(f string) *inverseBuilder {
b.desc.Field = f
return b
}
// Through allows setting an "edge schema" to interact explicitly with M2M edges.
//
// edge.From("liked_users", User.Type).
// Ref("liked_tweets").
// Through("likes", TweetLike.Type)
func (b *inverseBuilder) Through(name string, t any) *inverseBuilder {
b.desc.Through = &struct{ N, T string }{N: name, T: typ(t)}
return b
}
// Annotations adds a list of annotations to the edge object to be used by
// codegen extensions.
//
// edge.From("owner", User.Type).
// Ref("pets").
// Unique().
// Annotations(entgql.Bind())
func (b *inverseBuilder) Annotations(annotations ...schema.Annotation) *inverseBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Descriptor interface.
func (b *inverseBuilder) Descriptor() *Descriptor {
return b.desc
}
// StorageKey holds the configuration for edge storage-key.
type StorageKey struct {
Table string // Table or label.
Symbols []string // Symbols/names of the foreign-key constraints.
Columns []string // Foreign-key columns.
}
// StorageOption allows for setting the storage configuration using functional options.
type StorageOption func(*StorageKey)
// Table sets the table name option for M2M edges.
func Table(name string) StorageOption {
return func(key *StorageKey) {
key.Table = name
}
}
// Symbol sets the symbol/name of the foreign-key constraint for O2O, O2M and M2O edges.
// Note that, for M2M edges (2 columns and 2 constraints), use the edge.Symbols option.
func Symbol(symbol string) StorageOption {
return func(key *StorageKey) {
key.Symbols = []string{symbol}
}
}
// Symbols sets the symbol/name of the foreign-key constraints for M2M edges.
// The 1st column defines the name of the "To" edge, and the 2nd defines
// the name of the "From" edge (inverse edge).
// Note that, for O2O, O2M and M2O edges, use the edge.Symbol option.
func Symbols(to, from string) StorageOption {
return func(key *StorageKey) {
key.Symbols = []string{to, from}
}
}
// Column sets the foreign-key column name option for O2O, O2M and M2O edges.
// Note that, for M2M edges (2 columns), use the edge.Columns option.
func Column(name string) StorageOption {
return func(key *StorageKey) {
key.Columns = []string{name}
}
}
// Columns sets the foreign-key column names option for M2M edges.
// The 1st column defines the name of the "To" edge, and the 2nd defines
// the name of the "From" edge (inverse edge).
// Note that, for O2O, O2M and M2O edges, use the edge.Column option.
func Columns(to, from string) StorageOption {
return func(key *StorageKey) {
key.Columns = []string{to, from}
}
}

129
schema/edge/edge_test.go Normal file
View File

@@ -0,0 +1,129 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package edge_test
import (
"testing"
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEdge(t *testing.T) {
assert := assert.New(t)
type User struct{ ent.Schema }
e := edge.To("friends", User.Type).
Required().
Comment("comment").
Descriptor()
assert.False(e.Inverse)
assert.Equal("comment", e.Comment)
assert.Equal("User", e.Type)
assert.Equal("friends", e.Name)
assert.True(e.Required)
type Node struct{ ent.Schema }
e = edge.To("parent", Node.Type).
Unique().
Immutable().
Descriptor()
assert.False(e.Inverse)
assert.True(e.Unique)
assert.Equal("Node", e.Type)
assert.Equal("parent", e.Name)
assert.False(e.Required)
assert.True(e.Immutable)
e = edge.To("children", Node.Type).
From("parent").
Unique().
Comment("comment").
Field("parent_id").
Descriptor()
assert.Equal("parent_id", e.Field)
assert.Equal("comment", e.Comment)
assert.Empty(e.Ref.Field)
t.Log("m2m relation of the same type")
from := edge.To("following", User.Type).
From("followers").
Descriptor()
assert.True(from.Inverse)
assert.False(from.Unique)
assert.Equal("followers", from.Name)
assert.NotNil(from.Ref)
assert.Equal("following", from.Ref.Name)
assert.False(from.Ref.Unique)
t.Log("o2m relation of the same type")
from = edge.To("following", User.Type).
Unique().
From("followers").
Descriptor()
assert.False(from.Unique)
assert.True(from.Ref.Unique)
from = edge.To("following", User.Type).
From("followers").
Unique().
Descriptor()
assert.True(from.Unique)
assert.False(from.Ref.Unique)
t.Log("o2o relation of the same type")
from = edge.To("following", User.Type).
Unique().
From("followers").
Unique().
Descriptor()
assert.True(from.Unique)
assert.True(from.Ref.Unique)
e = edge.To("user", User.Type).
StructTag(`json:"user_name,omitempty"`).
Descriptor()
assert.Equal(`json:"user_name,omitempty"`, e.Tag)
from = edge.To("following", User.Type).
StructTag("following").
StorageKey(edge.Table("user_followers"), edge.Columns("following_id", "followers_id"), edge.Symbol("users_followers")).
From("followers").
StructTag("followers").
Descriptor()
assert.Equal("followers", from.Tag)
assert.Equal("following", from.Ref.Tag)
assert.Equal(edge.StorageKey{Table: "user_followers", Symbols: []string{"users_followers"}, Columns: []string{"following_id", "followers_id"}}, *from.Ref.StorageKey)
}
type GQL struct {
Field string
}
func (GQL) Name() string {
return "GQL"
}
func TestAnnotations(t *testing.T) {
type User struct{ ent.Schema }
to := edge.To("user", User.Type).
Annotations(GQL{Field: "to"}).
Descriptor()
require.Equal(t, []schema.Annotation{GQL{Field: "to"}}, to.Annotations)
from := edge.From("user", User.Type).
Annotations(GQL{Field: "from"}).
Descriptor()
require.Equal(t, []schema.Annotation{GQL{Field: "from"}}, from.Annotations)
bidi := edge.To("following", User.Type).
Annotations(GQL{Field: "to"}).
From("followers").
Annotations(GQL{Field: "from"}).
Descriptor()
require.Equal(t, []schema.Annotation{GQL{Field: "from"}}, bidi.Annotations)
require.Equal(t, []schema.Annotation{GQL{Field: "to"}}, bidi.Ref.Annotations)
}

View File

@@ -0,0 +1,81 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package field
import "entgo.io/ent/schema"
// Annotation is a builtin schema annotation for
// configuring the schema fields in codegen.
type Annotation struct {
// The StructTag option allows overriding the struct-tag
// of the fields in the generated entity. For example:
//
// field.Annotation{
// StructTag: map[string]string{
// "id": `json:"id,omitempty" yaml:"-"`,
// },
// }
//
StructTag map[string]string
// ID defines a multi-field schema identifier. Note,
// the annotation is valid only for edge schemas.
//
// func (TweetLike) Annotations() []schema.Annotation {
// return []schema.Annotation{
// field.ID("user_id", "tweet_id"),
// }
// }
//
ID []string
}
// ID defines a multi-field schema identifier. Note, the
// annotation is valid only for edge schemas.
//
// func (TweetLike) Annotations() []schema.Annotation {
// return []schema.Annotation{
// field.ID("user_id", "tweet_id"),
// }
// }
//
func ID(first, second string, fields ...string) *Annotation {
return &Annotation{ID: append([]string{first, second}, fields...)}
}
// Name describes the annotation name.
func (Annotation) Name() string {
return "Fields"
}
// Merge implements the schema.Merger interface.
func (a Annotation) Merge(other schema.Annotation) schema.Annotation {
var ant Annotation
switch other := other.(type) {
case Annotation:
ant = other
case *Annotation:
if other != nil {
ant = *other
}
default:
return a
}
if a.StructTag == nil && len(ant.StructTag) > 0 {
a.StructTag = make(map[string]string, len(ant.StructTag))
}
for k, v := range ant.StructTag {
a.StructTag[k] = v
}
if len(ant.ID) > 0 {
a.ID = ant.ID
}
return a
}
var _ interface {
schema.Annotation
schema.Merger
} = (*Annotation)(nil)

View File

@@ -0,0 +1,26 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package field_test
import (
"testing"
"entgo.io/ent/schema/field"
"github.com/stretchr/testify/assert"
)
func TestAnnotation_Merge(t *testing.T) {
ant := field.Annotation{}
a := ant.Merge(field.Annotation{
StructTag: map[string]string{"foo": "bar"},
})
assert.Equal(t, a.(field.Annotation).StructTag["foo"], "bar")
a = ant.Merge(&field.Annotation{
StructTag: map[string]string{"foo": "baz", "baz": "qux"},
})
assert.Equal(t, a.(field.Annotation).StructTag["foo"], "baz")
assert.Equal(t, a.(field.Annotation).StructTag["baz"], "qux")
}

1909
schema/field/field.go Normal file

File diff suppressed because it is too large Load Diff

1017
schema/field/field_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
// gen is a codegen cmd for generating numeric build types from template.
package main
import (
"bytes"
"go/format"
"log"
"os"
"strings"
"text/template"
"entgo.io/ent/schema/field"
)
func main() {
buf, err := os.ReadFile("internal/numeric.tmpl")
if err != nil {
log.Fatal("reading template file:", err)
}
intTmpl := template.Must(template.New("numeric").
Funcs(template.FuncMap{"title": strings.Title, "hasPrefix": strings.HasPrefix, "toUpper": strings.ToUpper}).
Parse(string(buf)))
b := &bytes.Buffer{}
if err := intTmpl.Execute(b, struct {
Ints, Floats []field.Type
}{
Ints: []field.Type{
field.TypeInt,
field.TypeUint,
field.TypeInt8,
field.TypeInt16,
field.TypeInt32,
field.TypeInt64,
field.TypeUint8,
field.TypeUint16,
field.TypeUint32,
field.TypeUint64,
},
Floats: []field.Type{
field.TypeFloat64,
field.TypeFloat32,
},
}); err != nil {
log.Fatal("executing template:", err)
}
if buf, err = format.Source(b.Bytes()); err != nil {
log.Fatal("formatting output:", err)
}
if err := os.WriteFile("numeric.go", buf, 0644); err != nil {
log.Fatal("writing go file:", err)
}
}

View File

@@ -0,0 +1,470 @@
{{ define "numeric" }}
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
// Code generated by internal/numeric.tmpl, DO NOT EDIT.
package field
import (
"errors"
"reflect"
"strings"
"entgo.io/ent/schema"
)
//go:generate go run internal/gen.go
{{ range $t := $.Ints }}
{{ $title := title $t.String }}
// {{ $title }} returns a new Field with type {{ $t }}.
func {{ $title }}(name string) *{{ $t }}Builder { return &{{ $t }}Builder{&Descriptor{
Name: name,
Info: &TypeInfo{Type: Type{{ $title }} },
}}
}
{{ end }}
// Float returns a new Field with type float64.
func Float(name string) *float64Builder { return &float64Builder{&Descriptor{
Name: name,
Info: &TypeInfo{Type: TypeFloat64},
}}
}
// Float32 returns a new Field with type float32.
func Float32(name string) *float32Builder { return &float32Builder{&Descriptor{
Name: name,
Info: &TypeInfo{Type: TypeFloat32},
}}
}
{{ range $t := $.Ints }}
{{ $builder := printf "%sBuilder" $t }}
// {{ $builder }} is the builder for {{ $t }} field.
type {{ $builder }} struct {
desc *Descriptor
}
// Unique makes the field unique within all vertices of this type.
func (b *{{ $builder }}) Unique() *{{ $builder }} {
b.desc.Unique = true
return b
}
// Range adds a range validator for this field where the given value needs to be in the range of [i, j].
func (b *{{ $builder }}) Range(i, j {{ $t }}) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, func(v {{ $t }}) error {
if v < i || v > j {
return errors.New("value out of range")
}
return nil
})
return b
}
// Min adds a minimum value validator for this field. Operation fails if the validator fails.
func (b *{{ $builder }}) Min(i {{ $t }}) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, func(v {{ $t }}) error {
if v < i {
return errors.New("value out of range")
}
return nil
})
return b
}
// Max adds a maximum value validator for this field. Operation fails if the validator fails.
func (b *{{ $builder }}) Max(i {{ $t }}) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, func(v {{ $t }}) error {
if v > i {
return errors.New("value out of range")
}
return nil
})
return b
}
// Positive adds a minimum value validator with the value of 1. Operation fails if the validator fails.
func (b *{{ $builder }}) Positive() *{{ $builder }} {
return b.Min(1)
}
{{ if hasPrefix $t.String "uint" | not }}
// Negative adds a maximum value validator with the value of -1. Operation fails if the validator fails.
func (b *{{ $builder }}) Negative() *{{ $builder }} {
return b.Max(-1)
}
// NonNegative adds a minimum value validator with the value of 0. Operation fails if the validator fails.
func (b *{{ $builder }}) NonNegative() *{{ $builder }} {
return b.Min(0)
}
{{ end }}
// Default sets the default value of the field.
func (b *{{ $builder }}) Default(i {{ $t }}) *{{ $builder }} {
b.desc.Default = i
return b
}
// DefaultFunc sets the function that is applied to set the default value
// of the field on creation.
func (b *{{ $builder }}) DefaultFunc(fn any) *{{ $builder }} {
b.desc.Default = fn
return b
}
// UpdateDefault sets the function that is applied to set default value
// of the field on update. For example:
//
// field.{{ title $t.String }}("{{$t.String}}").
// Default(0).
// UpdateDefault(GenNumber),
//
func (b *{{ $builder }}) UpdateDefault(fn any) *{{ $builder }} {
b.desc.UpdateDefault = fn
return b
}
// Nillable indicates that this field is a nillable.
// Unlike "Optional" only fields, "Nillable" fields are pointers in the generated struct.
func (b *{{ $builder }}) Nillable() *{{ $builder }} {
b.desc.Nillable = true
return b
}
// Comment sets the comment of the field.
func (b *{{ $builder }}) Comment(c string) *{{ $builder }} {
b.desc.Comment = c
return b
}
// Optional indicates that this field is optional on create.
// Unlike edges, fields are required by default.
func (b *{{ $builder }}) Optional() *{{ $builder }} {
b.desc.Optional = true
return b
}
// Immutable indicates that this field cannot be updated.
func (b *{{ $builder }}) Immutable() *{{ $builder }} {
b.desc.Immutable = true
return b
}
// StructTag sets the struct tag of the field.
func (b *{{ $builder }}) StructTag(s string) *{{ $builder }} {
b.desc.Tag = s
return b
}
// Validate adds a validator for this field. Operation fails if the validation fails.
func (b *{{ $builder }}) Validate(fn func({{ $t }}) error) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, fn)
return b
}
// StorageKey sets the storage key of the field.
// In SQL dialects is the column name and Gremlin is the property.
func (b *{{ $builder }}) StorageKey(key string) *{{ $builder }} {
b.desc.StorageKey = key
return b
}
// SchemaType overrides the default database type with a custom
// schema type (per dialect) for {{ $t.String }}.
//
// field.{{ title $t.String }}("oid").
// SchemaType(map[string]string{
// dialect.Postgres: "CustomType",
// })
//
func (b *{{ $builder }}) SchemaType(types map[string]string) *{{ $builder }} {
b.desc.SchemaType = types
return b
}
{{ $tt := title $t.String }}
// GoType overrides the default Go type with a custom one.
// If the provided type implements the Validator interface
// and no validators have been set, the type validator will
// be used.
//
// field.{{ $tt }}("{{ $t }}").
// GoType(pkg.{{ $tt }}(0))
//
// Note that, the custom Go type `T` needs to implement the
// `Add(T) T` method in order to support the `Add<F>` operation
// in mutations. For example:
//
// func(t1 T) Add(t2 T) T {
// return add(t1, t2)
// }
//
func (b *{{ $builder }}) GoType(typ any) *{{ $builder }} {
b.desc.goType(typ)
return b
}
// ValueScanner provides an external value scanner for the given GoType.
// Using this option allow users to use field types that do not implement
// the sql.Scanner and driver.Valuer interfaces.
func (b *{{ $builder }}) ValueScanner(vs any) *{{ $builder }} {
b.desc.ValueScanner = vs
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.{{ $tt }}("{{ $t }}").
// Annotations(entgql.OrderField("{{ $t.String | toUpper }}"))
//
func (b *{{ $builder }}) Annotations(annotations ...schema.Annotation) *{{ $builder }} {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Deprecated marks the field as deprecated. Deprecated fields are not
// selected by default in queries, and their struct fields are annotated
// with `deprecated` in the generated code.
func (b *{{ $builder }}) Deprecated(reason ...string) *{{ $builder }} {
b.desc.Deprecated = true
if len(reason) > 0 {
b.desc.DeprecatedReason = strings.Join(reason, " ")
}
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *{{ $builder }}) Descriptor() *Descriptor {
if b.desc.Default != nil || b.desc.UpdateDefault != nil {
b.desc.checkDefaultFunc({{ $t }}Type)
}
b.desc.checkGoType({{ $t }}Type)
return b.desc
}
{{ end }}
var (
{{- range $t := $.Ints }}
{{ $t }}Type = reflect.TypeOf({{ $t }}(0))
{{- end }}
)
{{ range $t := $.Floats }}
{{ $builder := printf "%sBuilder" $t }}
// {{ $builder }} is the builder for float fields.
type {{ $builder }} struct {
desc *Descriptor
}
// Unique makes the field unique within all vertices of this type.
func (b *{{ $builder }}) Unique() *{{ $builder }} {
b.desc.Unique = true
return b
}
// Range adds a range validator for this field where the given value needs to be in the range of [i, j].
func (b *{{ $builder }}) Range(i, j {{ $t }}) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, func(v {{ $t }}) error {
if v < i || v > j {
return errors.New("value out of range")
}
return nil
})
return b
}
// Min adds a minimum value validator for this field. Operation fails if the validator fails.
func (b *{{ $builder }}) Min(i {{ $t }}) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, func(v {{ $t }}) error {
if v < i {
return errors.New("value out of range")
}
return nil
})
return b
}
// Max adds a maximum value validator for this field. Operation fails if the validator fails.
func (b *{{ $builder }}) Max(i {{ $t }}) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, func(v {{ $t }}) error {
if v > i {
return errors.New("value out of range")
}
return nil
})
return b
}
// Positive adds a minimum value validator with the value of 0.000001. Operation fails if the validator fails.
func (b *{{ $builder }}) Positive() *{{ $builder }} {
return b.Min(1e-06)
}
// Negative adds a maximum value validator with the value of -0.000001. Operation fails if the validator fails.
func (b *{{ $builder }}) Negative() *{{ $builder }} {
return b.Max(-1e-06)
}
// Default sets the default value of the field.
func (b *{{ $builder }}) Default(i {{ $t }}) *{{ $builder }} {
b.desc.Default = i
return b
}
// DefaultFunc sets the function that is applied to set the default value
// of the field on creation.
func (b *{{ $builder }}) DefaultFunc(fn any) *{{ $builder }} {
b.desc.Default = fn
return b
}
// UpdateDefault sets the function that is applied to set default value
// of the field on update. For example:
//
// field.{{ title $t.String }}("{{$t.String}}").
// Default(0).
// UpdateDefault(GenNumber),
//
func (b *{{ $builder }}) UpdateDefault(fn any) *{{ $builder }} {
b.desc.UpdateDefault = fn
return b
}
// Nillable indicates that this field is a nillable.
// Unlike "Optional" only fields, "Nillable" fields are pointers in the generated struct.
func (b *{{ $builder }}) Nillable() *{{ $builder }} {
b.desc.Nillable = true
return b
}
// Comment sets the comment of the field.
func (b *{{ $builder }}) Comment(c string) *{{ $builder }} {
b.desc.Comment = c
return b
}
// Optional indicates that this field is optional on create.
// Unlike edges, fields are required by default.
func (b *{{ $builder }}) Optional() *{{ $builder }} {
b.desc.Optional = true
return b
}
// Immutable indicates that this field cannot be updated.
func (b *{{ $builder }}) Immutable() *{{ $builder }} {
b.desc.Immutable = true
return b
}
// StructTag sets the struct tag of the field.
func (b *{{ $builder }}) StructTag(s string) *{{ $builder }} {
b.desc.Tag = s
return b
}
// Validate adds a validator for this field. Operation fails if the validation fails.
func (b *{{ $builder }}) Validate(fn func({{ $t }}) error) *{{ $builder }} {
b.desc.Validators = append(b.desc.Validators, fn)
return b
}
// StorageKey sets the storage key of the field.
// In SQL dialects is the column name and Gremlin is the property.
func (b *{{ $builder }}) StorageKey(key string) *{{ $builder }} {
b.desc.StorageKey = key
return b
}
// SchemaType overrides the default database type with a custom
// schema type (per dialect) for {{ $t.String }}.
//
// field.{{ title $t.String }}("amount").
// SchemaType(map[string]string{
// dialect.MySQL: "decimal(5, 2)",
// dialect.Postgres: "numeric(5, 2)",
// })
//
func (b *{{ $builder }}) SchemaType(types map[string]string) *{{ $builder }} {
b.desc.SchemaType = types
return b
}
{{ $tt := title $t.String }}
// GoType overrides the default Go type with a custom one.
// If the provided type implements the Validator interface
// and no validators have been set, the type validator will
// be used.
//
// field.{{ $tt }}("{{ $t }}").
// GoType(pkg.{{ $tt }}(0))
//
// Note that, the custom Go type `T` needs to implement the
// `Add(T) T` method in order to support the `Add<F>` operation
// in mutations. For example:
//
// func(t1 T) Add(t2 T) T {
// return add(t1, t2)
// }
//
func (b *{{ $builder }}) GoType(typ any) *{{ $builder }} {
b.desc.goType(typ)
return b
}
// ValueScanner provides an external value scanner for the given GoType.
// Using this option allow users to use field types that do not implement
// the sql.Scanner and driver.Valuer interfaces.
func (b *{{ $builder }}) ValueScanner(vs any) *{{ $builder }} {
b.desc.ValueScanner = vs
return b
}
// Annotations adds a list of annotations to the field object to be used by
// codegen extensions.
//
// field.{{ $tt }}("{{ $t }}").
// Annotations(entgql.OrderField("{{ $t.String | toUpper }}"))
//
func (b *{{ $builder }}) Annotations(annotations ...schema.Annotation) *{{ $builder }} {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Deprecated marks the field as deprecated. Deprecated fields are not
// selected by default in queries, and their struct fields are annotated
// with `deprecated` in the generated code.
func (b *{{ $builder }}) Deprecated(reason ...string) *{{ $builder }} {
b.desc.Deprecated = true
if len(reason) > 0 {
b.desc.DeprecatedReason = strings.Join(reason, " ")
}
return b
}
// Descriptor implements the ent.Field interface by returning its descriptor.
func (b *{{ $builder }}) Descriptor() *Descriptor {
if b.desc.Default != nil || b.desc.UpdateDefault != nil {
b.desc.checkDefaultFunc({{ $t }}Type)
}
b.desc.checkGoType({{ $t }}Type)
return b.desc
}
{{ end }}
var (
{{- range $t := $.Floats }}
{{ $t }}Type = reflect.TypeOf({{ $t }}(0))
{{- end }}
)
{{ end }}

2449
schema/field/numeric.go Normal file

File diff suppressed because it is too large Load Diff

252
schema/field/type.go Normal file
View File

@@ -0,0 +1,252 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package field
import (
"fmt"
"reflect"
"strings"
)
// A Type represents a field type.
type Type uint8
// List of field types.
const (
TypeInvalid Type = iota
TypeBool
TypeTime
TypeJSON
TypeUUID
TypeBytes
TypeEnum
TypeString
TypeOther
TypeInt8
TypeInt16
TypeInt32
TypeInt
TypeInt64
TypeUint8
TypeUint16
TypeUint32
TypeUint
TypeUint64
TypeFloat32
TypeFloat64
TypeBlob
endTypes
)
// String returns the string representation of a type.
func (t Type) String() string {
if t < endTypes {
return typeNames[t]
}
return typeNames[TypeInvalid]
}
// Numeric reports if the given type is a numeric type.
func (t Type) Numeric() bool {
return t >= TypeInt8 && t < TypeBlob
}
// Float reports if the given type is a float type.
func (t Type) Float() bool {
return t == TypeFloat32 || t == TypeFloat64
}
// Integer reports if the given type is an integral type.
func (t Type) Integer() bool {
return t.Numeric() && !t.Float()
}
// Valid reports if the given type if known type.
func (t Type) Valid() bool {
return t > TypeInvalid && t < endTypes
}
// ConstName returns the constant name of an info type.
// It's used by entc for printing the constant name in templates.
func (t Type) ConstName() string {
switch {
case !t.Valid():
return typeNames[TypeInvalid]
case int(t) < len(constNames) && constNames[t] != "":
return constNames[t]
default:
return "Type" + strings.Title(typeNames[t])
}
}
// TypeInfo holds the information regarding field type.
// Used by complex types like JSON and Bytes.
type TypeInfo struct {
Type Type
Ident string
PkgPath string // import path.
PkgName string // local package name.
Nillable bool // slices or pointers.
RType *RType
}
// String returns the string representation of a type.
func (t TypeInfo) String() string {
switch {
case t.Ident != "":
return t.Ident
case t.Type < endTypes:
return typeNames[t.Type]
default:
return typeNames[TypeInvalid]
}
}
// Valid reports if the given type if known type.
func (t TypeInfo) Valid() bool {
return t.Type.Valid()
}
// Numeric reports if the given type is a numeric type.
func (t TypeInfo) Numeric() bool {
return t.Type.Numeric()
}
// ConstName returns the const name of the info type.
func (t TypeInfo) ConstName() string {
return t.Type.ConstName()
}
// ValueScanner indicates if this type implements the ValueScanner interface.
func (t TypeInfo) ValueScanner() bool {
return t.RType.Implements(valueScannerType)
}
// Validator indicates if this type implements the Validator interface.
func (t TypeInfo) Validator() bool {
return t.RType.Implements(validatorType)
}
// Valuer indicates if this type implements the driver.Valuer interface.
func (t TypeInfo) Valuer() bool {
return t.RType.Implements(valuerType)
}
// Comparable reports whether values of this type are comparable.
func (t TypeInfo) Comparable() bool {
switch t.Type {
case TypeBool, TypeTime, TypeUUID, TypeEnum, TypeString:
return true
case TypeOther:
// Always accept custom types as comparable on the database side.
// In the future, we should consider adding an interface to let
// custom types tell if they are comparable or not (see #1304).
return true
default:
return t.Numeric()
}
}
var stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
// Stringer indicates if this type implements the Stringer interface.
func (t TypeInfo) Stringer() bool {
return t.RType.Implements(stringerType)
}
var (
typeNames = [...]string{
TypeInvalid: "invalid",
TypeBool: "bool",
TypeTime: "time.Time",
TypeJSON: "json.RawMessage",
TypeUUID: "[16]byte",
TypeBytes: "[]byte",
TypeEnum: "string",
TypeString: "string",
TypeOther: "other",
TypeBlob: "blob",
TypeInt: "int",
TypeInt8: "int8",
TypeInt16: "int16",
TypeInt32: "int32",
TypeInt64: "int64",
TypeUint: "uint",
TypeUint8: "uint8",
TypeUint16: "uint16",
TypeUint32: "uint32",
TypeUint64: "uint64",
TypeFloat32: "float32",
TypeFloat64: "float64",
}
constNames = [...]string{
TypeJSON: "TypeJSON",
TypeUUID: "TypeUUID",
TypeTime: "TypeTime",
TypeEnum: "TypeEnum",
TypeBytes: "TypeBytes",
TypeOther: "TypeOther",
TypeBlob: "TypeBlob",
}
)
// RType holds a serializable reflect.Type information of
// Go object. Used by the entc package.
type RType struct {
Name string // reflect.Type.Name
Ident string // reflect.Type.String
Kind reflect.Kind
PkgPath string
Methods map[string]struct{ In, Out []*RType }
// Used only for in-package checks.
rtype reflect.Type
}
// TypeEqual reports if the underlying type is equal to the RType (after pointer indirections).
func (r *RType) TypeEqual(t reflect.Type) bool {
tv := indirect(t)
return r.Name == tv.Name() && r.Kind == t.Kind() && r.PkgPath == tv.PkgPath()
}
// RType returns the string value of the indirect reflect.Type.
func (r *RType) String() string {
if r.rtype != nil {
return r.rtype.String()
}
return r.Ident
}
// IsPtr reports if the reflect-type is a pointer type.
func (r *RType) IsPtr() bool {
return r != nil && r.Kind == reflect.Ptr
}
// Implements reports whether the RType ~implements the given interface type.
func (r *RType) Implements(typ reflect.Type) bool {
if r == nil {
return false
}
n := typ.NumMethod()
for i := 0; i < n; i++ {
m0 := typ.Method(i)
m1, ok := r.Methods[m0.Name]
if !ok || len(m1.In) != m0.Type.NumIn() || len(m1.Out) != m0.Type.NumOut() {
return false
}
in := m0.Type.NumIn()
for j := 0; j < in; j++ {
if !m1.In[j].TypeEqual(m0.Type.In(j)) {
return false
}
}
out := m0.Type.NumOut()
for j := 0; j < out; j++ {
if !m1.Out[j].TypeEqual(m0.Type.Out(j)) {
return false
}
}
}
return true
}

121
schema/index/index.go Normal file
View File

@@ -0,0 +1,121 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package index
import "entgo.io/ent/schema"
// A Descriptor for index configuration.
type Descriptor struct {
Unique bool // unique index.
Edges []string // edge columns.
Fields []string // field columns.
StorageKey string // custom index name.
Annotations []schema.Annotation // index annotations.
}
// Builder for indexes on vertex columns and edges in the graph.
type Builder struct {
desc *Descriptor
}
// Fields creates an index on the given vertex fields.
// Note that indexes are implemented only for SQL dialects, and does not support gremlin.
//
// func (T) Indexes() []ent.Index {
//
// // Unique index on 2 fields.
// index.Fields("first", "last").
// Unique(),
//
// // Unique index of field under specific edge.
// index.Fields("name").
// Edges("parent").
// Unique(),
//
// }
//
func Fields(fields ...string) *Builder {
return &Builder{desc: &Descriptor{Fields: fields}}
}
// Edges creates an index on the given vertex edge fields.
// Note that indexes are implemented only for SQL dialects, and does not support gremlin.
//
// func (T) Indexes() []ent.Index {
//
// // Unique index of field under 2 edges.
// index.Fields("name").
// Edges("parent", "type").
// Unique(),
//
// }
//
func Edges(edges ...string) *Builder {
return &Builder{desc: &Descriptor{Edges: edges}}
}
// Fields sets the fields of the index.
//
// func (T) Indexes() []ent.Index {
//
// // Unique "name" and "age" fields under the "parent" edge.
// index.Edges("parent").
// Fields("name", "age").
// Unique(),
//
// }
func (b *Builder) Fields(fields ...string) *Builder {
b.desc.Fields = fields
return b
}
// Edges sets the fields index to be unique under the set of edges (sub-graph). For example:
//
// func (T) Indexes() []ent.Index {
//
// // Unique "name" field under the "parent" edge.
// index.Fields("name").
// Edges("parent").
// Unique(),
// }
//
func (b *Builder) Edges(edges ...string) *Builder {
b.desc.Edges = edges
return b
}
// Unique sets the index to be a unique index.
// Note that defining a uniqueness on optional fields won't prevent
// duplicates if one of the column contains NULL values.
func (b *Builder) Unique() *Builder {
b.desc.Unique = true
return b
}
// StorageKey sets the storage key of the index. In SQL dialects, it's the index name.
func (b *Builder) StorageKey(key string) *Builder {
b.desc.StorageKey = key
return b
}
// Annotations adds a list of annotations to the index object to be used by codegen extensions.
//
// func (T) Indexes() []ent.Index {
//
// // Partial index on name where the entity is not deleted.
// index.Fields("name").
// Annotations(entsql.Prefix(100))
//
// }
//
func (b *Builder) Annotations(annotations ...schema.Annotation) *Builder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Descriptor interface.
func (b *Builder) Descriptor() *Descriptor {
return b.desc
}

View File

@@ -0,0 +1,39 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package index_test
import (
"testing"
"entgo.io/ent/schema/index"
"github.com/stretchr/testify/require"
)
func TestIndex(t *testing.T) {
idx := index.Fields("name", "address").
StorageKey("user_index").
Descriptor()
require.Empty(t, idx.Edges)
require.False(t, idx.Unique)
require.Equal(t, "user_index", idx.StorageKey)
require.Equal(t, []string{"name", "address"}, idx.Fields)
idx = index.Fields("name", "address").
Unique().
Descriptor()
require.Empty(t, idx.Edges)
require.True(t, idx.Unique)
require.Empty(t, idx.StorageKey)
require.Equal(t, []string{"name", "address"}, idx.Fields)
idx = index.Fields("name", "address").
Edges("parent", "type").
Unique().
Descriptor()
require.Equal(t, []string{"parent", "type"}, idx.Edges)
require.True(t, idx.Unique)
require.Equal(t, []string{"name", "address"}, idx.Fields)
}

127
schema/mixin/mixin.go Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package mixin
import (
"time"
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/field"
)
// Schema is the default implementation for the ent.Mixin interface.
// It should be embedded in end-user mixin as follows:
//
// type M struct {
// mixin.Schema
// }
type Schema struct{}
// Fields of the mixin.
func (Schema) Fields() []ent.Field { return nil }
// Edges of the mixin.
func (Schema) Edges() []ent.Edge { return nil }
// Indexes of the mixin.
func (Schema) Indexes() []ent.Index { return nil }
// Hooks of the mixin.
func (Schema) Hooks() []ent.Hook { return nil }
// Interceptors of the schema.
func (Schema) Interceptors() []ent.Interceptor { return nil }
// Policy of the mixin.
func (Schema) Policy() ent.Policy { return nil }
// Annotations of the mixin.
func (Schema) Annotations() []schema.Annotation { return nil }
// schema mixin must implement `Mixin` interface.
var _ ent.Mixin = (*Schema)(nil)
// CreateTime adds created at time field.
type CreateTime struct{ Schema }
// Fields of the create time mixin.
func (CreateTime) Fields() []ent.Field {
return []ent.Field{
field.Time("create_time").
Default(time.Now).
Immutable(),
}
}
// create time mixin must implement `Mixin` interface.
var _ ent.Mixin = (*CreateTime)(nil)
// UpdateTime adds updated at time field.
type UpdateTime struct{ Schema }
// Fields of the update time mixin.
func (UpdateTime) Fields() []ent.Field {
return []ent.Field{
field.Time("update_time").
Default(time.Now).
UpdateDefault(time.Now),
}
}
// update time mixin must implement `Mixin` interface.
var _ ent.Mixin = (*UpdateTime)(nil)
// Time composes create/update time mixin.
type Time struct{ Schema }
// Fields of the time mixin.
func (Time) Fields() []ent.Field {
return append(
CreateTime{}.Fields(),
UpdateTime{}.Fields()...,
)
}
// time mixin must implement `Mixin` interface.
var _ ent.Mixin = (*Time)(nil)
// AnnotateFields adds field annotations to underlying mixin fields.
func AnnotateFields(m ent.Mixin, annotations ...schema.Annotation) ent.Mixin {
return fieldAnnotator{Mixin: m, annotations: annotations}
}
// AnnotateEdges adds edge annotations to underlying mixin edges.
func AnnotateEdges(m ent.Mixin, annotations ...schema.Annotation) ent.Mixin {
return edgeAnnotator{Mixin: m, annotations: annotations}
}
type fieldAnnotator struct {
ent.Mixin
annotations []schema.Annotation
}
func (a fieldAnnotator) Fields() []ent.Field {
fields := a.Mixin.Fields()
for i := range fields {
desc := fields[i].Descriptor()
desc.Annotations = append(desc.Annotations, a.annotations...)
}
return fields
}
type edgeAnnotator struct {
ent.Mixin
annotations []schema.Annotation
}
func (a edgeAnnotator) Edges() []ent.Edge {
edges := a.Mixin.Edges()
for i := range edges {
desc := edges[i].Descriptor()
desc.Annotations = append(desc.Annotations, a.annotations...)
}
return edges
}

100
schema/mixin/mixin_test.go Normal file
View File

@@ -0,0 +1,100 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package mixin_test
import (
"testing"
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/mixin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTimeMixin(t *testing.T) {
t.Run("Create", func(t *testing.T) {
t.Parallel()
fields := mixin.CreateTime{}.Fields()
require.Len(t, fields, 1)
desc := fields[0].Descriptor()
assert.Equal(t, "create_time", desc.Name)
assert.True(t, desc.Immutable)
assert.NotNil(t, desc.Default)
assert.Nil(t, desc.UpdateDefault)
})
t.Run("Update", func(t *testing.T) {
t.Parallel()
fields := mixin.UpdateTime{}.Fields()
require.Len(t, fields, 1)
desc := fields[0].Descriptor()
assert.Equal(t, "update_time", desc.Name)
assert.NotNil(t, desc.Default)
assert.NotNil(t, desc.UpdateDefault)
})
t.Run("Compose", func(t *testing.T) {
t.Parallel()
fields := mixin.Time{}.Fields()
require.Len(t, fields, 2)
assert.Equal(t, "create_time", fields[0].Descriptor().Name)
assert.Equal(t, "update_time", fields[1].Descriptor().Name)
})
}
type annotation string
func (annotation) Name() string { return "" }
func TestAnnotateFields(t *testing.T) {
annotations := []schema.Annotation{
annotation("foo"),
annotation("bar"),
annotation("baz"),
}
fields := mixin.AnnotateFields(
mixin.Time{}, annotations...,
).Fields()
require.Len(t, fields, 2)
for _, f := range fields {
desc := f.Descriptor()
require.Len(t, desc.Annotations, len(annotations))
for i := range desc.Annotations {
assert.Equal(t, annotations[i], desc.Annotations[i])
}
}
}
type TestSchema struct {
ent.Schema
}
func (TestSchema) Edges() []ent.Edge {
return []ent.Edge{
edge.To("one", TestSchema.Type),
edge.From("two", TestSchema.Type).
Ref("one"),
}
}
func TestAnnotateEdges(t *testing.T) {
annotations := []schema.Annotation{
annotation("foo"),
annotation("bar"),
annotation("baz"),
}
edges := mixin.AnnotateEdges(
TestSchema{}, annotations...,
).Edges()
require.Len(t, edges, 2)
for _, e := range edges {
desc := e.Descriptor()
require.Len(t, desc.Annotations, len(annotations))
for i := range desc.Annotations {
assert.Equal(t, annotations[i], desc.Annotations[i])
}
}
}

43
schema/schema.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package schema
// Annotation is used to attach arbitrary metadata to the schema objects in codegen.
// The object must be serializable to JSON raw value (e.g. struct, map or slice).
//
// Template extensions can retrieve this metadata and use it inside their templates.
// Read more about it in ent website: https://entgo.io/docs/templates/#annotations.
type Annotation interface {
// Name defines the name of the annotation to be retrieved by the codegen.
Name() string
}
// Merger wraps the single Merge function allows custom annotation to provide
// an implementation for merging 2 or more annotations from the same type.
//
// A common use case is where the same Annotation type is defined both in
// mixin.Schema and ent.Schema.
type Merger interface {
Merge(Annotation) Annotation
}
// CommentAnnotation is a builtin schema annotation for
// configuring the schema's Godoc comment.
type CommentAnnotation struct {
Text string // Comment text.
}
// Name implements the Annotation interface.
func (*CommentAnnotation) Name() string {
return "Comment"
}
// Comment is a builtin schema annotation for
// configuring the schema's Godoc comment.
func Comment(text string) *CommentAnnotation {
return &CommentAnnotation{Text: text}
}
var _ Annotation = (*CommentAnnotation)(nil)