Skip to content

Commit 91a02b4

Browse files
author
Diwaker Gupta
committed
Add generator README.
1 parent fa0427a commit 91a02b4

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

generator/README.asciidoc

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
= Dropbox Go SDK Generator
2+
3+
This repository is used to generate the (currently private) https://github.com/dropbox/dropbox-sdk-go[Dropbox Go SDK]. It does not contain any auto-generated code.
4+
5+
== Requirements
6+
7+
* While not a hard requirement, this repo currently assumes `python3` in the path.
8+
* Requires https://godoc.org/golang.org/x/tools/cmd/goimports[goimports] to fix up imports in the auto-generated code
9+
10+
== Basic Setup
11+
12+
. Clone this repo
13+
. Run `git submodule init` followed by `git submodule update`
14+
. Run `./codegen.sh` to generate code under `$GOPATH/src/github.com/dropbox/dropbox-sdk-go`
15+
16+
== Generated Code
17+
18+
=== Structs
19+
20+
Babel https://github.com/braincore/babel-docs/blob/master/doc/lang_ref.rst#struct[structs] are represented as Go https://gobyexample.com/structs[structs] in a relatively straigh-forward manner.
21+
22+
----
23+
struct Account <1>
24+
"The amount of detail revealed about an account depends on the user
25+
being queried and the user making the query." <2>
26+
27+
account_id AccountId <3>
28+
"The user's unique Dropbox ID." <4>
29+
name Name <5>
30+
"Details of a user's name."
31+
----
32+
<1> A struct is defined as a Go struct
33+
<2> The documentation shows up before the struct definition
34+
<3> Each struct member is exported and also gets assigned the correct json tag. The latter is used for serializing requests and deserializing responses.
35+
<4> Member documentation appears above the member definition
36+
<5> Non-primitive types are represented as pointers to the corresponding type
37+
38+
[source,go]
39+
----
40+
// The amount of detail revealed about an account depends on the user being
41+
// queried and the user making the query. <2>
42+
type Account struct { // <1>
43+
// The user's unique Dropbox ID. <4>
44+
AccountId string `json:"account_id"` // <3>
45+
// Details of a user's name.
46+
Name *Name `json:"name"` // <5>
47+
}
48+
----
49+
50+
=== Unions
51+
52+
Babel https://github.com/braincore/babel-docs/blob/master/doc/lang_ref.rst#union[unions] are bit more complex as Go doesn't have native support for union types (tagged or otherwise). We declare a union as a Go struct with all the possible fields as pointer types, and then use the tag value to populate the correct field during deserialization. This necessitates the use of an intermedia wrapper struct for the deserialization to work correctly, see below for a concrete example.
53+
54+
----
55+
union SpaceAllocation
56+
"Space is allocated differently based on the type of account."
57+
58+
individual IndividualSpaceAllocation
59+
"The user's space allocation applies only to their individual account."
60+
team TeamSpaceAllocation
61+
"The user shares space with other members of their team."
62+
----
63+
64+
[source,go]
65+
----
66+
// Space is allocated differently based on the type of account.
67+
type SpaceAllocation struct { // <1>
68+
Tag string `json:".tag"` // <2>
69+
// The user's space allocation applies only to their individual account.
70+
Individual *IndividualSpaceAllocation `json:"individual,omitempty"` // <3>
71+
// The user shares space with other members of their team.
72+
Team *TeamSpaceAllocation `json:"team,omitempty"`
73+
}
74+
75+
func (u *SpaceAllocation) UnmarshalJSON(body []byte) error { // <4>
76+
type wrap struct { // <5>
77+
Tag string `json:".tag"`
78+
// The user's space allocation applies only to their individual account.
79+
Individual json.RawMessage `json:"individual"` // <6>
80+
// The user shares space with other members of their team.
81+
Team json.RawMessage `json:"team"`
82+
}
83+
var w wrap
84+
if err := json.Unmarshal(body, &w); err != nil { // <7>
85+
return err
86+
}
87+
u.Tag = w.Tag
88+
switch w.Tag {
89+
case "individual":
90+
{
91+
if err := json.Unmarshal(body, &u.Individual); err != nil { // <8>
92+
return err
93+
}
94+
}
95+
case "team":
96+
{
97+
if err := json.Unmarshal(body, &u.Team); err != nil {
98+
return err
99+
}
100+
}
101+
}
102+
return nil
103+
}
104+
----
105+
<1> A babel union is represented as Go struct
106+
<2> The tag value is used to determine which field is actually set
107+
<3> Possible values are represented as pointer types. Note the `omitempty` in the JSON tag; this is so values not set are automatically elided during serialization.
108+
<4> We generate a custom `UnmarshalJSON` method for union types
109+
<5> An intermedia wrapper struct is used to help with deserialization
110+
<6> Note that members of the wrapper struct are of type `RawMessage` so we can capture the body without deserializing it right away
111+
<7> When we deserialize response into the wrapper struct, it should get the tag value and the raw JSON as part of the members.
112+
<8> We then use the tag value to deserialize the `RawMessage` into the appropriate member of the union type
113+
114+
=== Struct with Enumerated Subtypes
115+
116+
Per the https://github.com/braincore/babel-docs/blob/master/doc/lang_ref.rst#struct-with-enumerated-subtypes[spec], structs with enumerated subtypes are a mechanism of inheritance:
117+
118+
> If a struct enumerates its subtypes, an instance of any subtype will satisfy the type constraint.
119+
120+
So to represent structs with enumerated subtypes in Go, we use a strategy similar to unions. The _base_ struct (the one that defines the subtypes) is represented like we represent a union above. The _subtype_ is represented as a struct that essentially duplicates all fields of the base type and includes fields specific to that subtype. Here's an example:
121+
122+
----
123+
struct Metadata
124+
"Metadata for a file or folder."
125+
126+
union
127+
file FileMetadata
128+
folder FolderMetadata
129+
deleted DeletedMetadata # Used by list_folder* and search
130+
131+
name String #<1>
132+
"The last component of the path (including extension).
133+
This never contains a slash."
134+
135+
...
136+
struct FileMetadata extends Metadata
137+
id Id? #<2>
138+
...
139+
----
140+
<1> Field common to all subtypes
141+
<2> Field specific to `FileMetadata`
142+
143+
144+
[source,go]
145+
----
146+
// Metadata for a file or folder.
147+
type Metadata struct { // <1>
148+
Tag string `json:".tag"`
149+
File *FileMetadata `json:"file,omitempty"`
150+
Folder *FolderMetadata `json:"folder,omitempty"`
151+
Deleted *DeletedMetadata `json:"deleted,omitempty"`
152+
}
153+
...
154+
155+
type FileMetadata struct {
156+
// The last component of the path (including extension). This never contains a
157+
// slash.
158+
Name string `json:"name"` // <2>
159+
...
160+
Id string `json:"id,omitempty"` // <3>
161+
}
162+
----
163+
<1> Subtype is represented like we represent unions as described above
164+
<2> Common fields are duplicated in subtypes
165+
<3> Subtype specific fields are included as usual in structs
166+
167+
== Known Issues
168+
169+
* Wildcard unions not supported
170+
* Min/max constraints on strings are not enforced

0 commit comments

Comments
 (0)