# Test that a get go works for various "interesting" types,
# including those special cases that result in _ and string
# that are specifically mentioned in help get go.
#
# This test does not exercise with/without --local. Instead
# that is left for other tests, so that this test can focus
# on the various rules of get go.

# cue get go
exec cue get go --local ./...
cmp ./pkg3/pkg3_go_gen.cue ./pkg3/pkg3_go_gen.cue.golden
cmp ./pkg1/file1_go_gen.cue ./pkg1/file1_go_gen.cue.golden
cmp ./pkg1/alias_go_gen.cue ./pkg1/alias_go_gen.cue.golden
cmp ./pkg2/pkg2_go_gen.cue ./pkg2/pkg2_go_gen.cue.golden
cmp ./pkg4/pkg4_go_gen.cue ./pkg4/pkg4_go_gen.cue.golden
cmp ./pkg_generics/pkg_generics_go_gen.cue ./pkg_generics/pkg_generics_go_gen.cue.golden

# Verify dependencies did not change
cmp go.mod go.mod.golden

-- go.mod --
module mod.test

go 1.21
-- go.mod.golden --
module mod.test

go 1.21
-- cue.mod/module.cue --
module: "mod.test"
-- pkg1/alias.go --
package pkg1

import p3 "mod.test/pkg3"

type MyBarzer = p3.Barzer
-- pkg1/file1.go --
/*
  block comment
*/
package pkg1

import (
	"encoding"
	"encoding/json"
	"time"
	"unsafe"

	p2 "mod.test/pkg2"
)

// Foozer foozes a jaman.
type Foozer struct {
	Int    int
	String string

	Inline `json:",inline"`
	NoInline

	CustomJSON CustomJSON
	CustomYAML *CustomYAML
	AnyJSON    json.Marshaler
	AnyText    encoding.TextMarshaler

	Bar int `json:"bar,omitempty" cue:">10"`

	exclude int

	Ptr uintptr

	UnsafePointer unsafe.Pointer

	Complex64     complex64
	Complex128    complex128

	Byte byte

	// Time is mapped to CUE's internal type.
	Time time.Time

	Barzer p2.Barzer

	Alias1           *MyBarzer
	EmbedAliasInline struct{ MyBarzer `json:",inline"` }
	SliceAlias       []MyBarzer

	// Note: Go encodings of protobuf tags are lossy. So this is a best-effort
	// thing.
	Map    map[string]*CustomJSON `protobuf:"bytes,1,name=intf" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
	Slice1 []int
	Slice2 []interface{}
	Slice3 *[]json.Unmarshaler
	Array1 [5]int
	Array2 [5]interface{}
	Array3 *[5]json.Marshaler
	Array4 [5]byte

	Intf  Interface `protobuf:"varint,2,name=intf"`
	Intf2 interface{}
	Intf3 struct{ Interface }
	Intf4 interface{ Foo() }

	// Even though this struct as a type implements MarshalJSON, it is known
	// that it is really only implemented by the embedded field.
	Embed struct{ CustomJSON }

	Unsupported map[int]string

	OptionalOmitEmptyJSON string `json:"optionalOmitEmptyJSON,omitempty"`

	OptionalOmitZeroJSON time.Time `json:"optionalOmitZeroJSON,omitzero"`

	OptionalOmitEmptyYAML string `yaml:"optionalOmitEmptyYAML,omitempty"`

	// +optional
	OptionalComment string `json:"optionalComment"`

	//+optional
	OptionalCommentNoSpace string `json:"optionalCommentNoSpace"`

	// Something before
	//
	// +optional
	//
	// Something after
	OptionalCommentExtra string `json:"optionalCommentExtra"`

	// +optional=
	OptionalCommentTag string `json:"optionalCommentTag"`

	// +optional=some-value
	OptionalCommentTagValue string `json:"optionalCommentTagValue"`

	// some-prefix+optional
	RequiredCommentPrefix string `json:"requiredCommentPrefix"`

	// +optional-some-suffix
	RequiredCommentSuffix string `json:"requiredCommentSuffix"`
}

type Identifier string

const (
	internalIdentifier Identifier = "internal"
)

const _ = true

// appease linter
var _ = internalIdentifier

// Level gives an indication of the extent of stuff.
type Level int

const (
	/*
	  Block comment.
	   Indented.

	  Empty line before.
	*/
	Unknown Level = iota
	Low
	// Medium is neither High nor Low
	Medium
	High
)

type Level2 Level

const (
	AnotherLevel1 Level2 = iota + 4
	AnotherLevel2
)

type CustomJSON struct {
}

func (c *CustomJSON) MarshalJSON() ([]byte, error) {
	return nil, nil
}

type CustomYAML struct {
}

func (c CustomYAML) MarshalYAML() ([]byte, error) {
	return nil, nil
}

type localType int

const (
	localConst localType = 1

	_ = localConst // silence linter
)

type Inline struct {
	Kind string
}

type NoInline struct {
	Kind string
}

type Interface interface {
	Boomer() bool
}
-- pkg2/add.cue --
package pkgtwo

Barzer: {
	S: =~"cat$"
}
-- pkg2/pkg2.go --
// Package pkgtwo does other stuff.
package pkgtwo

import (
	"math/big"
	t "time"
)

// A Barzer barzes.
type Barzer struct {
	A int `protobuf:"varint,2," json:"a"`

	T t.Time
	B *big.Int
	C big.Int
	F big.Float `xml:",attr"`
	G *big.Float
	H bool `json:"-"`
	S string

	XY bool `json:"x-y"`

	Err error

	*Inline `json:",inline"`
}

const Perm = 0755

const Few = 3

const Couple int = 2

// LongStringConst ensures that we are using go/constant
// correctly for the exact value of a constant as opposed
// to a shortened version (relevant for strings)
const LongStringConst = "This is a really long string. Why are we using a long string? Because that way it ensures we are using go/constant.Value.ExactString() instead of go/constant.Value.String()"

type Inline struct{ A int }
-- pkg3/pkg3.go --
package pkg3

import pkgtwo "mod.test/pkg2"

type Barzer = pkgtwo.Barzer
-- pkg1/alias_go_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg1

package pkg1

import "mod.test/pkg2"

#MyBarzer: pkgtwo.#Barzer
-- pkg1/file1_go_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg1

// block comment
package pkg1

import (
	"time"
	p2 "mod.test/pkg2:pkgtwo"
)

// Foozer foozes a jaman.
#Foozer: {
	Int:    int
	String: string

	#Inline
	NoInline:      #NoInline
	CustomJSON:    #CustomJSON
	CustomYAML?:   null | #CustomYAML @go(,*CustomYAML)
	AnyJSON:       _                  @go(,json.Marshaler)
	AnyText:       string             @go(,encoding.TextMarshaler)
	bar?:          int & >10          @go(Bar)
	Ptr:           uint64             @go(,uintptr)
	UnsafePointer: uint64             @go(,unsafe.Pointer)
	Complex64:     _                  @go(,complex64)
	Complex128:    _                  @go(,complex128)
	Byte:          uint8              @go(,byte)

	// Time is mapped to CUE's internal type.
	Time:    time.Time
	Barzer:  p2.#Barzer
	Alias1?: null | p2.#Barzer @go(,*MyBarzer)
	EmbedAliasInline: {
		p2.#Barzer
	} @go(,"struct{MyBarzer \"json:\\\",inline\\\"\"}")
	SliceAlias: [...p2.#Barzer] @go(,[]MyBarzer)

	// Note: Go encodings of protobuf tags are lossy. So this is a best-effort
	// thing.
	Map: {[string]: null | #CustomJSON} @go(,map[string]*CustomJSON) @protobuf(1,map[bytes]bytes,name=intf)
	Slice1: [...int] @go(,[]int)
	Slice2: [...] @go(,[]interface{})
	Slice3?: null | [...] @go(,*[]json.Unmarshaler)
	Array1: 5 * [int] @go(,[5]int)
	Array2: 5 * [_] @go(,[5]interface{})
	Array3?: null | 5*[_] @go(,*[5]json.Marshaler)
	Array4: bytes      @go(,[5]byte)
	Intf:   #Interface @protobuf(2,varint,name=intf)
	Intf2:  _          @go(,interface{})
	Intf3: {
		Interface: #Interface
	} @go(,struct{Interface})
	Intf4: _ @go(,"interface{Foo()}")

	// Even though this struct as a type implements MarshalJSON, it is known
	// that it is really only implemented by the embedded field.
	Embed: {
		CustomJSON: #CustomJSON
	} @go(,struct{CustomJSON})
	optionalOmitEmptyJSON?: string    @go(OptionalOmitEmptyJSON)
	optionalOmitZeroJSON?:  time.Time @go(OptionalOmitZeroJSON)
	optionalOmitEmptyYAML?: string    @go(OptionalOmitEmptyYAML)

	// +optional
	optionalComment?: string @go(OptionalComment)

	//+optional
	optionalCommentNoSpace?: string @go(OptionalCommentNoSpace)

	// Something before
	//
	// +optional
	//
	// Something after
	optionalCommentExtra?: string @go(OptionalCommentExtra)

	// +optional=
	optionalCommentTag?: string @go(OptionalCommentTag)

	// +optional=some-value
	optionalCommentTagValue?: string @go(OptionalCommentTagValue)

	// some-prefix+optional
	requiredCommentPrefix: string @go(RequiredCommentPrefix)

	// +optional-some-suffix
	requiredCommentSuffix: string @go(RequiredCommentSuffix)
}

#Identifier: string // #enumIdentifier

#enumIdentifier:
	_#internalIdentifier

_#internalIdentifier: #Identifier & "internal"

// Level gives an indication of the extent of stuff.
#Level: int // #enumLevel

#enumLevel:
	#Unknown |
	#Low |
	#Medium |
	#High

#values_Level: {
	Unknown: #Unknown
	Low:     #Low
	Medium:  #Medium
	High:    #High
}

// Block comment.
//  Indented.
//
// Empty line before.
#Unknown: #Level & 0
#Low:     #Level & 1

// Medium is neither High nor Low
#Medium: #Level & 2
#High:   #Level & 3

#Level2: #Level // #enumLevel2

#enumLevel2:
	#AnotherLevel1 |
	#AnotherLevel2

#values_Level2: {
	AnotherLevel1: #AnotherLevel1
	AnotherLevel2: #AnotherLevel2
}

#AnotherLevel1: #Level2 & 4
#AnotherLevel2: #Level2 & 5

#CustomJSON: _

#CustomYAML: {}

_#localType: int

_#localConst: _#localType & 1

#Inline: Kind: string

#NoInline: Kind: string

#Interface: _
-- pkg2/add_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg2

// Copyright 2019 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pkgtwo

Barzer: {
 S: =~"cat$"
}
-- pkg2/pkg2_go_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg2

// Package pkgtwo does other stuff.
package pkgtwo

import t "time"

// A Barzer barzes.
#Barzer: {
	a:     int @go(A) @protobuf(2,varint,)
	T:     t.Time
	B?:    null | int    @go(,*big.Int)
	C:     int           @go(,big.Int)
	F:     string        @go(,big.Float) @xml(,attr)
	G?:    null | string @go(,*big.Float)
	S:     string
	"x-y": bool @go(XY)
	Err:   _    @go(,error)

	#Inline
}

#Perm: 0o755

#Few: 3

#Couple: int & 2

#LongStringConst: "This is a really long string. Why are we using a long string? Because that way it ensures we are using go/constant.Value.ExactString() instead of go/constant.Value.String()"

#Inline: A: int
-- pkg3/pkg3_go_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg3

package pkg3

import "mod.test/pkg2:pkgtwo"

#Barzer: pkgtwo.#Barzer
-- pkg4/pkg4.go --
package pkg4

import "unsafe"

type A struct {
	UnsafePointer unsafe.Pointer
}
-- pkg4/pkg4_go_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg4

package pkg4

#A: {
	UnsafePointer: uint64 @go(,unsafe.Pointer)
}
-- pkg_generics/pkg_generics.go --
package pkg_generics

type SomeUnionInterface interface {
	string | int | byte
}

type SomeStructAny[T any] struct {
	SomeField T
}

type SomeStructString[T string] struct {
	SomeField T
}

type SomeStructSomeInterface[T SomeUnionInterface] struct {
	SomeField T
}

type SomeStructMultipleTypeParameters[T string, Z ~int] struct {
	SomeField      T
	SomeOtherField Z
}

type SomeStructComparable[T comparable] struct {
	SomeField T
}

type SomeInterfaceWithMethod interface {
	int

	Do() string
}

type SomeAlias = string

type StructWithAlias[T SomeAlias] struct {
	SomeField T
}

type SomeType int

type StructWithType[T SomeType] struct {
	SomeField T
}
-- pkg_generics/pkg_generics_go_gen.cue.golden --
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go mod.test/pkg_generics

package pkg_generics

#SomeUnionInterface: string | int | uint8

#SomeStructAny: {
	SomeField: _ @go(,T)
}

#SomeStructString: {
	SomeField: string @go(,T)
}

#SomeStructSomeInterface: {
	SomeField: #SomeUnionInterface @go(,T)
}

#SomeStructMultipleTypeParameters: {
	SomeField:      string @go(,T)
	SomeOtherField: int    @go(,Z)
}

#SomeStructComparable: {
	SomeField: _ @go(,T)
}

#SomeInterfaceWithMethod: int

#SomeAlias: string

#StructWithAlias: {
	SomeField: string @go(,T)
}

#SomeType: int

#StructWithType: {
	SomeField: #SomeType @go(,T)
}
