1package pgs
2
3import (
4	"fmt"
5	"sort"
6	"strconv"
7	"strings"
8	"time"
9)
10
11const outputPathKey = "output_path"
12
13// Parameters provides a convenience for accessing and modifying the parameters
14// passed into the protoc-gen-star plugin.
15type Parameters map[string]string
16
17// ParseParameters converts the raw params string provided by protoc into a
18// representative mapping.
19func ParseParameters(p string) (params Parameters) {
20	parts := strings.Split(p, ",")
21	params = make(map[string]string, len(parts))
22
23	for _, p = range parts {
24		if i := strings.Index(p, "="); i < 0 {
25			params[p] = ""
26		} else {
27			params[p[:i]] = p[i+1:]
28		}
29	}
30
31	return
32}
33
34// Clone creates an independent copy of Parameters p.
35func (p Parameters) Clone() Parameters {
36	out := make(Parameters, len(p))
37	for k, v := range p {
38		out[k] = v
39	}
40	return out
41}
42
43// OutputPath returns the protoc-gen-star special parameter. If not set in the
44// execution of protoc, "." is returned, indicating that output is relative to
45// the (unknown) output location for sub-plugins or the directory where protoc
46// is executed for a Module. Setting "output_path" during the protoc execution
47// ensures that Modules can know absolutely where to generate code.
48func (p Parameters) OutputPath() string { return p.StrDefault(outputPathKey, ".") }
49
50// SetOutputPath sets the protoc-gen-star OutputPath parameter. This is useful
51// for overriding the behavior of the ImportPath at runtime.
52func (p Parameters) SetOutputPath(path string) { p.SetStr(outputPathKey, path) }
53
54// String satisfies the string.Stringer interface. This method returns p in the
55// format it is provided to the protoc execution. Output of this function is
56// always stable; parameters are sorted before the string is emitted.
57func (p Parameters) String() string {
58	parts := make([]string, 0, len(p))
59
60	for k, v := range p {
61		if v == "" {
62			parts = append(parts, k)
63		} else {
64			parts = append(parts, fmt.Sprintf("%s=%s", k, v))
65		}
66	}
67
68	sort.Strings(parts)
69
70	return strings.Join(parts, ",")
71}
72
73// Str returns the parameter with name, returning an empty string if it is not
74// set.
75func (p Parameters) Str(name string) string { return p.StrDefault(name, "") }
76
77// StrDefault returns the parameter with name, or if it is unset, returns the
78// def default value.
79func (p Parameters) StrDefault(name string, def string) string {
80	if s, ok := p[name]; ok {
81		return s
82	}
83
84	return def
85}
86
87// SetStr sets the parameter name to s.
88func (p Parameters) SetStr(name string, s string) { p[name] = s }
89
90// Int returns the parameter with name, returning zero if it is not set. An
91// error is returned if the value cannot be parsed as an int.
92func (p Parameters) Int(name string) (int, error) { return p.IntDefault(name, 0) }
93
94// IntDefault returns the parameter with name, or if it is unset, returns the
95// def default value. An error is returned if the value cannot be parsed as an
96// int.
97func (p Parameters) IntDefault(name string, def int) (int, error) {
98	if s, ok := p[name]; ok {
99		return strconv.Atoi(s)
100	}
101	return def, nil
102}
103
104// SetInt sets the parameter name to i.
105func (p Parameters) SetInt(name string, i int) { p[name] = strconv.Itoa(i) }
106
107// Uint returns the parameter with name, returning zero if it is not set. An
108// error is returned if the value cannot be parsed as a base-10 uint.
109func (p Parameters) Uint(name string) (uint, error) { return p.UintDefault(name, 0) }
110
111// UintDefault returns the parameter with name, or if it is unset, returns the
112// def default value. An error is returned if the value cannot be parsed as a
113// base-10 uint.
114func (p Parameters) UintDefault(name string, def uint) (uint, error) {
115	if s, ok := p[name]; ok {
116		ui, err := strconv.ParseUint(s, 10, strconv.IntSize)
117		return uint(ui), err
118	}
119	return def, nil
120}
121
122// SetUint sets the parameter name to ui.
123func (p Parameters) SetUint(name string, ui uint) { p[name] = strconv.FormatUint(uint64(ui), 10) }
124
125// Float returns the parameter with name, returning zero if it is
126// not set. An error is returned if the value cannot be parsed as a float64
127func (p Parameters) Float(name string) (float64, error) { return p.FloatDefault(name, 0) }
128
129// FloatDefault returns the parameter with name, or if it is unset, returns the
130// def default value. An error is returned if the value cannot be parsed as a
131// float64.
132func (p Parameters) FloatDefault(name string, def float64) (float64, error) {
133	if s, ok := p[name]; ok {
134		return strconv.ParseFloat(s, 64)
135	}
136	return def, nil
137}
138
139// SetFloat sets the parameter name to f.
140func (p Parameters) SetFloat(name string, f float64) { p[name] = strconv.FormatFloat(f, 'g', -1, 64) }
141
142// Bool returns the parameter with name, returning false if it is not set. An
143// error is returned if the value cannot be parsed as a boolean. Empty values
144// are considered true.
145func (p Parameters) Bool(name string) (bool, error) { return p.BoolDefault(name, false) }
146
147// BoolDefault returns the parameter with name, or if it is unset, returns the
148// def default value. An error is returned if the value cannot be parsed as a
149// boolean. Empty values are considered true.
150func (p Parameters) BoolDefault(name string, def bool) (bool, error) {
151	if s, ok := p[name]; ok {
152		if strings.TrimSpace(s) == "" {
153			return true, nil
154		}
155		return strconv.ParseBool(s)
156	}
157
158	return def, nil
159}
160
161// SetBool sets the parameter name to b.
162func (p Parameters) SetBool(name string, b bool) { p[name] = strconv.FormatBool(b) }
163
164// Duration returns the parameter with name, returning zero if it is not set.
165// An error is returned if the value cannot be parsed as a time.Duration.
166func (p Parameters) Duration(name string) (time.Duration, error) { return p.DurationDefault(name, 0) }
167
168// DurationDefault returns the parameter with name, or if it is unset, returns
169// the def default value. An error is returned if the value cannot be parsed as
170// a time.Duration.
171func (p Parameters) DurationDefault(name string, def time.Duration) (time.Duration, error) {
172	if s, ok := p[name]; ok {
173		return time.ParseDuration(s)
174	}
175	return def, nil
176}
177
178// SetDuration sets the parameter name to d.
179func (p Parameters) SetDuration(name string, d time.Duration) { p[name] = d.String() }
180