1package pgsgo
2
3import (
4	"fmt"
5	"strings"
6
7	pgs "github.com/lyft/protoc-gen-star"
8)
9
10func (c context) Type(f pgs.Field) TypeName {
11	ft := f.Type()
12
13	var t TypeName
14	switch {
15	case ft.IsMap():
16		key := scalarType(ft.Key().ProtoType())
17		return TypeName(fmt.Sprintf("map[%s]%s", key, c.elType(ft)))
18	case ft.IsRepeated():
19		return TypeName(fmt.Sprintf("[]%s", c.elType(ft)))
20	case ft.IsEmbed():
21		return c.importableTypeName(f, ft.Embed()).Pointer()
22	case ft.IsEnum():
23		t = c.importableTypeName(f, ft.Enum())
24	default:
25		t = scalarType(ft.ProtoType())
26	}
27
28	if f.Syntax() == pgs.Proto2 {
29		return t.Pointer()
30	}
31
32	return t
33}
34
35func (c context) importableTypeName(f pgs.Field, e pgs.Entity) TypeName {
36	t := TypeName(c.Name(e))
37
38	if c.ImportPath(e) == c.ImportPath(f) {
39		return t
40	}
41
42	return TypeName(fmt.Sprintf("%s.%s", c.PackageName(e), t))
43}
44
45func (c context) elType(ft pgs.FieldType) TypeName {
46	el := ft.Element()
47	switch {
48	case el.IsEnum():
49		return c.importableTypeName(ft.Field(), el.Enum())
50	case el.IsEmbed():
51		return c.importableTypeName(ft.Field(), el.Embed()).Pointer()
52	default:
53		return scalarType(el.ProtoType())
54	}
55}
56
57func scalarType(t pgs.ProtoType) TypeName {
58	switch t {
59	case pgs.DoubleT:
60		return "float64"
61	case pgs.FloatT:
62		return "float32"
63	case pgs.Int64T, pgs.SFixed64, pgs.SInt64:
64		return "int64"
65	case pgs.UInt64T, pgs.Fixed64T:
66		return "uint64"
67	case pgs.Int32T, pgs.SFixed32, pgs.SInt32:
68		return "int32"
69	case pgs.UInt32T, pgs.Fixed32T:
70		return "uint32"
71	case pgs.BoolT:
72		return "bool"
73	case pgs.StringT:
74		return "string"
75	case pgs.BytesT:
76		return "[]byte"
77	default:
78		panic("unreachable: invalid scalar type")
79	}
80}
81
82// A TypeName describes the name of a type (type on a field, or method signature)
83type TypeName string
84
85// String satisfies the strings.Stringer interface.
86func (n TypeName) String() string { return string(n) }
87
88// Element returns the TypeName of the element of n. For types other than
89// slices and maps, this just returns n.
90func (n TypeName) Element() TypeName {
91	parts := strings.SplitN(string(n), "]", 2)
92	return TypeName(parts[len(parts)-1])
93}
94
95// Key returns the TypeName of the key of n. For slices, the return TypeName is
96// always "int", and for non slice/map types an empty TypeName is returned.
97func (n TypeName) Key() TypeName {
98	parts := strings.SplitN(string(n), "]", 2)
99	if len(parts) == 1 {
100		return TypeName("")
101	}
102
103	parts = strings.SplitN(parts[0], "[", 2)
104	if len(parts) != 2 {
105		return TypeName("")
106	} else if parts[1] == "" {
107		return TypeName("int")
108	}
109
110	return TypeName(parts[1])
111}
112
113// Pointer converts TypeName n to it's pointer type. If n is already a pointer,
114// slice, or map, it is returned unmodified.
115func (n TypeName) Pointer() TypeName {
116	ns := string(n)
117	if strings.HasPrefix(ns, "*") ||
118		strings.HasPrefix(ns, "[") ||
119		strings.HasPrefix(ns, "map[") {
120		return n
121	}
122
123	return TypeName("*" + ns)
124}
125
126// Value converts TypeName n to it's value type. If n is already a value type,
127// slice, or map it is returned unmodified.
128func (n TypeName) Value() TypeName {
129	return TypeName(strings.TrimPrefix(string(n), "*"))
130}
131