PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/google-api-go-generator/gen.go

https://github.com/ninnemana/google-api-go-client
Go | 1627 lines | 1391 code | 177 blank | 59 comment | 330 complexity | e9e32db973e7ceebae6c119d8809e2af MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "bytes"
  7. "encoding/json"
  8. "errors"
  9. "flag"
  10. "fmt"
  11. "go/format"
  12. "io/ioutil"
  13. "log"
  14. "net/http"
  15. "net/url"
  16. "os"
  17. "os/exec"
  18. "path/filepath"
  19. "sort"
  20. "strings"
  21. "unicode"
  22. )
  23. // goGenVersion is the version of the Go code generator
  24. const goGenVersion = "0.5"
  25. var (
  26. apiToGenerate = flag.String("api", "*", "The API ID to generate, like 'tasks:v1'. A value of '*' means all.")
  27. useCache = flag.Bool("cache", true, "Use cache of discovered Google API discovery documents.")
  28. genDir = flag.String("gendir", "", "Directory to use to write out generated Go files")
  29. build = flag.Bool("build", false, "Compile generated packages.")
  30. install = flag.Bool("install", false, "Install generated packages.")
  31. apisURL = flag.String("discoveryurl", "https://www.googleapis.com/discovery/v1/apis", "URL to root discovery document")
  32. publicOnly = flag.Bool("publiconly", true, "Only build public, released APIs. Only applicable for Google employees.")
  33. jsonFile = flag.String("api_json_file", "", "If non-empty, the path to a local file on disk containing the API to generate. Exclusive with setting --api.")
  34. output = flag.String("output", "", "(optional) Path to source output file. If not specified, the API name and version are used to construct an output path (e.g. tasks/v1).")
  35. googleAPIPkg = flag.String("googleapi_pkg", "code.google.com/p/google-api-go-client/googleapi", "Go package path of the 'googleapi' support package.")
  36. )
  37. // API represents an API to generate, as well as its state while it's
  38. // generating.
  39. type API struct {
  40. ID string `json:"id"`
  41. Name string `json:"name"`
  42. Version string `json:"version"`
  43. Title string `json:"title"`
  44. DiscoveryLink string `json:"discoveryLink"` // relative
  45. RootURL string `json:"rootUrl"`
  46. ServicePath string `json:"servicePath"`
  47. Preferred bool `json:"preferred"`
  48. m map[string]interface{}
  49. forceJSON []byte // if non-nil, the JSON schema file. else fetched.
  50. usedNames namePool
  51. schemas map[string]*Schema // apiName -> schema
  52. p func(format string, args ...interface{}) // print raw
  53. pn func(format string, args ...interface{}) // print with indent and newline
  54. }
  55. func (a *API) sortedSchemaNames() (names []string) {
  56. for name := range a.schemas {
  57. names = append(names, name)
  58. }
  59. sort.Strings(names)
  60. return
  61. }
  62. type AllAPIs struct {
  63. Items []*API `json:"items"`
  64. }
  65. type generateError struct {
  66. api *API
  67. error error
  68. }
  69. func (e *generateError) Error() string {
  70. return fmt.Sprintf("API %s failed to generate code: %v", e.api.ID, e.error)
  71. }
  72. type compileError struct {
  73. api *API
  74. output string
  75. }
  76. func (e *compileError) Error() string {
  77. return fmt.Sprintf("API %s failed to compile:\n%v", e.api.ID, e.output)
  78. }
  79. func main() {
  80. flag.Parse()
  81. if *install {
  82. *build = true
  83. }
  84. var (
  85. apiIds = []string{}
  86. matches = []*API{}
  87. errors = []error{}
  88. )
  89. for _, api := range getAPIs() {
  90. apiIds = append(apiIds, api.ID)
  91. if !api.want() {
  92. continue
  93. }
  94. matches = append(matches, api)
  95. log.Printf("Generating API %s", api.ID)
  96. err := api.WriteGeneratedCode()
  97. if err != nil {
  98. errors = append(errors, &generateError{api, err})
  99. continue
  100. }
  101. if *build {
  102. var args []string
  103. if *install {
  104. args = append(args, "install")
  105. } else {
  106. args = append(args, "build")
  107. }
  108. args = append(args, api.Target())
  109. out, err := exec.Command("go", args...).CombinedOutput()
  110. if err != nil {
  111. errors = append(errors, &compileError{api, string(out)})
  112. }
  113. }
  114. }
  115. if len(matches) == 0 {
  116. log.Fatalf("No APIs matched %q; options are %v", *apiToGenerate, apiIds)
  117. }
  118. if len(errors) > 0 {
  119. log.Printf("%d API(s) failed to generate or compile:", len(errors))
  120. for _, ce := range errors {
  121. log.Printf(ce.Error())
  122. }
  123. os.Exit(1)
  124. }
  125. }
  126. func (a *API) want() bool {
  127. if strings.Contains(a.ID, "buzz") {
  128. // R.I.P.
  129. return false
  130. }
  131. if strings.Contains(a.ID, "fusiontables") {
  132. // TODO(bradfitz): broken codegen.
  133. return false
  134. }
  135. return *apiToGenerate == "*" || *apiToGenerate == a.ID
  136. }
  137. func getAPIs() []*API {
  138. if *jsonFile != "" {
  139. return getAPIsFromFile()
  140. }
  141. var all AllAPIs
  142. disco := slurpURL(*apisURL)
  143. if err := json.Unmarshal(disco, &all); err != nil {
  144. log.Fatalf("error decoding JSON in %s: %v", apisURL, err)
  145. }
  146. if !*publicOnly && *apiToGenerate != "*" {
  147. parts := strings.SplitN(*apiToGenerate, ":", 2)
  148. apiName := parts[0]
  149. apiVersion := parts[1]
  150. all.Items = append(all.Items, &API{
  151. ID: *apiToGenerate,
  152. Name: apiName,
  153. Version: apiVersion,
  154. DiscoveryLink: fmt.Sprintf("./apis/%s/%s/rest", apiName, apiVersion),
  155. })
  156. }
  157. return all.Items
  158. }
  159. // getAPIsFromFile handles the case of generating exactly one API
  160. // from the flag given in --api_json_file
  161. func getAPIsFromFile() []*API {
  162. if *apiToGenerate != "*" {
  163. log.Fatalf("Can't set --api with --api_json_file.")
  164. }
  165. if !*publicOnly {
  166. log.Fatalf("Can't set --publiconly with --api_json_file.")
  167. }
  168. a, err := apiFromFile(*jsonFile)
  169. if err != nil {
  170. log.Fatal(err)
  171. }
  172. return []*API{a}
  173. }
  174. func apiFromFile(file string) (*API, error) {
  175. jsonBytes, err := ioutil.ReadFile(file)
  176. if err != nil {
  177. return nil, fmt.Errorf("Error reading %s: %v", file, err)
  178. }
  179. a := &API{
  180. forceJSON: jsonBytes,
  181. }
  182. if err := json.Unmarshal(jsonBytes, a); err != nil {
  183. return nil, fmt.Errorf("Decoding JSON in %s: %v", file, err)
  184. }
  185. return a, nil
  186. }
  187. func writeFile(file string, contents []byte) error {
  188. // Don't write it if the contents are identical.
  189. existing, err := ioutil.ReadFile(file)
  190. if err == nil && bytes.Equal(existing, contents) {
  191. return nil
  192. }
  193. return ioutil.WriteFile(file, contents, 0644)
  194. }
  195. func slurpURL(urlStr string) []byte {
  196. diskFile := filepath.Join(os.TempDir(), "google-api-cache-"+url.QueryEscape(urlStr))
  197. if *useCache {
  198. bs, err := ioutil.ReadFile(diskFile)
  199. if err == nil && len(bs) > 0 {
  200. return bs
  201. }
  202. }
  203. req, err := http.NewRequest("GET", urlStr, nil)
  204. if err != nil {
  205. log.Fatal(err)
  206. }
  207. if *publicOnly {
  208. req.Header.Add("X-User-IP", "0.0.0.0") // hack
  209. }
  210. res, err := http.DefaultClient.Do(req)
  211. if err != nil {
  212. log.Fatalf("Error fetching URL %s: %v", urlStr, err)
  213. }
  214. bs, err := ioutil.ReadAll(res.Body)
  215. if err != nil {
  216. log.Fatalf("Error reading body of URL %s: %v", urlStr, err)
  217. }
  218. if *useCache {
  219. if err := ioutil.WriteFile(diskFile, bs, 0666); err != nil {
  220. log.Printf("Warning: failed to write JSON of %s to disk file %s: %v", urlStr, diskFile, err)
  221. }
  222. }
  223. return bs
  224. }
  225. func panicf(format string, args ...interface{}) {
  226. panic(fmt.Sprintf(format, args...))
  227. }
  228. // namePool keeps track of used names and assigns free ones based on a
  229. // preferred name
  230. type namePool struct {
  231. m map[string]bool // lazily initialized
  232. }
  233. func (p *namePool) Get(preferred string) string {
  234. if p.m == nil {
  235. p.m = make(map[string]bool)
  236. }
  237. name := preferred
  238. tries := 0
  239. for p.m[name] {
  240. tries++
  241. name = fmt.Sprintf("%s%d", preferred, tries)
  242. }
  243. p.m[name] = true
  244. return name
  245. }
  246. func (a *API) SourceDir() string {
  247. if *genDir == "" {
  248. paths := filepath.SplitList(os.Getenv("GOPATH"))
  249. if len(paths) > 0 && paths[0] != "" {
  250. *genDir = filepath.Join(paths[0], "src", "code.google.com", "p", "google-api-go-client")
  251. }
  252. }
  253. return filepath.Join(*genDir, a.Package(), a.Version)
  254. }
  255. func (a *API) DiscoveryURL() string {
  256. if a.DiscoveryLink == "" {
  257. log.Fatalf("API %s has no DiscoveryLink", a.ID)
  258. }
  259. base, _ := url.Parse(*apisURL)
  260. u, err := base.Parse(a.DiscoveryLink)
  261. if err != nil {
  262. log.Fatalf("API %s has bogus DiscoveryLink %s: %v", a.ID, a.DiscoveryLink, err)
  263. }
  264. return u.String()
  265. }
  266. func (a *API) Package() string {
  267. return strings.ToLower(a.Name)
  268. }
  269. func (a *API) Target() string {
  270. return fmt.Sprintf("code.google.com/p/google-api-go-client/%s/%s", a.Package(), a.Version)
  271. }
  272. // GetName returns a free top-level function/type identifier in the package.
  273. // It tries to return your preferred match if it's free.
  274. func (a *API) GetName(preferred string) string {
  275. return a.usedNames.Get(preferred)
  276. }
  277. func (a *API) apiBaseURL() string {
  278. if a.RootURL != "" {
  279. return a.RootURL + a.ServicePath
  280. }
  281. return resolveRelative(*apisURL, jstr(a.m, "basePath"))
  282. }
  283. func (a *API) needsDataWrapper() bool {
  284. for _, feature := range jstrlist(a.m, "features") {
  285. if feature == "dataWrapper" {
  286. return true
  287. }
  288. }
  289. return false
  290. }
  291. func (a *API) jsonBytes() []byte {
  292. if v := a.forceJSON; v != nil {
  293. return v
  294. }
  295. return slurpURL(a.DiscoveryURL())
  296. }
  297. func (a *API) WriteGeneratedCode() error {
  298. outdir := a.SourceDir()
  299. err := os.MkdirAll(outdir, 0755)
  300. if err != nil {
  301. return fmt.Errorf("failed to Mkdir %s: %v", outdir, err)
  302. }
  303. pkg := a.Package()
  304. writeFile(filepath.Join(outdir, a.Package()+"-api.json"), a.jsonBytes())
  305. genfilename := *output
  306. if genfilename == "" {
  307. genfilename = filepath.Join(outdir, pkg+"-gen.go")
  308. }
  309. code, err := a.GenerateCode()
  310. errw := writeFile(genfilename, code)
  311. if err == nil {
  312. err = errw
  313. }
  314. return err
  315. }
  316. func (a *API) GenerateCode() ([]byte, error) {
  317. pkg := a.Package()
  318. a.m = make(map[string]interface{})
  319. m := a.m
  320. jsonBytes := a.jsonBytes()
  321. err := json.Unmarshal(jsonBytes, &a.m)
  322. if err != nil {
  323. return nil, err
  324. }
  325. // Buffer the output in memory, for gofmt'ing later in the defer.
  326. var buf bytes.Buffer
  327. a.p = func(format string, args ...interface{}) {
  328. _, err := fmt.Fprintf(&buf, format, args...)
  329. if err != nil {
  330. panic(err)
  331. }
  332. }
  333. a.pn = func(format string, args ...interface{}) {
  334. a.p(format+"\n", args...)
  335. }
  336. p, pn := a.p, a.pn
  337. reslist := a.Resources(a.m, "")
  338. p("// Package %s provides access to the %s.\n", pkg, jstr(m, "title"))
  339. if docs := jstr(m, "documentationLink"); docs != "" {
  340. p("//\n")
  341. p("// See %s\n", docs)
  342. }
  343. p("//\n// Usage example:\n")
  344. p("//\n")
  345. p("// import %q\n", a.Target())
  346. p("// ...\n")
  347. p("// %sService, err := %s.New(oauthHttpClient)\n", pkg, pkg)
  348. p("package %s\n", pkg)
  349. p("\n")
  350. p("import (\n")
  351. for _, pkg := range []string{
  352. "bytes",
  353. *googleAPIPkg,
  354. "encoding/json",
  355. "errors",
  356. "fmt",
  357. "io",
  358. "net/http",
  359. "net/url",
  360. "strconv",
  361. "strings",
  362. } {
  363. p("\t%q\n", pkg)
  364. }
  365. p(")\n\n")
  366. pn("// Always reference these packages, just in case the auto-generated code")
  367. pn("// below doesn't.")
  368. pn("var _ = bytes.NewBuffer")
  369. pn("var _ = strconv.Itoa")
  370. pn("var _ = fmt.Sprintf")
  371. pn("var _ = json.NewDecoder")
  372. pn("var _ = io.Copy")
  373. pn("var _ = url.Parse")
  374. pn("var _ = googleapi.Version")
  375. pn("var _ = errors.New")
  376. pn("var _ = strings.Replace")
  377. pn("")
  378. pn("const apiId = %q", jstr(m, "id"))
  379. pn("const apiName = %q", jstr(m, "name"))
  380. pn("const apiVersion = %q", jstr(m, "version"))
  381. p("const basePath = %q\n", a.apiBaseURL())
  382. p("\n")
  383. a.generateScopeConstants()
  384. a.GetName("New") // ignore return value; we're the first caller
  385. pn("func New(client *http.Client) (*Service, error) {")
  386. pn("if client == nil { return nil, errors.New(\"client is nil\") }")
  387. pn("s := &Service{client: client, BasePath: basePath}")
  388. for _, res := range reslist { // add top level resources.
  389. pn("s.%s = New%s(s)", res.GoField(), res.GoType())
  390. }
  391. pn("return s, nil")
  392. pn("}")
  393. a.GetName("Service") // ignore return value; no user-defined names yet
  394. p("\ntype Service struct {\n")
  395. p("\tclient *http.Client\n")
  396. p("\tBasePath string // API endpoint base URL\n")
  397. for _, res := range reslist {
  398. p("\n\t%s\t*%s\n", res.GoField(), res.GoType())
  399. }
  400. p("}\n")
  401. for _, res := range reslist {
  402. res.generateType()
  403. }
  404. a.PopulateSchemas()
  405. for _, name := range a.sortedSchemaNames() {
  406. a.schemas[name].writeSchemaCode()
  407. }
  408. for _, meth := range a.APIMethods() {
  409. meth.generateCode()
  410. }
  411. for _, res := range reslist {
  412. res.generateMethods()
  413. }
  414. clean, err := format.Source(buf.Bytes())
  415. if err != nil {
  416. return buf.Bytes(), err
  417. }
  418. return clean, nil
  419. }
  420. func (a *API) generateScopeConstants() {
  421. auth := jobj(a.m, "auth")
  422. if auth == nil {
  423. return
  424. }
  425. oauth2 := jobj(auth, "oauth2")
  426. if oauth2 == nil {
  427. return
  428. }
  429. scopes := jobj(oauth2, "scopes")
  430. if scopes == nil || len(scopes) == 0 {
  431. return
  432. }
  433. a.p("// OAuth2 scopes used by this API.\n")
  434. a.p("const (\n")
  435. n := 0
  436. for _, scopeName := range sortedKeys(scopes) {
  437. mi := scopes[scopeName]
  438. if n > 0 {
  439. a.p("\n")
  440. }
  441. n++
  442. ident := scopeIdentifierFromURL(scopeName)
  443. if des := jstr(mi.(map[string]interface{}), "description"); des != "" {
  444. a.p("%s", asComment("\t", des))
  445. }
  446. a.p("\t%s = %q\n", ident, scopeName)
  447. }
  448. a.p(")\n\n")
  449. }
  450. func scopeIdentifierFromURL(urlStr string) string {
  451. const prefix = "https://www.googleapis.com/auth/"
  452. if !strings.HasPrefix(urlStr, prefix) {
  453. log.Fatalf("Unexpected oauth2 scope %q doesn't start with %q", urlStr, prefix)
  454. }
  455. ident := validGoIdentifer(initialCap(urlStr[len(prefix):])) + "Scope"
  456. return ident
  457. }
  458. type Schema struct {
  459. api *API
  460. m map[string]interface{} // original JSON map
  461. typ *Type // lazily populated by Type
  462. apiName string // the native API-defined name of this type
  463. goName string // lazily populated by GoName
  464. goReturnType string // lazily populated by GoReturnType
  465. }
  466. type Property struct {
  467. s *Schema // property of which schema
  468. apiName string // the native API-defined name of this property
  469. m map[string]interface{} // original JSON map
  470. typ *Type // lazily populated by Type
  471. }
  472. func (p *Property) Type() *Type {
  473. if p.typ == nil {
  474. p.typ = &Type{api: p.s.api, m: p.m}
  475. }
  476. return p.typ
  477. }
  478. func (p *Property) GoName() string {
  479. return initialCap(p.apiName)
  480. }
  481. func (p *Property) APIName() string {
  482. return p.apiName
  483. }
  484. func (p *Property) Description() string {
  485. return jstr(p.m, "description")
  486. }
  487. type Type struct {
  488. m map[string]interface{} // JSON map containing key "type" and maybe "items", "properties"
  489. api *API
  490. }
  491. func (t *Type) apiType() string {
  492. // Note: returns "" on reference types
  493. if t, ok := t.m["type"].(string); ok {
  494. return t
  495. }
  496. return ""
  497. }
  498. func (t *Type) apiTypeFormat() string {
  499. if f, ok := t.m["format"].(string); ok {
  500. return f
  501. }
  502. return ""
  503. }
  504. func (t *Type) isIntAsString() bool {
  505. return t.apiType() == "string" && strings.Contains(t.apiTypeFormat(), "int")
  506. }
  507. func (t *Type) asSimpleGoType() (goType string, ok bool) {
  508. return simpleTypeConvert(t.apiType(), t.apiTypeFormat())
  509. }
  510. func (t *Type) String() string {
  511. return fmt.Sprintf("[type=%q, map=%s]", t.apiType(), prettyJSON(t.m))
  512. }
  513. func (t *Type) AsGo() string {
  514. if t, ok := t.asSimpleGoType(); ok {
  515. return t
  516. }
  517. if at, ok := t.ArrayType(); ok {
  518. if at.apiType() == "string" {
  519. switch at.apiTypeFormat() {
  520. case "int64":
  521. return "googleapi.Int64s"
  522. case "uint64":
  523. return "googleapi.Uint64s"
  524. case "int32":
  525. return "googleapi.Int32s"
  526. case "uint32":
  527. return "googleapi.Uint32s"
  528. case "float64":
  529. return "googleapi.Float64s"
  530. default:
  531. return "[]" + at.AsGo()
  532. }
  533. }
  534. return "[]" + at.AsGo()
  535. }
  536. if ref, ok := t.Reference(); ok {
  537. s := t.api.schemas[ref]
  538. if s == nil {
  539. panic(fmt.Sprintf("in Type.AsGo(), failed to find referenced type %q for %s",
  540. ref, prettyJSON(t.m)))
  541. }
  542. return s.Type().AsGo()
  543. }
  544. if typ, ok := t.MapType(); ok {
  545. return typ
  546. }
  547. if t.IsStruct() {
  548. if apiName, ok := t.m["_apiName"].(string); ok {
  549. s := t.api.schemas[apiName]
  550. if s == nil {
  551. panic(fmt.Sprintf("in Type.AsGo, _apiName of %q didn't point to a valid schema; json: %s",
  552. apiName, prettyJSON(t.m)))
  553. }
  554. return "*" + s.GoName()
  555. }
  556. panic("in Type.AsGo, no _apiName found for struct type " + prettyJSON(t.m))
  557. }
  558. panic("unhandled Type.AsGo for " + prettyJSON(t.m))
  559. }
  560. func (t *Type) IsSimple() bool {
  561. _, ok := simpleTypeConvert(t.apiType(), t.apiTypeFormat())
  562. return ok
  563. }
  564. func (t *Type) IsStruct() bool {
  565. return t.apiType() == "object"
  566. }
  567. func (t *Type) Reference() (apiName string, ok bool) {
  568. apiName = jstr(t.m, "$ref")
  569. ok = apiName != ""
  570. return
  571. }
  572. func (t *Type) IsMap() bool {
  573. _, ok := t.MapType()
  574. return ok
  575. }
  576. // MapType checks if the current node is a map and if true, it returns the Go type for the map, such as map[string]string.
  577. func (t *Type) MapType() (typ string, ok bool) {
  578. props := jobj(t.m, "additionalProperties")
  579. if props == nil {
  580. return "", false
  581. }
  582. s := jstr(props, "type")
  583. if s == "string" {
  584. return "map[string]string", true
  585. }
  586. if s != "array" {
  587. // TODO(gmlewis): support maps to any type.
  588. log.Printf("Warning: found map to type %q which is not implemented yet.", s)
  589. return "", false
  590. }
  591. items := jobj(props, "items")
  592. if items == nil {
  593. return "", false
  594. }
  595. s = jstr(items, "type")
  596. if s != "string" {
  597. // TODO(gmlewis): support maps to any type.
  598. log.Printf("Warning: found map of arrays of type %q which is not implemented yet.", s)
  599. return "", false
  600. }
  601. return "map[string][]string", true
  602. }
  603. func (t *Type) IsReference() bool {
  604. return jstr(t.m, "$ref") != ""
  605. }
  606. func (t *Type) ReferenceSchema() (s *Schema, ok bool) {
  607. apiName, ok := t.Reference()
  608. if !ok {
  609. return
  610. }
  611. s = t.api.schemas[apiName]
  612. if s == nil {
  613. panicf("failed to find t.api.schemas[%q] while resolving reference",
  614. apiName)
  615. }
  616. return s, true
  617. }
  618. func (t *Type) ArrayType() (elementType *Type, ok bool) {
  619. if t.apiType() != "array" {
  620. return
  621. }
  622. items := jobj(t.m, "items")
  623. if items == nil {
  624. panicf("can't handle array type missing its 'items' key. map is %#v", t.m)
  625. }
  626. return &Type{api: t.api, m: items}, true
  627. }
  628. func (s *Schema) Type() *Type {
  629. if s.typ == nil {
  630. s.typ = &Type{api: s.api, m: s.m}
  631. }
  632. return s.typ
  633. }
  634. func (s *Schema) properties() []*Property {
  635. if !s.Type().IsStruct() {
  636. panic("called properties on non-object schema")
  637. }
  638. pl := []*Property{}
  639. propMap := jobj(s.m, "properties")
  640. for _, name := range sortedKeys(propMap) {
  641. m := propMap[name].(map[string]interface{})
  642. pl = append(pl, &Property{
  643. s: s,
  644. m: m,
  645. apiName: name,
  646. })
  647. }
  648. return pl
  649. }
  650. func (s *Schema) populateSubSchemas() (outerr error) {
  651. defer func() {
  652. r := recover()
  653. if r == nil {
  654. return
  655. }
  656. outerr = fmt.Errorf("%v", r)
  657. }()
  658. addSubStruct := func(subApiName string, t *Type) {
  659. if s.api.schemas[subApiName] != nil {
  660. panic("dup schema apiName: " + subApiName)
  661. }
  662. subm := t.m
  663. subm["_apiName"] = subApiName
  664. subs := &Schema{
  665. api: s.api,
  666. m: subm,
  667. typ: t,
  668. apiName: subApiName,
  669. }
  670. s.api.schemas[subApiName] = subs
  671. err := subs.populateSubSchemas()
  672. if err != nil {
  673. panicf("in sub-struct %q: %v", subApiName, err)
  674. }
  675. }
  676. if s.Type().IsStruct() {
  677. for _, p := range s.properties() {
  678. if p.Type().IsSimple() || p.Type().IsMap() {
  679. continue
  680. }
  681. if at, ok := p.Type().ArrayType(); ok {
  682. if at.IsSimple() || at.IsReference() {
  683. continue
  684. }
  685. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.apiName)
  686. if at.IsStruct() {
  687. addSubStruct(subApiName, at) // was p.Type()?
  688. continue
  689. }
  690. if _, ok := at.ArrayType(); ok {
  691. addSubStruct(subApiName, at)
  692. continue
  693. }
  694. panicf("Unknown property array type for %q: %s", subApiName, at)
  695. continue
  696. }
  697. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.apiName)
  698. if p.Type().IsStruct() {
  699. addSubStruct(subApiName, p.Type())
  700. continue
  701. }
  702. if p.Type().IsReference() {
  703. continue
  704. }
  705. panicf("Unknown type for %q: %s", subApiName, p.Type())
  706. }
  707. return
  708. }
  709. if at, ok := s.Type().ArrayType(); ok {
  710. if at.IsSimple() || at.IsReference() {
  711. return
  712. }
  713. subApiName := fmt.Sprintf("%s.Item", s.apiName)
  714. if at.IsStruct() {
  715. addSubStruct(subApiName, at)
  716. return
  717. }
  718. if at, ok := at.ArrayType(); ok {
  719. if at.IsSimple() || at.IsReference() {
  720. return
  721. }
  722. addSubStruct(subApiName, at)
  723. return
  724. }
  725. panicf("Unknown array type for %q: %s", subApiName, at)
  726. return
  727. }
  728. if s.Type().IsSimple() || s.Type().IsReference() {
  729. return
  730. }
  731. fmt.Fprintf(os.Stderr, "in populateSubSchemas, schema is: %s", prettyJSON(s.m))
  732. panicf("populateSubSchemas: unsupported type for schema %q", s.apiName)
  733. panic("unreachable")
  734. }
  735. // GoName returns (or creates and returns) the bare Go name
  736. // of the apiName, making sure that it's a proper Go identifier
  737. // and doesn't conflict with an existing name.
  738. func (s *Schema) GoName() string {
  739. if s.goName == "" {
  740. if name, ok := s.Type().MapType(); ok {
  741. s.goName = name
  742. } else {
  743. s.goName = s.api.GetName(initialCap(s.apiName))
  744. }
  745. }
  746. return s.goName
  747. }
  748. // GoReturnType returns the Go type to use as the return type.
  749. // If a type is a struct, it will return *StructType,
  750. // for a map it will return map[string]ValueType,
  751. // for (not yet supported) slices it will return []ValueType.
  752. func (s *Schema) GoReturnType() string {
  753. if s.goReturnType == "" {
  754. if s.Type().IsMap() {
  755. s.goReturnType = s.GoName()
  756. } else {
  757. s.goReturnType = "*" + s.GoName()
  758. }
  759. }
  760. return s.goReturnType
  761. }
  762. func (s *Schema) writeSchemaCode() {
  763. if s.Type().IsStruct() && !s.Type().IsMap() {
  764. s.writeSchemaStruct()
  765. return
  766. }
  767. if _, ok := s.Type().ArrayType(); ok {
  768. log.Printf("TODO writeSchemaCode for arrays for %s", s.GoName())
  769. return
  770. }
  771. if destSchema, ok := s.Type().ReferenceSchema(); ok {
  772. // Convert it to a struct using embedding.
  773. s.api.p("\ntype %s struct {\n", s.GoName())
  774. s.api.p("\t%s\n", destSchema.GoName())
  775. s.api.p("}\n")
  776. return
  777. }
  778. if s.Type().IsSimple() || s.Type().IsMap() {
  779. return
  780. }
  781. fmt.Fprintf(os.Stderr, "in writeSchemaCode, schema is: %s", prettyJSON(s.m))
  782. panicf("writeSchemaCode: unsupported type for schema %q", s.apiName)
  783. }
  784. func (s *Schema) writeSchemaStruct() {
  785. // TODO: description
  786. s.api.p("\ntype %s struct {\n", s.GoName())
  787. for i, p := range s.properties() {
  788. if i > 0 {
  789. s.api.p("\n")
  790. }
  791. pname := p.GoName()
  792. if des := p.Description(); des != "" {
  793. s.api.p("%s", asComment("\t", fmt.Sprintf("%s: %s", pname, des)))
  794. }
  795. var extraOpt string
  796. if p.Type().isIntAsString() {
  797. extraOpt += ",string"
  798. }
  799. s.api.p("\t%s %s `json:\"%s,omitempty%s\"`\n", pname, p.Type().AsGo(), p.APIName(), extraOpt)
  800. }
  801. s.api.p("}\n")
  802. }
  803. // PopulateSchemas reads all the API types ("schemas") from the JSON file
  804. // and converts them to *Schema instances, returning an identically
  805. // keyed map, additionally containing subresources. For instance,
  806. //
  807. // A resource "Foo" of type "object" with a property "bar", also of type
  808. // "object" (an anonymous sub-resource), will get a synthetic API name
  809. // of "Foo.bar".
  810. //
  811. // A resource "Foo" of type "array" with an "items" of type "object"
  812. // will get a synthetic API name of "Foo.Item".
  813. func (a *API) PopulateSchemas() {
  814. m := jobj(a.m, "schemas")
  815. if a.schemas != nil {
  816. panic("")
  817. }
  818. a.schemas = make(map[string]*Schema)
  819. for name, mi := range m {
  820. s := &Schema{
  821. api: a,
  822. apiName: name,
  823. m: mi.(map[string]interface{}),
  824. }
  825. // And a little gross hack, so a map alone is good
  826. // enough to get its apiName:
  827. s.m["_apiName"] = name
  828. a.schemas[name] = s
  829. err := s.populateSubSchemas()
  830. if err != nil {
  831. panicf("Error populating schema with API name %q: %v", name, err)
  832. }
  833. }
  834. }
  835. type Resource struct {
  836. api *API
  837. name string
  838. parent string
  839. m map[string]interface{}
  840. resources []*Resource
  841. }
  842. func (r *Resource) generateType() {
  843. p, pn := r.api.p, r.api.pn
  844. t := r.GoType()
  845. pn(fmt.Sprintf("func New%s(s *Service) *%s {", t, t))
  846. pn("rs := &%s{s : s}", t)
  847. for _, res := range r.resources {
  848. pn("rs.%s = New%s(s)", res.GoField(), res.GoType())
  849. }
  850. pn("return rs")
  851. pn("}")
  852. p("\ntype %s struct {\n", t)
  853. p("\ts *Service\n")
  854. for _, res := range r.resources {
  855. p("\n\t%s\t*%s\n", res.GoField(), res.GoType())
  856. }
  857. p("}\n")
  858. for _, res := range r.resources {
  859. res.generateType()
  860. }
  861. }
  862. func (r *Resource) generateMethods() {
  863. for _, meth := range r.Methods() {
  864. meth.generateCode()
  865. }
  866. for _, res := range r.resources {
  867. res.generateMethods()
  868. }
  869. }
  870. func (r *Resource) GoField() string {
  871. return initialCap(r.name)
  872. }
  873. func (r *Resource) GoType() string {
  874. return initialCap(fmt.Sprintf("%s.%s", r.parent, r.name)) + "Service"
  875. }
  876. func (r *Resource) Methods() []*Method {
  877. ms := []*Method{}
  878. methMap := jobj(r.m, "methods")
  879. for _, mname := range sortedKeys(methMap) {
  880. mi := methMap[mname]
  881. ms = append(ms, &Method{
  882. api: r.api,
  883. r: r,
  884. name: mname,
  885. m: mi.(map[string]interface{}),
  886. })
  887. }
  888. return ms
  889. }
  890. type Method struct {
  891. api *API
  892. r *Resource // or nil if a API-level (top-level) method
  893. name string
  894. m map[string]interface{} // original JSON
  895. params []*Param // all Params, of each type, lazily set by first access to Parameters
  896. }
  897. func (m *Method) Id() string {
  898. return jstr(m.m, "id")
  899. }
  900. func (m *Method) supportsMedia() bool {
  901. return jobj(m.m, "mediaUpload") != nil
  902. }
  903. func (m *Method) mediaPath() string {
  904. return jstr(jobj(jobj(jobj(m.m, "mediaUpload"), "protocols"), "simple"), "path")
  905. }
  906. func (m *Method) Params() []*Param {
  907. if m.params == nil {
  908. paramMap := jobj(m.m, "parameters")
  909. for _, name := range sortedKeys(paramMap) {
  910. mi := paramMap[name]
  911. pm := mi.(map[string]interface{})
  912. m.params = append(m.params, &Param{
  913. name: name,
  914. m: pm,
  915. method: m,
  916. })
  917. }
  918. }
  919. return m.params
  920. }
  921. func (m *Method) grepParams(f func(*Param) bool) []*Param {
  922. matches := make([]*Param, 0)
  923. for _, param := range m.Params() {
  924. if f(param) {
  925. matches = append(matches, param)
  926. }
  927. }
  928. return matches
  929. }
  930. func (m *Method) NamedParam(name string) *Param {
  931. matches := m.grepParams(func(p *Param) bool {
  932. return p.name == name
  933. })
  934. if len(matches) < 1 {
  935. log.Panicf("failed to find named parameter %q", name)
  936. }
  937. if len(matches) > 1 {
  938. log.Panicf("found multiple parameters for parameter name %q", name)
  939. }
  940. return matches[0]
  941. }
  942. func (m *Method) OptParams() []*Param {
  943. return m.grepParams(func(p *Param) bool {
  944. return !p.IsRequired()
  945. })
  946. }
  947. func (m *Method) RequiredRepeatedQueryParams() []*Param {
  948. return m.grepParams(func(p *Param) bool {
  949. return p.IsRequired() && p.IsRepeated() && p.Location() == "query"
  950. })
  951. }
  952. func (m *Method) RequiredQueryParams() []*Param {
  953. return m.grepParams(func(p *Param) bool {
  954. return p.IsRequired() && !p.IsRepeated() && p.Location() == "query"
  955. })
  956. }
  957. func (meth *Method) generateCode() {
  958. res := meth.r // may be nil if a top-level method
  959. a := meth.api
  960. p, pn := a.p, a.pn
  961. pn("\n// method id %q:", meth.Id())
  962. retTypeComma := responseType(a, meth.m)
  963. if retTypeComma != "" {
  964. retTypeComma += ", "
  965. }
  966. args := meth.NewArguments()
  967. methodName := initialCap(meth.name)
  968. prefix := ""
  969. if res != nil {
  970. prefix = initialCap(fmt.Sprintf("%s.%s", res.parent, res.name))
  971. }
  972. callName := a.GetName(prefix + methodName + "Call")
  973. p("\ntype %s struct {\n", callName)
  974. p("\ts *Service\n")
  975. for _, arg := range args.l {
  976. p("\t%s %s\n", arg.goname, arg.gotype)
  977. }
  978. p("\topt_ map[string]interface{}\n")
  979. if meth.supportsMedia() {
  980. p("\tmedia_ io.Reader\n")
  981. }
  982. p("}\n")
  983. p("\n%s", asComment("", methodName+": "+jstr(meth.m, "description")))
  984. var servicePtr string
  985. if res == nil {
  986. p("func (s *Service) %s(%s) *%s {\n", methodName, args, callName)
  987. servicePtr = "s"
  988. } else {
  989. p("func (r *%s) %s(%s) *%s {\n", res.GoType(), methodName, args, callName)
  990. servicePtr = "r.s"
  991. }
  992. p("\tc := &%s{s: %s, opt_: make(map[string]interface{})}\n", callName, servicePtr)
  993. for _, arg := range args.l {
  994. p("\tc.%s = %s\n", arg.goname, arg.goname)
  995. }
  996. p("\treturn c\n")
  997. p("}\n")
  998. for _, opt := range meth.OptParams() {
  999. setter := initialCap(opt.name)
  1000. des := jstr(opt.m, "description")
  1001. des = strings.Replace(des, "Optional.", "", 1)
  1002. des = strings.TrimSpace(des)
  1003. p("\n%s", asComment("", fmt.Sprintf("%s sets the optional parameter %q: %s", setter, opt.name, des)))
  1004. np := new(namePool)
  1005. np.Get("c") // take the receiver's name
  1006. paramName := np.Get(validGoIdentifer(opt.name))
  1007. p("func (c *%s) %s(%s %s) *%s {\n", callName, setter, paramName, opt.GoType(), callName)
  1008. p("c.opt_[%q] = %s\n", opt.name, paramName)
  1009. p("return c\n")
  1010. p("}\n")
  1011. }
  1012. if meth.supportsMedia() {
  1013. p("func (c *%s) Media(r io.Reader) *%s {\n", callName, callName)
  1014. p("c.media_ = r\n")
  1015. p("return c\n")
  1016. p("}\n")
  1017. }
  1018. pn("\nfunc (c *%s) Do() (%serror) {", callName, retTypeComma)
  1019. nilRet := ""
  1020. if retTypeComma != "" {
  1021. nilRet = "nil, "
  1022. }
  1023. pn("var body io.Reader = nil")
  1024. hasContentType := false
  1025. httpMethod := jstr(meth.m, "httpMethod")
  1026. if ba := args.bodyArg(); ba != nil && httpMethod != "GET" {
  1027. style := "WithoutDataWrapper"
  1028. if a.needsDataWrapper() {
  1029. style = "WithDataWrapper"
  1030. }
  1031. pn("body, err := googleapi.%s.JSONReader(c.%s)", style, ba.goname)
  1032. pn("if err != nil { return %serr }", nilRet)
  1033. pn(`ctype := "application/json"`)
  1034. hasContentType = true
  1035. }
  1036. pn("params := make(url.Values)")
  1037. // Set this first. if they override it, though, might be gross. We don't expect
  1038. // XML replies elsewhere. TODO(bradfitz): hide this option in the generated code?
  1039. pn(`params.Set("alt", "json")`)
  1040. for _, p := range meth.RequiredQueryParams() {
  1041. pn("params.Set(%q, fmt.Sprintf(\"%%v\", c.%s))", p.name, p.goCallFieldName())
  1042. }
  1043. for _, p := range meth.RequiredRepeatedQueryParams() {
  1044. pn("for _, v := range c.%s { params.Add(%q, fmt.Sprintf(\"%%v\", v)) }",
  1045. p.name, p.name)
  1046. }
  1047. for _, p := range meth.OptParams() {
  1048. pn("if v, ok := c.opt_[%q]; ok { params.Set(%q, fmt.Sprintf(\"%%v\", v)) }",
  1049. p.name, p.name)
  1050. }
  1051. p("urls := googleapi.ResolveRelative(c.s.BasePath, %q)\n", jstr(meth.m, "path"))
  1052. if meth.supportsMedia() {
  1053. pn("if c.media_ != nil {")
  1054. // Hack guess, since we get a 404 otherwise:
  1055. //pn("urls = googleapi.ResolveRelative(%q, %q)", a.apiBaseURL(), meth.mediaPath())
  1056. // Further hack. Discovery doc is wrong?
  1057. pn("urls = strings.Replace(urls, %q, %q, 1)", "https://www.googleapis.com/", "https://www.googleapis.com/upload/")
  1058. pn(`params.Set("uploadType", "multipart")`)
  1059. pn("}")
  1060. }
  1061. pn("urls += \"?\" + params.Encode()")
  1062. if meth.supportsMedia() && httpMethod != "GET" {
  1063. if !hasContentType { // Support mediaUpload but no ctype set.
  1064. pn("body = new(bytes.Buffer)")
  1065. pn(`ctype := "application/json"`)
  1066. hasContentType = true
  1067. }
  1068. pn("contentLength_, hasMedia_ := googleapi.ConditionallyIncludeMedia(c.media_, &body, &ctype)")
  1069. }
  1070. pn("req, _ := http.NewRequest(%q, urls, body)", httpMethod)
  1071. // Replace param values after NewRequest to avoid reencoding them.
  1072. // E.g. Cloud Storage API requires '%2F' in entity param to be kept, but url.Parse replaces it with '/'.
  1073. for _, arg := range args.forLocation("path") {
  1074. pn(`req.URL.Path = strings.Replace(req.URL.Path, "{%s}", %s, 1)`, arg.apiname, arg.cleanExpr("c."))
  1075. }
  1076. // Set opaque to avoid encoding of the parameters in the URL path.
  1077. pn("googleapi.SetOpaque(req.URL)")
  1078. if meth.supportsMedia() {
  1079. pn("if hasMedia_ { req.ContentLength = contentLength_ }")
  1080. }
  1081. if hasContentType {
  1082. pn(`req.Header.Set("Content-Type", ctype)`)
  1083. }
  1084. pn(`req.Header.Set("User-Agent", "google-api-go-client/` + goGenVersion + `")`)
  1085. pn("res, err := c.s.client.Do(req);")
  1086. pn("if err != nil { return %serr }", nilRet)
  1087. pn("defer googleapi.CloseBody(res)")
  1088. pn("if err := googleapi.CheckResponse(res); err != nil { return %serr }", nilRet)
  1089. if retTypeComma == "" {
  1090. pn("return nil")
  1091. } else {
  1092. pn("var ret %s", responseType(a, meth.m))
  1093. pn("if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { return nil, err }")
  1094. pn("return ret, nil")
  1095. }
  1096. bs, _ := json.MarshalIndent(meth.m, "\t// ", " ")
  1097. pn("// %s\n", string(bs))
  1098. pn("}")
  1099. }
  1100. type Param struct {
  1101. method *Method
  1102. name string
  1103. m map[string]interface{}
  1104. callFieldName string // empty means to use the default
  1105. }
  1106. func (p *Param) IsRequired() bool {
  1107. v, _ := p.m["required"].(bool)
  1108. return v
  1109. }
  1110. func (p *Param) IsRepeated() bool {
  1111. v, _ := p.m["repeated"].(bool)
  1112. return v
  1113. }
  1114. func (p *Param) Location() string {
  1115. return p.m["location"].(string)
  1116. }
  1117. func (p *Param) GoType() string {
  1118. typ, format := jstr(p.m, "type"), jstr(p.m, "format")
  1119. if typ == "string" && strings.Contains(format, "int") && p.Location() != "query" {
  1120. panic("unexpected int parameter encoded as string, not in query: " + p.name)
  1121. }
  1122. t, ok := simpleTypeConvert(typ, format)
  1123. if !ok {
  1124. panic("failed to convert parameter type " + fmt.Sprintf("type=%q, format=%q", typ, format))
  1125. }
  1126. return t
  1127. }
  1128. // goCallFieldName returns the name of this parameter's field in a
  1129. // method's "Call" struct.
  1130. func (p *Param) goCallFieldName() string {
  1131. if p.callFieldName != "" {
  1132. return p.callFieldName
  1133. }
  1134. return validGoIdentifer(p.name)
  1135. }
  1136. // APIMethods returns top-level ("API-level") methods. They don't have an associated resource.
  1137. func (a *API) APIMethods() []*Method {
  1138. meths := []*Method{}
  1139. methMap := jobj(a.m, "methods")
  1140. for _, name := range sortedKeys(methMap) {
  1141. mi := methMap[name]
  1142. meths = append(meths, &Method{
  1143. api: a,
  1144. r: nil, // to be explicit
  1145. name: name,
  1146. m: mi.(map[string]interface{}),
  1147. })
  1148. }
  1149. return meths
  1150. }
  1151. func (a *API) Resources(m map[string]interface{}, p string) []*Resource {
  1152. res := []*Resource{}
  1153. resMap := jobj(m, "resources")
  1154. for _, rname := range sortedKeys(resMap) {
  1155. rmi := resMap[rname]
  1156. rm := rmi.(map[string]interface{})
  1157. res = append(res, &Resource{a, rname, p, rm, a.Resources(rm, fmt.Sprintf("%s.%s", p, rname))})
  1158. }
  1159. return res
  1160. }
  1161. func resolveRelative(basestr, relstr string) string {
  1162. u, err := url.Parse(basestr)
  1163. if err != nil {
  1164. panicf("Error parsing base URL %q: %v", basestr, err)
  1165. }
  1166. rel, err := url.Parse(relstr)
  1167. if err != nil {
  1168. panicf("Error parsing relative URL %q: %v", relstr, err)
  1169. }
  1170. u = u.ResolveReference(rel)
  1171. return u.String()
  1172. }
  1173. func (meth *Method) NewArguments() (args *arguments) {
  1174. args = &arguments{
  1175. method: meth,
  1176. m: make(map[string]*argument),
  1177. }
  1178. po, ok := meth.m["parameterOrder"].([]interface{})
  1179. if ok {
  1180. for _, poi := range po {
  1181. pname := poi.(string)
  1182. arg := meth.NewArg(pname, meth.NamedParam(pname))
  1183. args.AddArg(arg)
  1184. }
  1185. }
  1186. if ro := jobj(meth.m, "request"); ro != nil {
  1187. args.AddArg(meth.NewBodyArg(ro))
  1188. }
  1189. return
  1190. }
  1191. func (meth *Method) NewBodyArg(m map[string]interface{}) *argument {
  1192. reftype := jstr(m, "$ref")
  1193. return &argument{
  1194. goname: validGoIdentifer(strings.ToLower(reftype)),
  1195. apiname: "REQUEST",
  1196. gotype: "*" + reftype,
  1197. apitype: reftype,
  1198. location: "body",
  1199. }
  1200. }
  1201. func (meth *Method) NewArg(apiname string, p *Param) *argument {
  1202. m := p.m
  1203. apitype := jstr(m, "type")
  1204. des := jstr(m, "description")
  1205. goname := validGoIdentifer(apiname) // but might be changed later, if conflicts
  1206. if strings.Contains(des, "identifier") && !strings.HasSuffix(strings.ToLower(goname), "id") {
  1207. goname += "id" // yay
  1208. p.callFieldName = goname
  1209. }
  1210. gotype := mustSimpleTypeConvert(apitype, jstr(m, "format"))
  1211. if p.IsRepeated() {
  1212. gotype = "[]" + gotype
  1213. }
  1214. return &argument{
  1215. apiname: apiname,
  1216. apitype: apitype,
  1217. goname: goname,
  1218. gotype: gotype,
  1219. location: jstr(m, "location"),
  1220. }
  1221. }
  1222. type argument struct {
  1223. method *Method
  1224. apiname, apitype string
  1225. goname, gotype string
  1226. location string // "path", "query", "body"
  1227. }
  1228. func (a *argument) String() string {
  1229. return a.goname + " " + a.gotype
  1230. }
  1231. func (a *argument) cleanExpr(prefix string) string {
  1232. switch a.gotype {
  1233. case "[]string":
  1234. log.Printf("TODO(bradfitz): only including the first parameter in path query.")
  1235. return "url.QueryEscape(" + prefix + a.goname + "[0])"
  1236. case "string":
  1237. return "url.QueryEscape(" + prefix + a.goname + ")"
  1238. case "integer", "int64":
  1239. return "strconv.FormatInt(" + prefix + a.goname + ", 10)"
  1240. case "uint64":
  1241. return "strconv.FormatUint(" + prefix + a.goname + ", 10)"
  1242. }
  1243. log.Panicf("unknown type: apitype=%q, gotype=%q", a.apitype, a.gotype)
  1244. return ""
  1245. }
  1246. // arguments are the arguments that a method takes
  1247. type arguments struct {
  1248. l []*argument
  1249. m map[string]*argument
  1250. method *Method
  1251. }
  1252. func (args *arguments) forLocation(loc string) []*argument {
  1253. matches := make([]*argument, 0)
  1254. for _, arg := range args.l {
  1255. if arg.location == loc {
  1256. matches = append(matches, arg)
  1257. }
  1258. }
  1259. return matches
  1260. }
  1261. func (args *arguments) bodyArg() *argument {
  1262. for _, arg := range args.l {
  1263. if arg.location == "body" {
  1264. return arg
  1265. }
  1266. }
  1267. return nil
  1268. }
  1269. func (args *arguments) AddArg(arg *argument) {
  1270. n := 1
  1271. oname := arg.goname
  1272. for {
  1273. _, present := args.m[arg.goname]
  1274. if !present {
  1275. args.m[arg.goname] = arg
  1276. args.l = append(args.l, arg)
  1277. return
  1278. }
  1279. n++
  1280. arg.goname = fmt.Sprintf("%s%d", oname, n)
  1281. }
  1282. }
  1283. func (a *arguments) String() string {
  1284. var buf bytes.Buffer
  1285. for i, arg := range a.l {
  1286. if i != 0 {
  1287. buf.Write([]byte(", "))
  1288. }
  1289. buf.Write([]byte(arg.String()))
  1290. }
  1291. return buf.String()
  1292. }
  1293. func asComment(pfx, c string) string {
  1294. var buf bytes.Buffer
  1295. const maxLen = 70
  1296. removeNewlines := func(s string) string {
  1297. return strings.Replace(s, "\n", "\n"+pfx+"// ", -1)
  1298. }
  1299. for len(c) > 0 {
  1300. line := c
  1301. if len(line) < maxLen {
  1302. fmt.Fprintf(&buf, "%s// %s\n", pfx, removeNewlines(line))
  1303. break
  1304. }
  1305. line = line[:maxLen]
  1306. si := strings.LastIndex(line, " ")
  1307. if si != -1 {
  1308. line = line[:si]
  1309. }
  1310. fmt.Fprintf(&buf, "%s// %s\n", pfx, removeNewlines(line))
  1311. c = c[len(line):]
  1312. if si != -1 {
  1313. c = c[1:]
  1314. }
  1315. }
  1316. return buf.String()
  1317. }
  1318. func simpleTypeConvert(apiType, format string) (gotype string, ok bool) {
  1319. // From http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1
  1320. switch apiType {
  1321. case "boolean":
  1322. gotype = "bool"
  1323. case "string":
  1324. gotype = "string"
  1325. switch format {
  1326. case "int64", "uint64", "int32", "uint32":
  1327. gotype = format
  1328. }
  1329. case "number":
  1330. gotype = "float64"
  1331. case "integer":
  1332. gotype = "int64"
  1333. case "any":
  1334. gotype = "interface{}"
  1335. }
  1336. return gotype, gotype != ""
  1337. }
  1338. func mustSimpleTypeConvert(apiType, format string) string {
  1339. if gotype, ok := simpleTypeConvert(apiType, format); ok {
  1340. return gotype
  1341. }
  1342. panic(fmt.Sprintf("failed to simpleTypeConvert(%q, %q)", apiType, format))
  1343. }
  1344. func (a *API) goTypeOfJsonObject(outerName, memberName string, m map[string]interface{}) (string, error) {
  1345. apitype := jstr(m, "type")
  1346. switch apitype {
  1347. case "array":
  1348. items := jobj(m, "items")
  1349. if items == nil {
  1350. return "", errors.New("no items but type was array")
  1351. }
  1352. if ref := jstr(items, "$ref"); ref != "" {
  1353. return "[]*" + ref, nil // TODO: wrong; delete this whole function
  1354. }
  1355. if atype := jstr(items, "type"); atype != "" {
  1356. return "[]" + mustSimpleTypeConvert(atype, jstr(items, "format")), nil
  1357. }
  1358. return "", errors.New("unsupported 'array' type")
  1359. case "object":
  1360. return "*" + outerName + "_" + memberName, nil
  1361. //return "", os.NewError("unsupported 'object' type")
  1362. }
  1363. return mustSimpleTypeConvert(apitype, jstr(m, "format")), nil
  1364. }
  1365. func responseType(api *API, m map[string]interface{}) string {
  1366. ro := jobj(m, "response")
  1367. if ro != nil {
  1368. if ref := jstr(ro, "$ref"); ref != "" {
  1369. if s := api.schemas[ref]; s != nil {
  1370. return s.GoReturnType()
  1371. }
  1372. return "*" + ref
  1373. }
  1374. }
  1375. return ""
  1376. }
  1377. // initialCap returns the identifier with a leading capital letter.
  1378. // it also maps "foo-bar" to "FooBar".
  1379. func initialCap(ident string) string {
  1380. if ident == "" {
  1381. panic("blank identifier")
  1382. }
  1383. return depunct(ident, true)
  1384. }
  1385. func validGoIdentifer(ident string) string {
  1386. id := depunct(ident, false)
  1387. switch id {
  1388. case "break", "default", "func", "interface", "select",
  1389. "case", "defer", "go", "map", "struct",
  1390. "chan", "else", "goto", "package", "switch",
  1391. "const", "fallthrough", "if", "range", "type",
  1392. "continue", "for", "import", "return", "var":
  1393. return id + "_"
  1394. }
  1395. return id
  1396. }
  1397. // depunct removes '-', '.', '$', '/' from identifers, making the
  1398. // following character uppercase
  1399. func depunct(ident string, needCap bool) string {
  1400. var buf bytes.Buffer
  1401. for _, c := range ident {
  1402. if c == '-' || c == '.' || c == '$' || c == '/' {
  1403. needCap = true
  1404. continue
  1405. }
  1406. if needCap {
  1407. c = unicode.ToUpper(c)
  1408. needCap = false
  1409. }
  1410. buf.WriteByte(byte(c))
  1411. }
  1412. return buf.String()
  1413. }
  1414. func prettyJSON(m map[string]interface{}) string {
  1415. bs, err := json.MarshalIndent(m, "", " ")
  1416. if err != nil {
  1417. return fmt.Sprintf("[JSON error %v on %#v]", err, m)
  1418. }
  1419. return string(bs)
  1420. }
  1421. func jstr(m map[string]interface{}, key string) string {
  1422. if s, ok := m[key].(string); ok {
  1423. return s
  1424. }
  1425. return ""
  1426. }
  1427. func sortedKeys(m map[string]interface{}) (keys []string) {
  1428. for key := range m {
  1429. keys = append(keys, key)
  1430. }
  1431. sort.Strings(keys)
  1432. return
  1433. }
  1434. func jobj(m map[string]interface{}, key string) map[string]interface{} {
  1435. if m, ok := m[key].(map[string]interface{}); ok {
  1436. return m
  1437. }
  1438. return nil
  1439. }
  1440. func jstrlist(m map[string]interface{}, key string) []string {
  1441. si, ok := m[key].([]interface{})
  1442. if !ok {
  1443. return nil
  1444. }
  1445. sl := make([]string, 0)
  1446. for _, si := range si {
  1447. sl = append(sl, si.(string))
  1448. }
  1449. return sl
  1450. }