-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgenerate_enum
executable file
·176 lines (151 loc) · 4.09 KB
/
generate_enum
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'fileutils'
require 'optparse'
def snake_case(str)
return str.downcase if str =~ /\A[A-Z]+\z/
str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z])([A-Z])/, '\1_\2')
.downcase
end
def make_identifier(str)
str.split(/[_-](?!\d)/).collect(&:capitalize).join
end
def generate_enumeration(file_name, package_name, states, iota_offset)
File.open(file_name, 'w') do |f|
f << file_header(package_name, states)
f << generate_states(package_name, states, iota_offset)
f << generate_valid_strings(states)
f << stringer
f << generate_parser(package_name, states)
f << generate_marshal(package_name, states)
f << unmarshal_text
end
end
def file_header(package_name, states)
<<~ENDFUNC
// Code generated by generate_enum; DO NOT EDIT.
// Package #{package_name} is an enumeration of the states #{states.map { |x| make_identifier(x) }.join(', ')}
package #{package_name}
import (
"fmt"
"encoding/json"
"reflect"
)
ENDFUNC
end
def generate_states(package_name, states, iota_offset)
first = make_identifier(states[0])
result = <<~FIRST
// Enum is an enumerated type
type Enum uint8
const (
// #{first} is an enumeration for #{package_name}.Enum
#{first} Enum = iota + #{iota_offset}
FIRST
states[1..-1].reduce(result.dup) do |memo, state|
identifier = make_identifier(state)
memo << <<-NEXT
// #{identifier} is an enumeration for #{package_name}.Enum
#{identifier}
NEXT
end << <<~FINAL
)
FINAL
end
def generate_valid_strings(states)
result = <<~FIRST
// ValidStrings is the set of strings that are valid inputs to ParseEnum
var ValidStrings = []string{
FIRST
states.reduce(result.dup) do |memo, state|
memo << <<-NEXT
#{make_identifier(state)}.String(),
NEXT
end << <<-FINAL
}
FINAL
end
def stringer
<<~ENDFUNC
// String makes Enum satisfy the Stringer interface
func (i Enum) String() string {
tmp, err := i.MarshalText()
if err == nil {
return string(tmp)
}
return ""
}
ENDFUNC
end
def generate_parser(package_name, states)
result = <<~FIRST
// ParseEnum attempts to convert a string into a Enum
func ParseEnum(name string) (Enum, error) {
switch name {
FIRST
states.reduce(result.dup) do |memo, state|
memo << <<-NEXT
case "#{state.downcase}":
return #{make_identifier(state)}, nil
NEXT
end << <<~FINAL
}
var zero Enum
return zero, fmt.Errorf("%s is not a valid #{package_name}.Enum", name)
}
FINAL
end
def generate_marshal(package_name, states)
result = <<~FIRST
// MarshalText implements the text marshaller method
func (i Enum) MarshalText() ([]byte, error) {
switch i {
FIRST
states.reduce(result.dup) do |memo, state|
memo << <<-NEXT
case #{make_identifier(state)}:
return []byte("#{state.downcase}"), nil
NEXT
end << <<~FINAL
}
return nil, fmt.Errorf("%d is not a valid #{package_name}.Enum", i)
}
FINAL
end
def unmarshal_text
<<~ENDFUNC
// UnmarshalText implements the text unmarshaller method
func (i *Enum) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParseEnum(name)
if err != nil {
return &json.UnmarshalTypeError{
Value: name,
Type: reflect.TypeOf(*i),
}
}
*i = tmp
return nil
}
ENDFUNC
end
# Check if the zero value is to be used in this enumeration
iota = 1
OptionParser.new do |opts|
opts.on('-z', 'Make first enumeration the default') do |default|
iota = default ? 0 : 1
end
end.parse!
# First argument is the package name
package_name = ARGV.shift
if package_name.to_s.empty? || ARGV.empty?
warn('Need a package name and at least one enumeration')
exit 1
end
enum_dir = File.join('enums', package_name)
file_name = File.join(enum_dir, "#{package_name}.go")
FileUtils.mkdir_p(enum_dir)
# Generate the enumeration from the rest of the arguments
generate_enumeration(file_name, package_name, ARGV, iota)
exec('gofmt', '-w', file_name)