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 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
import os
import shutil
from stone.backend import CodeBackend
from stone.ir import (
is_boolean_type,
is_list_type,
is_nullable_type,
is_primitive_type,
is_string_type,
is_struct_type,
is_union_type,
is_void_type,
)
from go_helpers import (
HEADER,
fmt_type,
fmt_var,
generate_doc,
needs_base_type,
_needs_base_type
)
class GoTypesBackend(CodeBackend):
def generate(self, api):
rsrc_folder = os.path.join(os.path.dirname(__file__), 'go_rsrc')
shutil.copy(os.path.join(rsrc_folder, 'sdk.go'),
self.target_folder_path)
for namespace in api.namespaces.values():
self._generate_namespace(namespace)
def _generate_namespace(self, namespace):
file_name = os.path.join(self.target_folder_path, namespace.name,
'types.go')
with self.output_to_relative_path(file_name):
self.emit_raw(HEADER)
self.emit()
generate_doc(self, namespace)
self.emit('package %s' % namespace.name)
self.emit()
for data_type in namespace.linearize_data_types():
self._generate_data_type(data_type)
def _generate_data_type(self, data_type):
generate_doc(self, data_type)
if is_struct_type(data_type):
self._generate_struct(data_type)
if data_type.has_enumerated_subtypes():
self._generate_base_type(data_type)
elif is_union_type(data_type):
self._generate_union(data_type)
else:
self.logger.info("Unhandled data type", data_type)
def _generate_base_type(self, base):
t = fmt_type(base).lstrip('*')
self.emit('// Is{0} is the interface type for {0} and its subtypes'.format(t))
with self.block('type Is%s interface' % t):
self.emit('Is%s()' % t)
self.emit()
self.emit('// Is{0} implements the Is{0} interface'.format(t))
self.emit("func (u *{0}) Is{0}() {{}}".format(t))
self.emit()
self._generate_union_helper(base)
self.emit("// Is{0}FromJSON converts JSON to a concrete Is{0} instance".format(t))
with self.block("func Is{0}FromJSON(data []byte) (Is{0}, error)".format(t)):
name = fmt_var(t, export=False) + 'Union'
self.emit("var t {0}".format(name))
with self.block("if err := json.Unmarshal(data, &t); err != nil"):
self.emit("return nil, err")
with self.block("switch t.Tag"):
fields = base.get_enumerated_subtypes()
for field in fields:
with self.block('case "%s":' % field.name, delim=(None, None)):
self.emit("return t.{0}, nil".format(fmt_var(field.name)))
# FIX THIS
self.emit("return nil, nil")
def _generate_struct(self, struct):
with self.block('type %s struct' % struct.name):
if struct.parent_type:
self.emit(fmt_type(struct.parent_type, struct.namespace).lstrip('*'))
for field in struct.fields:
self._generate_field(field, namespace=struct.namespace)
if struct.name in ('DownloadArg',):
self.emit('// ExtraHeaders can be used to pass Range, If-None-Match headers')
self.emit('ExtraHeaders map[string]string `json:"-"`')
self._generate_struct_builder(struct)
self.emit()
if needs_base_type(struct):
self.emit('// UnmarshalJSON deserializes into a %s instance' % struct.name)
with self.block('func (u *%s) UnmarshalJSON(b []byte) error' % struct.name):
with self.block('type wrap struct'):
for field in struct.all_fields:
self._generate_field(field, namespace=struct.namespace,
raw=_needs_base_type(field.data_type))
self.emit('var w wrap')
with self.block('if err := json.Unmarshal(b, &w); err != nil'):
self.emit('return err')
for field in struct.all_fields:
dt = field.data_type
fn = fmt_var(field.name)
tn = fmt_type(dt, namespace=struct.namespace, use_interface=True)
if _needs_base_type(dt):
if is_list_type(dt):
self.emit("u.{0} = make({1}, len(w.{0}))".format(fn, tn))
# Grab the underlying type to get the correct Is...FromJSON method
tn = fmt_type(dt.data_type, namespace=struct.namespace, use_interface=True)
with self.block("for i, e := range w.{0}".format(fn)):
self.emit("v, err := {1}FromJSON(e)".format(fn, tn))
with self.block('if err != nil'):
self.emit('return err')
self.emit("u.{0}[i] = v".format(fn))
else:
self.emit("{0}, err := {1}FromJSON(w.{0})".format(fn, tn))
with self.block('if err != nil'):
self.emit('return err')
self.emit("u.{0} = {0}".format(fn))
else:
self.emit("u.{0} = w.{0}".format(fn))
self.emit('return nil')
def _generate_struct_builder(self, struct):
fields = ["%s %s" % (fmt_var(field.name),
fmt_type(field.data_type, struct.namespace,
use_interface=True))
for field in struct.all_required_fields]
self.emit('// New{0} returns a new {0} instance'.format(struct.name))
signature = "func New{0}({1}) *{0}".format(struct.name, ', '.join(fields))
with self.block(signature):
self.emit('s := new({0})'.format(struct.name))
for field in struct.all_required_fields:
field_name = fmt_var(field.name)
self.emit("s.{0} = {0}".format(field_name))
for field in struct.all_optional_fields:
if field.has_default:
if is_primitive_type(field.data_type):
default = field.default
if is_boolean_type(field.data_type):
default = str(default).lower()
if is_string_type(field.data_type):
default = '"{}"'.format(default)
self.emit('s.{0} = {1}'.format(fmt_var(field.name), default))
elif is_union_type(field.data_type):
self.emit('s.%s = &%s{Tagged:dropbox.Tagged{Tag: "%s"}}' %
(fmt_var(field.name),
fmt_type(field.data_type, struct.namespace).lstrip('*'),
field.default.tag_name))
self.emit('return s')
self.emit()
def _generate_field(self, field, union_field=False, namespace=None, raw=False):
generate_doc(self, field)
field_name = fmt_var(field.name)
type_name = fmt_type(field.data_type, namespace, use_interface=True, raw=raw)
json_tag = '`json:"%s"`' % field.name
if is_nullable_type(field.data_type) or union_field:
json_tag = '`json:"%s,omitempty"`' % field.name
self.emit('%s %s %s' % (field_name, type_name, json_tag))
def _generate_union(self, union):
self._generate_union_helper(union)
def _generate_union_helper(self, u):
name = u.name
namespace = u.namespace
# Unions can be inherited, but don't need to be polymorphic.
# So let's flatten out all the inherited fields.
fields = u.all_fields
if is_struct_type(u) and u.has_enumerated_subtypes():
name = fmt_var(name, export=False) + 'Union'
fields = u.get_enumerated_subtypes()
with self.block('type %s struct' % name):
self.emit('dropbox.Tagged')
for field in fields:
if is_void_type(field.data_type):
continue
self._generate_field(field, union_field=True,
namespace=namespace)
self.emit()
self.emit('// Valid tag values for %s' % fmt_var(u.name))
with self.block('const', delim=('(', ')')):
for field in fields:
self.emit('%s%s = "%s"' % (fmt_var(u.name), fmt_var(field.name), field.name))
self.emit()
num_void_fields = sum([is_void_type(f.data_type) for f in fields])
# Simple structure, no need in UnmarshalJSON
if len(fields) == num_void_fields:
return
self.emit('// UnmarshalJSON deserializes into a %s instance' % name)
with self.block('func (u *%s) UnmarshalJSON(body []byte) error' % name):
with self.block('type wrap struct'):
self.emit('dropbox.Tagged')
for field in fields:
if is_void_type(field.data_type) or (
is_struct_type(field.data_type) and not _needs_base_type(field.data_type)):
# pure structures are flattened in the containing union json blob and thus are loaded from body
continue
# sub-unions must be handled as RawMessage, which will be loaded into correct implementation later
self._generate_field(field, union_field=True,
namespace=namespace, raw=_needs_base_type(field.data_type))
self.emit('var w wrap')
self.emit('var err error')
with self.block('if err = json.Unmarshal(body, &w); err != nil'):
self.emit('return err')
self.emit('u.Tag = w.Tag')
with self.block('switch u.Tag'):
for field in fields:
if is_void_type(field.data_type):
continue
field_name = fmt_var(field.name)
with self.block('case "%s":' % field.name, delim=(None, None)):
if _needs_base_type(field.data_type):
with self.block("if u.{0}, err = Is{1}FromJSON(w.{0}); err != nil"
.format(field_name, field.data_type.name)):
self.emit("return err")
elif is_struct_type(field.data_type):
with self.block('if err = json.Unmarshal(body, &u.{0}); err != nil'
.format(field_name)):
self.emit("return err")
else:
self.emit('u.{0} = w.{0}'.format(field_name))
self.emit('return nil')
self.emit()
|