{{/*
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.
*/}}

{{/* gotype: entgo.io/ent/entc/gen.Graph */}}

{{/*
The runtime template generates the schema-stitching logic and holds the
module version that was used to generate the assets.

It has 2 formats. A "runtime" package that should be empty-imported in the
main package for schemas with hooks or policies (potential cyclic-import).
The second format is generated under the "ent" package, and empty-import is
not necessary (no option for cyclic-import). The second format used to keep
backwards-compatibility with previous versions of ent.
*/}}

{{ define "runtime/ent" }}
	{{ template "runtime" $ }}
{{ end }}

{{ define "runtime/pkg" }}
	{{ with extend $ "Package" "runtime" }}
		{{ template "runtime" . }}
	{{ end }}
	{{ $module := $.ModuleInfo }}
	{{ if or $module.Version $module.Sum }}
	const (
		{{- with $module.Version }}
			Version = "{{ . }}" // Version of ent codegen.
		{{- end }}
		{{- with $module.Sum }}
			Sum = "{{ . }}" // Sum of ent codegen.
		{{- end }}
	)
	{{ end }}
{{ end }}

{{ define "runtime" }}

{{ template "header" . }}

{{ $hooks := 0 }}
{{ range $n := $.Nodes }}
	{{ $numHooks := $n.NumHooks }}{{ if $n.NumPolicy }}{{ $numHooks = add $numHooks 1 }}{{ end }}
	{{ $hooks = add $hooks $numHooks }}
{{ end }}
{{ $rtpkg := false }}{{ if hasField $ "Scope" }}{{ $rtpkg = eq $.Scope.Package "runtime" }}{{ end }}

{{ if $rtpkg }} {{/* 1st format - runtime/runtime.go */}}
	{{ if $hooks  }}
		{{ template "runtime/register" $ }}
	{{ else }}
		// The schema-stitching logic is generated in {{ $.Config.Package }}/runtime.go
	{{ end }}
{{ else }}{{/* 2nd format - ent/runtime.go */}}
	{{ if not $hooks  }}
		{{ template "runtime/register" $ }}
	{{ else }}
		// The schema-stitching logic is generated in {{ $.Config.Package }}/runtime/runtime.go
	{{ end }}
{{ end }}
{{ end }}


{{/* register schema handlers to type packages */}}
{{ define "runtime/register" }}
import (
	{{- with $.Config.Schema }}
		"{{ . }}"
	{{- end }}
	{{- range $n := $.Nodes }}
		"{{ $.Config.Package }}/{{ $n.Package }}"
		{{- /* Import external packages */}}
		{{- template "import/types" $n }}
	{{- end }}

	"entgo.io/ent"
	"entgo.io/ent/privacy"
)


// The init function reads all schema descriptors with runtime code
// (default values, validators, hooks and policies) and stitches it
// to their package variables.
func init() {
{{- range $n := $.Nodes }}
	{{- $pkg := $n.Package }}
	{{- $schema := base $.Config.Schema }}
	{{- with $n.RuntimeMixin }}
		{{ $pkg }}Mixin := {{ $schema }}.{{ $n.Name }}{}.Mixin()
	{{- end }}
	{{- with $policies := $n.PolicyPositions }}
		{{- /* policies defined in schema and mixins. */}}
		{{ $pkg }}.Policy = privacy.NewPolicies({{ range $idx := $n.MixedInPolicies }}{{ $pkg }}Mixin[{{ $idx }}],{{ end }}{{ $schema }}.{{ $n.Name }}{})
		{{ $pkg }}.Hooks[0] = func(next ent.Mutator) ent.Mutator {
			return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
				if err := {{ $pkg }}.Policy.EvalMutation(ctx, m); err != nil {
					return nil, err
				}
				return next.Mutate(ctx, m)
			})
		}
	{{- end }}
	{{- with $hooks := $n.HookPositions }}
		{{- /* Hooks defined in schema mixins. */}}
		{{- with $idx := $n.MixedInHooks }}
			{{- range $i := $idx }}
				{{ print $pkg "MixinHooks" $i }} := {{ $pkg }}Mixin[{{ $i }}].Hooks()
			{{- end }}
		{{- end }}
		{{- /* If there are hooks defined in the schema. */}}
		{{- $schemaHooks := false }}{{ range $p := $hooks }}{{ if not $p.MixedIn }}{{ $schemaHooks = true }}{{ end }}{{ end }}
		{{- if $schemaHooks }}
			{{ print $pkg "Hooks" }} := {{ $schema }}.{{ $n.Name }}{}.Hooks()
		{{- end }}
		{{- range $i, $p := $hooks }}
			{{- if $n.NumPolicy }}
				{{ $i = add $i 1 }}
			{{- end }}
			{{- if $p.MixedIn }}
				{{ print $pkg ".Hooks" }}[{{ $i }}] =  {{ print $pkg "MixinHooks" $p.MixinIndex }}[{{ $p.Index }}]
			{{- else }}
				{{ print $pkg ".Hooks" }}[{{ $i }}] = {{ print $pkg "Hooks" }}[{{ $p.Index }}]
			{{- end }}
		{{- end }}
	{{- end }}
	{{- if or $n.HasDefault $n.HasValidators }}
		{{- with $idx := $n.MixedInFields }}
			{{- range $i := $idx }}
				{{ print $pkg "MixinFields" $i }} := {{ $pkg }}Mixin[{{ $i }}].Fields()
				_ = {{ $pkg }}MixinFields{{ $i }}
			{{- end }}
		{{- end }}
		{{- $fields := $n.Fields }}{{ if $n.ID.UserDefined }}{{ $fields = append $fields $n.ID }}{{ end }}
		{{- with $fields }}
			{{ $pkg }}Fields := {{ $schema }}.{{ $n.Name }}{}.Fields()
			_ = {{ $pkg }}Fields
		{{- end }}
		{{- range $i, $f := $fields }}
			{{- $desc := print $pkg "Desc" $f.StructField }}
			{{- /* enum default values handled near their declarations (in type package). */}}
			{{- if or (and $f.Default (not $f.IsEnum)) $f.UpdateDefault $f.Validators }}
				// {{ $desc }} is the schema descriptor for {{ $f.Name }} field.
				{{- if $f.Position.MixedIn }}
					{{ $desc }} := {{ print $pkg "MixinFields" $f.Position.MixinIndex }}[{{ $f.Position.Index }}].Descriptor()
				{{- else }}
					{{ $desc }} := {{ $pkg }}Fields[{{ $f.Position.Index }}].Descriptor()
				{{- end }}
			{{- end }}
			{{- if and $f.Default (not $f.IsEnum) }}
				{{- $default := print $pkg "." $f.DefaultName }}
				// {{ $default }} holds the default value on creation for the {{ $f.Name }} field.
				{{- $defaultType := print $f.Type.Type }}{{ if $f.DefaultFunc }}{{ $defaultType = print "func() " $f.Type }}{{ end }}
				{{- if and $f.HasGoType (not (hasPrefix $defaultType "func")) }}
					{{ $default }} = {{ $f.Type }}({{ $desc }}.Default.({{ $defaultType }}))
				{{- else }}
					{{ $default }} = {{ $desc }}.Default.({{ $defaultType }})
				{{- end }}
			{{- end }}
			{{- if $f.UpdateDefault }}
				{{- $default := print $pkg "." $f.UpdateDefaultName }}
				// {{ $default }} holds the default value on update for the {{ $f.Name }} field.
				{{ $default }} = {{ $desc }}.UpdateDefault.(func() {{ $f.Type }})
			{{- end }}
			{{- with $f.Validators }}
				{{- $name := print $pkg "." $f.Validator }}
				{{- $type :=  printf "func (%s) error" $f.Type.Type }}
				// {{ $name }} is a validator for the "{{ $f.Name }}" field. It is called by the builders before save.
				{{- if eq $f.Validators 1 }}
					{{ $name }} = {{ $desc }}.Validators[0].({{ $type }})
				{{- else }}
					{{ $name }} = func() {{ $type }} {
						validators := {{ $desc }}.Validators
						fns := [...]func({{ $f.Type.Type }}) error {
							{{- range $j, $n := xrange $f.Validators }}
								validators[{{ $j }}].(func({{ $f.Type.Type }}) error),
							{{- end }}
						}
						return func({{ $f.BuilderField }} {{ $f.Type.Type }}) error {
							for _, fn := range fns {
								if err := fn({{ $f.BuilderField }}); err != nil {
									return err
								}
							}
							return nil
						}
					}()
				{{- end }}
		{{- end }}
	{{- end }}
{{- end }}
{{- end }}
}
{{ end }}
