PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/third_party/src/code.google.com/p/google-api-go-client/google-api-go-generator/gen.go

https://github.com/discordianfish/kubernetes
Go | 1588 lines | 1360 code | 175 blank | 53 comment | 318 complexity | aabdc96d7e8b50201b7e31921c5ddea3 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-3.0
  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. }
  465. type Property struct {
  466. s *Schema // property of which schema
  467. apiName string // the native API-defined name of this property
  468. m map[string]interface{} // original JSON map
  469. typ *Type // lazily populated by Type
  470. }
  471. func (p *Property) Type() *Type {
  472. if p.typ == nil {
  473. p.typ = &Type{api: p.s.api, m: p.m}
  474. }
  475. return p.typ
  476. }
  477. func (p *Property) GoName() string {
  478. return initialCap(p.apiName)
  479. }
  480. func (p *Property) APIName() string {
  481. return p.apiName
  482. }
  483. func (p *Property) Description() string {
  484. return jstr(p.m, "description")
  485. }
  486. type Type struct {
  487. m map[string]interface{} // JSON map containing key "type" and maybe "items", "properties"
  488. api *API
  489. }
  490. func (t *Type) apiType() string {
  491. // Note: returns "" on reference types
  492. if t, ok := t.m["type"].(string); ok {
  493. return t
  494. }
  495. return ""
  496. }
  497. func (t *Type) apiTypeFormat() string {
  498. if f, ok := t.m["format"].(string); ok {
  499. return f
  500. }
  501. return ""
  502. }
  503. func (t *Type) isIntAsString() bool {
  504. return t.apiType() == "string" && strings.Contains(t.apiTypeFormat(), "int")
  505. }
  506. func (t *Type) asSimpleGoType() (goType string, ok bool) {
  507. return simpleTypeConvert(t.apiType(), t.apiTypeFormat())
  508. }
  509. func (t *Type) String() string {
  510. return fmt.Sprintf("[type=%q, map=%s]", t.apiType(), prettyJSON(t.m))
  511. }
  512. func (t *Type) AsGo() string {
  513. if t, ok := t.asSimpleGoType(); ok {
  514. return t
  515. }
  516. if at, ok := t.ArrayType(); ok {
  517. if at.apiType() == "string" {
  518. switch at.apiTypeFormat() {
  519. case "int64":
  520. return "googleapi.Int64s"
  521. case "uint64":
  522. return "googleapi.Uint64s"
  523. case "int32":
  524. return "googleapi.Int32s"
  525. case "uint32":
  526. return "googleapi.Uint32s"
  527. case "float64":
  528. return "googleapi.Float64s"
  529. default:
  530. return "[]" + at.AsGo()
  531. }
  532. }
  533. return "[]" + at.AsGo()
  534. }
  535. if ref, ok := t.Reference(); ok {
  536. s := t.api.schemas[ref]
  537. if s == nil {
  538. panic(fmt.Sprintf("in Type.AsGo(), failed to find referenced type %q for %s",
  539. ref, prettyJSON(t.m)))
  540. }
  541. return s.Type().AsGo()
  542. }
  543. if t.IsMap() {
  544. // TODO(gmlewis): support maps to any type.
  545. return fmt.Sprintf("map[string]string")
  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. props := jobj(t.m, "additionalProperties")
  574. if props == nil {
  575. return false
  576. }
  577. s := jstr(props, "type")
  578. b := s == "string"
  579. if !b {
  580. log.Printf("Warning: found map to type %q which is not implemented yet.", s)
  581. }
  582. return b
  583. }
  584. func (t *Type) IsReference() bool {
  585. return jstr(t.m, "$ref") != ""
  586. }
  587. func (t *Type) ReferenceSchema() (s *Schema, ok bool) {
  588. apiName, ok := t.Reference()
  589. if !ok {
  590. return
  591. }
  592. s = t.api.schemas[apiName]
  593. if s == nil {
  594. panicf("failed to find t.api.schemas[%q] while resolving reference",
  595. apiName)
  596. }
  597. return s, true
  598. }
  599. func (t *Type) ArrayType() (elementType *Type, ok bool) {
  600. if t.apiType() != "array" {
  601. return
  602. }
  603. items := jobj(t.m, "items")
  604. if items == nil {
  605. panicf("can't handle array type missing its 'items' key. map is %#v", t.m)
  606. }
  607. return &Type{api: t.api, m: items}, true
  608. }
  609. func (s *Schema) Type() *Type {
  610. if s.typ == nil {
  611. s.typ = &Type{api: s.api, m: s.m}
  612. }
  613. return s.typ
  614. }
  615. func (s *Schema) properties() []*Property {
  616. if !s.Type().IsStruct() {
  617. panic("called properties on non-object schema")
  618. }
  619. pl := []*Property{}
  620. propMap := jobj(s.m, "properties")
  621. for _, name := range sortedKeys(propMap) {
  622. m := propMap[name].(map[string]interface{})
  623. pl = append(pl, &Property{
  624. s: s,
  625. m: m,
  626. apiName: name,
  627. })
  628. }
  629. return pl
  630. }
  631. func (s *Schema) populateSubSchemas() (outerr error) {
  632. defer func() {
  633. r := recover()
  634. if r == nil {
  635. return
  636. }
  637. outerr = fmt.Errorf("%v", r)
  638. }()
  639. addSubStruct := func(subApiName string, t *Type) {
  640. if s.api.schemas[subApiName] != nil {
  641. panic("dup schema apiName: " + subApiName)
  642. }
  643. subm := t.m
  644. subm["_apiName"] = subApiName
  645. subs := &Schema{
  646. api: s.api,
  647. m: subm,
  648. typ: t,
  649. apiName: subApiName,
  650. }
  651. s.api.schemas[subApiName] = subs
  652. err := subs.populateSubSchemas()
  653. if err != nil {
  654. panicf("in sub-struct %q: %v", subApiName, err)
  655. }
  656. }
  657. if s.Type().IsStruct() {
  658. for _, p := range s.properties() {
  659. if p.Type().IsSimple() || p.Type().IsMap() {
  660. continue
  661. }
  662. if at, ok := p.Type().ArrayType(); ok {
  663. if at.IsSimple() || at.IsReference() {
  664. continue
  665. }
  666. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.apiName)
  667. if at.IsStruct() {
  668. addSubStruct(subApiName, at) // was p.Type()?
  669. continue
  670. }
  671. if _, ok := at.ArrayType(); ok {
  672. addSubStruct(subApiName, at)
  673. continue
  674. }
  675. panicf("Unknown property array type for %q: %s", subApiName, at)
  676. continue
  677. }
  678. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.apiName)
  679. if p.Type().IsStruct() {
  680. addSubStruct(subApiName, p.Type())
  681. continue
  682. }
  683. if p.Type().IsReference() {
  684. continue
  685. }
  686. panicf("Unknown type for %q: %s", subApiName, p.Type())
  687. }
  688. return
  689. }
  690. if at, ok := s.Type().ArrayType(); ok {
  691. if at.IsSimple() || at.IsReference() {
  692. return
  693. }
  694. subApiName := fmt.Sprintf("%s.Item", s.apiName)
  695. if at.IsStruct() {
  696. addSubStruct(subApiName, at)
  697. return
  698. }
  699. if at, ok := at.ArrayType(); ok {
  700. if at.IsSimple() || at.IsReference() {
  701. return
  702. }
  703. addSubStruct(subApiName, at)
  704. return
  705. }
  706. panicf("Unknown array type for %q: %s", subApiName, at)
  707. return
  708. }
  709. if s.Type().IsSimple() || s.Type().IsReference() {
  710. return
  711. }
  712. fmt.Fprintf(os.Stderr, "in populateSubSchemas, schema is: %s", prettyJSON(s.m))
  713. panicf("populateSubSchemas: unsupported type for schema %q", s.apiName)
  714. panic("unreachable")
  715. }
  716. // GoName returns (or creates and returns) the bare Go name
  717. // of the apiName, making sure that it's a proper Go identifier
  718. // and doesn't conflict with an existing name.
  719. func (s *Schema) GoName() string {
  720. if s.goName == "" {
  721. s.goName = s.api.GetName(initialCap(s.apiName))
  722. }
  723. return s.goName
  724. }
  725. func (s *Schema) writeSchemaCode() {
  726. if s.Type().IsStruct() && !s.Type().IsMap() {
  727. s.writeSchemaStruct()
  728. return
  729. }
  730. if _, ok := s.Type().ArrayType(); ok {
  731. log.Printf("TODO writeSchemaCode for arrays for %s", s.GoName())
  732. return
  733. }
  734. if destSchema, ok := s.Type().ReferenceSchema(); ok {
  735. // Convert it to a struct using embedding.
  736. s.api.p("\ntype %s struct {\n", s.GoName())
  737. s.api.p("\t%s\n", destSchema.GoName())
  738. s.api.p("}\n")
  739. return
  740. }
  741. if s.Type().IsSimple() || s.Type().IsMap() {
  742. return
  743. }
  744. fmt.Fprintf(os.Stderr, "in writeSchemaCode, schema is: %s", prettyJSON(s.m))
  745. panicf("writeSchemaCode: unsupported type for schema %q", s.apiName)
  746. }
  747. func (s *Schema) writeSchemaStruct() {
  748. // TODO: description
  749. s.api.p("\ntype %s struct {\n", s.GoName())
  750. for i, p := range s.properties() {
  751. if i > 0 {
  752. s.api.p("\n")
  753. }
  754. pname := p.GoName()
  755. if des := p.Description(); des != "" {
  756. s.api.p("%s", asComment("\t", fmt.Sprintf("%s: %s", pname, des)))
  757. }
  758. var extraOpt string
  759. if p.Type().isIntAsString() {
  760. extraOpt += ",string"
  761. }
  762. s.api.p("\t%s %s `json:\"%s,omitempty%s\"`\n", pname, p.Type().AsGo(), p.APIName(), extraOpt)
  763. }
  764. s.api.p("}\n")
  765. }
  766. // PopulateSchemas reads all the API types ("schemas") from the JSON file
  767. // and converts them to *Schema instances, returning an identically
  768. // keyed map, additionally containing subresources. For instance,
  769. //
  770. // A resource "Foo" of type "object" with a property "bar", also of type
  771. // "object" (an anonymous sub-resource), will get a synthetic API name
  772. // of "Foo.bar".
  773. //
  774. // A resource "Foo" of type "array" with an "items" of type "object"
  775. // will get a synthetic API name of "Foo.Item".
  776. func (a *API) PopulateSchemas() {
  777. m := jobj(a.m, "schemas")
  778. if a.schemas != nil {
  779. panic("")
  780. }
  781. a.schemas = make(map[string]*Schema)
  782. for name, mi := range m {
  783. s := &Schema{
  784. api: a,
  785. apiName: name,
  786. m: mi.(map[string]interface{}),
  787. }
  788. // And a little gross hack, so a map alone is good
  789. // enough to get its apiName:
  790. s.m["_apiName"] = name
  791. a.schemas[name] = s
  792. err := s.populateSubSchemas()
  793. if err != nil {
  794. panicf("Error populating schema with API name %q: %v", name, err)
  795. }
  796. }
  797. }
  798. type Resource struct {
  799. api *API
  800. name string
  801. parent string
  802. m map[string]interface{}
  803. resources []*Resource
  804. }
  805. func (r *Resource) generateType() {
  806. p, pn := r.api.p, r.api.pn
  807. t := r.GoType()
  808. pn(fmt.Sprintf("func New%s(s *Service) *%s {", t, t))
  809. pn("rs := &%s{s : s}", t)
  810. for _, res := range r.resources {
  811. pn("rs.%s = New%s(s)", res.GoField(), res.GoType())
  812. }
  813. pn("return rs")
  814. pn("}")
  815. p("\ntype %s struct {\n", t)
  816. p("\ts *Service\n")
  817. for _, res := range r.resources {
  818. p("\n\t%s\t*%s\n", res.GoField(), res.GoType())
  819. }
  820. p("}\n")
  821. for _, res := range r.resources {
  822. res.generateType()
  823. }
  824. }
  825. func (r *Resource) generateMethods() {
  826. for _, meth := range r.Methods() {
  827. meth.generateCode()
  828. }
  829. for _, res := range r.resources {
  830. res.generateMethods()
  831. }
  832. }
  833. func (r *Resource) GoField() string {
  834. return initialCap(r.name)
  835. }
  836. func (r *Resource) GoType() string {
  837. return initialCap(fmt.Sprintf("%s.%s", r.parent, r.name)) + "Service"
  838. }
  839. func (r *Resource) Methods() []*Method {
  840. ms := []*Method{}
  841. methMap := jobj(r.m, "methods")
  842. for _, mname := range sortedKeys(methMap) {
  843. mi := methMap[mname]
  844. ms = append(ms, &Method{
  845. api: r.api,
  846. r: r,
  847. name: mname,
  848. m: mi.(map[string]interface{}),
  849. })
  850. }
  851. return ms
  852. }
  853. type Method struct {
  854. api *API
  855. r *Resource // or nil if a API-level (top-level) method
  856. name string
  857. m map[string]interface{} // original JSON
  858. params []*Param // all Params, of each type, lazily set by first access to Parameters
  859. }
  860. func (m *Method) Id() string {
  861. return jstr(m.m, "id")
  862. }
  863. func (m *Method) supportsMedia() bool {
  864. return jobj(m.m, "mediaUpload") != nil
  865. }
  866. func (m *Method) mediaPath() string {
  867. return jstr(jobj(jobj(jobj(m.m, "mediaUpload"), "protocols"), "simple"), "path")
  868. }
  869. func (m *Method) Params() []*Param {
  870. if m.params == nil {
  871. paramMap := jobj(m.m, "parameters")
  872. for _, name := range sortedKeys(paramMap) {
  873. mi := paramMap[name]
  874. pm := mi.(map[string]interface{})
  875. m.params = append(m.params, &Param{
  876. name: name,
  877. m: pm,
  878. method: m,
  879. })
  880. }
  881. }
  882. return m.params
  883. }
  884. func (m *Method) grepParams(f func(*Param) bool) []*Param {
  885. matches := make([]*Param, 0)
  886. for _, param := range m.Params() {
  887. if f(param) {
  888. matches = append(matches, param)
  889. }
  890. }
  891. return matches
  892. }
  893. func (m *Method) NamedParam(name string) *Param {
  894. matches := m.grepParams(func(p *Param) bool {
  895. return p.name == name
  896. })
  897. if len(matches) < 1 {
  898. log.Panicf("failed to find named parameter %q", name)
  899. }
  900. if len(matches) > 1 {
  901. log.Panicf("found multiple parameters for parameter name %q", name)
  902. }
  903. return matches[0]
  904. }
  905. func (m *Method) OptParams() []*Param {
  906. return m.grepParams(func(p *Param) bool {
  907. return !p.IsRequired()
  908. })
  909. }
  910. func (m *Method) RequiredRepeatedQueryParams() []*Param {
  911. return m.grepParams(func(p *Param) bool {
  912. return p.IsRequired() && p.IsRepeated() && p.Location() == "query"
  913. })
  914. }
  915. func (m *Method) RequiredQueryParams() []*Param {
  916. return m.grepParams(func(p *Param) bool {
  917. return p.IsRequired() && !p.IsRepeated() && p.Location() == "query"
  918. })
  919. }
  920. func (meth *Method) generateCode() {
  921. res := meth.r // may be nil if a top-level method
  922. a := meth.api
  923. p, pn := a.p, a.pn
  924. pn("\n// method id %q:", meth.Id())
  925. retTypeComma := responseType(a, meth.m)
  926. if retTypeComma != "" {
  927. retTypeComma += ", "
  928. }
  929. args := meth.NewArguments()
  930. methodName := initialCap(meth.name)
  931. prefix := ""
  932. if res != nil {
  933. prefix = initialCap(fmt.Sprintf("%s.%s", res.parent, res.name))
  934. }
  935. callName := a.GetName(prefix + methodName + "Call")
  936. p("\ntype %s struct {\n", callName)
  937. p("\ts *Service\n")
  938. for _, arg := range args.l {
  939. p("\t%s %s\n", arg.goname, arg.gotype)
  940. }
  941. p("\topt_ map[string]interface{}\n")
  942. if meth.supportsMedia() {
  943. p("\tmedia_ io.Reader\n")
  944. }
  945. p("}\n")
  946. p("\n%s", asComment("", methodName+": "+jstr(meth.m, "description")))
  947. var servicePtr string
  948. if res == nil {
  949. p("func (s *Service) %s(%s) *%s {\n", methodName, args, callName)
  950. servicePtr = "s"
  951. } else {
  952. p("func (r *%s) %s(%s) *%s {\n", res.GoType(), methodName, args, callName)
  953. servicePtr = "r.s"
  954. }
  955. p("\tc := &%s{s: %s, opt_: make(map[string]interface{})}\n", callName, servicePtr)
  956. for _, arg := range args.l {
  957. p("\tc.%s = %s\n", arg.goname, arg.goname)
  958. }
  959. p("\treturn c\n")
  960. p("}\n")
  961. for _, opt := range meth.OptParams() {
  962. setter := initialCap(opt.name)
  963. des := jstr(opt.m, "description")
  964. des = strings.Replace(des, "Optional.", "", 1)
  965. des = strings.TrimSpace(des)
  966. p("\n%s", asComment("", fmt.Sprintf("%s sets the optional parameter %q: %s", setter, opt.name, des)))
  967. np := new(namePool)
  968. np.Get("c") // take the receiver's name
  969. paramName := np.Get(validGoIdentifer(opt.name))
  970. p("func (c *%s) %s(%s %s) *%s {\n", callName, setter, paramName, opt.GoType(), callName)
  971. p("c.opt_[%q] = %s\n", opt.name, paramName)
  972. p("return c\n")
  973. p("}\n")
  974. }
  975. if meth.supportsMedia() {
  976. p("func (c *%s) Media(r io.Reader) *%s {\n", callName, callName)
  977. p("c.media_ = r\n")
  978. p("return c\n")
  979. p("}\n")
  980. }
  981. pn("\nfunc (c *%s) Do() (%serror) {", callName, retTypeComma)
  982. nilRet := ""
  983. if retTypeComma != "" {
  984. nilRet = "nil, "
  985. }
  986. pn("var body io.Reader = nil")
  987. hasContentType := false
  988. httpMethod := jstr(meth.m, "httpMethod")
  989. if ba := args.bodyArg(); ba != nil && httpMethod != "GET" {
  990. style := "WithoutDataWrapper"
  991. if a.needsDataWrapper() {
  992. style = "WithDataWrapper"
  993. }
  994. pn("body, err := googleapi.%s.JSONReader(c.%s)", style, ba.goname)
  995. pn("if err != nil { return %serr }", nilRet)
  996. pn(`ctype := "application/json"`)
  997. hasContentType = true
  998. }
  999. pn("params := make(url.Values)")
  1000. // Set this first. if they override it, though, might be gross. We don't expect
  1001. // XML replies elsewhere. TODO(bradfitz): hide this option in the generated code?
  1002. pn(`params.Set("alt", "json")`)
  1003. for _, p := range meth.RequiredQueryParams() {
  1004. pn("params.Set(%q, fmt.Sprintf(\"%%v\", c.%s))", p.name, p.goCallFieldName())
  1005. }
  1006. for _, p := range meth.RequiredRepeatedQueryParams() {
  1007. pn("for _, v := range c.%s { params.Add(%q, fmt.Sprintf(\"%%v\", v)) }",
  1008. p.name, p.name)
  1009. }
  1010. for _, p := range meth.OptParams() {
  1011. pn("if v, ok := c.opt_[%q]; ok { params.Set(%q, fmt.Sprintf(\"%%v\", v)) }",
  1012. p.name, p.name)
  1013. }
  1014. p("urls := googleapi.ResolveRelative(c.s.BasePath, %q)\n", jstr(meth.m, "path"))
  1015. if meth.supportsMedia() {
  1016. pn("if c.media_ != nil {")
  1017. // Hack guess, since we get a 404 otherwise:
  1018. //pn("urls = googleapi.ResolveRelative(%q, %q)", a.apiBaseURL(), meth.mediaPath())
  1019. // Further hack. Discovery doc is wrong?
  1020. pn("urls = strings.Replace(urls, %q, %q, 1)", "https://www.googleapis.com/", "https://www.googleapis.com/upload/")
  1021. pn(`params.Set("uploadType", "multipart")`)
  1022. pn("}")
  1023. }
  1024. pn("urls += \"?\" + params.Encode()")
  1025. if meth.supportsMedia() && httpMethod != "GET" {
  1026. if !hasContentType { // Support mediaUpload but no ctype set.
  1027. pn("body = new(bytes.Buffer)")
  1028. pn(`ctype := "application/json"`)
  1029. hasContentType = true
  1030. }
  1031. pn("contentLength_, hasMedia_ := googleapi.ConditionallyIncludeMedia(c.media_, &body, &ctype)")
  1032. }
  1033. pn("req, _ := http.NewRequest(%q, urls, body)", httpMethod)
  1034. // Replace param values after NewRequest to avoid reencoding them.
  1035. // E.g. Cloud Storage API requires '%2F' in entity param to be kept, but url.Parse replaces it with '/'.
  1036. for _, arg := range args.forLocation("path") {
  1037. pn(`req.URL.Path = strings.Replace(req.URL.Path, "{%s}", %s, 1)`, arg.apiname, arg.cleanExpr("c."))
  1038. }
  1039. // Set opaque to avoid encoding of the parameters in the URL path.
  1040. pn("googleapi.SetOpaque(req.URL)")
  1041. if meth.supportsMedia() {
  1042. pn("if hasMedia_ { req.ContentLength = contentLength_ }")
  1043. }
  1044. if hasContentType {
  1045. pn(`req.Header.Set("Content-Type", ctype)`)
  1046. }
  1047. pn(`req.Header.Set("User-Agent", "google-api-go-client/` + goGenVersion + `")`)
  1048. pn("res, err := c.s.client.Do(req);")
  1049. pn("if err != nil { return %serr }", nilRet)
  1050. pn("defer googleapi.CloseBody(res)")
  1051. pn("if err := googleapi.CheckResponse(res); err != nil { return %serr }", nilRet)
  1052. if retTypeComma == "" {
  1053. pn("return nil")
  1054. } else {
  1055. pn("ret := new(%s)", responseType(a, meth.m)[1:])
  1056. pn("if err := json.NewDecoder(res.Body).Decode(ret); err != nil { return nil, err }")
  1057. pn("return ret, nil")
  1058. }
  1059. bs, _ := json.MarshalIndent(meth.m, "\t// ", " ")
  1060. pn("// %s\n", string(bs))
  1061. pn("}")
  1062. }
  1063. type Param struct {
  1064. method *Method
  1065. name string
  1066. m map[string]interface{}
  1067. callFieldName string // empty means to use the default
  1068. }
  1069. func (p *Param) IsRequired() bool {
  1070. v, _ := p.m["required"].(bool)
  1071. return v
  1072. }
  1073. func (p *Param) IsRepeated() bool {
  1074. v, _ := p.m["repeated"].(bool)
  1075. return v
  1076. }
  1077. func (p *Param) Location() string {
  1078. return p.m["location"].(string)
  1079. }
  1080. func (p *Param) GoType() string {
  1081. typ, format := jstr(p.m, "type"), jstr(p.m, "format")
  1082. if typ == "string" && strings.Contains(format, "int") && p.Location() != "query" {
  1083. panic("unexpected int parameter encoded as string, not in query: " + p.name)
  1084. }
  1085. t, ok := simpleTypeConvert(typ, format)
  1086. if !ok {
  1087. panic("failed to convert parameter type " + fmt.Sprintf("type=%q, format=%q", typ, format))
  1088. }
  1089. return t
  1090. }
  1091. // goCallFieldName returns the name of this parameter's field in a
  1092. // method's "Call" struct.
  1093. func (p *Param) goCallFieldName() string {
  1094. if p.callFieldName != "" {
  1095. return p.callFieldName
  1096. }
  1097. return validGoIdentifer(p.name)
  1098. }
  1099. // APIMethods returns top-level ("API-level") methods. They don't have an associated resource.
  1100. func (a *API) APIMethods() []*Method {
  1101. meths := []*Method{}
  1102. methMap := jobj(a.m, "methods")
  1103. for _, name := range sortedKeys(methMap) {
  1104. mi := methMap[name]
  1105. meths = append(meths, &Method{
  1106. api: a,
  1107. r: nil, // to be explicit
  1108. name: name,
  1109. m: mi.(map[string]interface{}),
  1110. })
  1111. }
  1112. return meths
  1113. }
  1114. func (a *API) Resources(m map[string]interface{}, p string) []*Resource {
  1115. res := []*Resource{}
  1116. resMap := jobj(m, "resources")
  1117. for _, rname := range sortedKeys(resMap) {
  1118. rmi := resMap[rname]
  1119. rm := rmi.(map[string]interface{})
  1120. res = append(res, &Resource{a, rname, p, rm, a.Resources(rm, fmt.Sprintf("%s.%s", p, rname))})
  1121. }
  1122. return res
  1123. }
  1124. func resolveRelative(basestr, relstr string) string {
  1125. u, err := url.Parse(basestr)
  1126. if err != nil {
  1127. panicf("Error parsing base URL %q: %v", basestr, err)
  1128. }
  1129. rel, err := url.Parse(relstr)
  1130. if err != nil {
  1131. panicf("Error parsing relative URL %q: %v", relstr, err)
  1132. }
  1133. u = u.ResolveReference(rel)
  1134. return u.String()
  1135. }
  1136. func (meth *Method) NewArguments() (args *arguments) {
  1137. args = &arguments{
  1138. method: meth,
  1139. m: make(map[string]*argument),
  1140. }
  1141. po, ok := meth.m["parameterOrder"].([]interface{})
  1142. if ok {
  1143. for _, poi := range po {
  1144. pname := poi.(string)
  1145. arg := meth.NewArg(pname, meth.NamedParam(pname))
  1146. args.AddArg(arg)
  1147. }
  1148. }
  1149. if ro := jobj(meth.m, "request"); ro != nil {
  1150. args.AddArg(meth.NewBodyArg(ro))
  1151. }
  1152. return
  1153. }
  1154. func (meth *Method) NewBodyArg(m map[string]interface{}) *argument {
  1155. reftype := jstr(m, "$ref")
  1156. return &argument{
  1157. goname: validGoIdentifer(strings.ToLower(reftype)),
  1158. apiname: "REQUEST",
  1159. gotype: "*" + reftype,
  1160. apitype: reftype,
  1161. location: "body",
  1162. }
  1163. }
  1164. func (meth *Method) NewArg(apiname string, p *Param) *argument {
  1165. m := p.m
  1166. apitype := jstr(m, "type")
  1167. des := jstr(m, "description")
  1168. goname := validGoIdentifer(apiname) // but might be changed later, if conflicts
  1169. if strings.Contains(des, "identifier") && !strings.HasSuffix(strings.ToLower(goname), "id") {
  1170. goname += "id" // yay
  1171. p.callFieldName = goname
  1172. }
  1173. gotype := mustSimpleTypeConvert(apitype, jstr(m, "format"))
  1174. if p.IsRepeated() {
  1175. gotype = "[]" + gotype
  1176. }
  1177. return &argument{
  1178. apiname: apiname,
  1179. apitype: apitype,
  1180. goname: goname,
  1181. gotype: gotype,
  1182. location: jstr(m, "location"),
  1183. }
  1184. }
  1185. type argument struct {
  1186. method *Method
  1187. apiname, apitype string
  1188. goname, gotype string
  1189. location string // "path", "query", "body"
  1190. }
  1191. func (a *argument) String() string {
  1192. return a.goname + " " + a.gotype
  1193. }
  1194. func (a *argument) cleanExpr(prefix string) string {
  1195. switch a.gotype {
  1196. case "[]string":
  1197. log.Printf("TODO(bradfitz): only including the first parameter in path query.")
  1198. return "url.QueryEscape(" + prefix + a.goname + "[0])"
  1199. case "string":
  1200. return "url.QueryEscape(" + prefix + a.goname + ")"
  1201. case "integer", "int64":
  1202. return "strconv.FormatInt(" + prefix + a.goname + ", 10)"
  1203. case "uint64":
  1204. return "strconv.FormatUint(" + prefix + a.goname + ", 10)"
  1205. }
  1206. log.Panicf("unknown type: apitype=%q, gotype=%q", a.apitype, a.gotype)
  1207. return ""
  1208. }
  1209. // arguments are the arguments that a method takes
  1210. type arguments struct {
  1211. l []*argument
  1212. m map[string]*argument
  1213. method *Method
  1214. }
  1215. func (args *arguments) forLocation(loc string) []*argument {
  1216. matches := make([]*argument, 0)
  1217. for _, arg := range args.l {
  1218. if arg.location == loc {
  1219. matches = append(matches, arg)
  1220. }
  1221. }
  1222. return matches
  1223. }
  1224. func (args *arguments) bodyArg() *argument {
  1225. for _, arg := range args.l {
  1226. if arg.location == "body" {
  1227. return arg
  1228. }
  1229. }
  1230. return nil
  1231. }
  1232. func (args *arguments) AddArg(arg *argument) {
  1233. n := 1
  1234. oname := arg.goname
  1235. for {
  1236. _, present := args.m[arg.goname]
  1237. if !present {
  1238. args.m[arg.goname] = arg
  1239. args.l = append(args.l, arg)
  1240. return
  1241. }
  1242. n++
  1243. arg.goname = fmt.Sprintf("%s%d", oname, n)
  1244. }
  1245. }
  1246. func (a *arguments) String() string {
  1247. var buf bytes.Buffer
  1248. for i, arg := range a.l {
  1249. if i != 0 {
  1250. buf.Write([]byte(", "))
  1251. }
  1252. buf.Write([]byte(arg.String()))
  1253. }
  1254. return buf.String()
  1255. }
  1256. func asComment(pfx, c string) string {
  1257. var buf bytes.Buffer
  1258. const maxLen = 70
  1259. removeNewlines := func(s string) string {
  1260. return strings.Replace(s, "\n", "\n"+pfx+"// ", -1)
  1261. }
  1262. for len(c) > 0 {
  1263. line := c
  1264. if len(line) < maxLen {
  1265. fmt.Fprintf(&buf, "%s// %s\n", pfx, removeNewlines(line))
  1266. break
  1267. }
  1268. line = line[:maxLen]
  1269. si := strings.LastIndex(line, " ")
  1270. if si != -1 {
  1271. line = line[:si]
  1272. }
  1273. fmt.Fprintf(&buf, "%s// %s\n", pfx, removeNewlines(line))
  1274. c = c[len(line):]
  1275. if si != -1 {
  1276. c = c[1:]
  1277. }
  1278. }
  1279. return buf.String()
  1280. }
  1281. func simpleTypeConvert(apiType, format string) (gotype string, ok bool) {
  1282. // From http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1
  1283. switch apiType {
  1284. case "boolean":
  1285. gotype = "bool"
  1286. case "string":
  1287. gotype = "string"
  1288. switch format {
  1289. case "int64", "uint64", "int32", "uint32":
  1290. gotype = format
  1291. }
  1292. case "number":
  1293. gotype = "float64"
  1294. case "integer":
  1295. gotype = "int64"
  1296. case "any":
  1297. gotype = "interface{}"
  1298. }
  1299. return gotype, gotype != ""
  1300. }
  1301. func mustSimpleTypeConvert(apiType, format string) string {
  1302. if gotype, ok := simpleTypeConvert(apiType, format); ok {
  1303. return gotype
  1304. }
  1305. panic(fmt.Sprintf("failed to simpleTypeConvert(%q, %q)", apiType, format))
  1306. }
  1307. func (a *API) goTypeOfJsonObject(outerName, memberName string, m map[string]interface{}) (string, error) {
  1308. apitype := jstr(m, "type")
  1309. switch apitype {
  1310. case "array":
  1311. items := jobj(m, "items")
  1312. if items == nil {
  1313. return "", errors.New("no items but type was array")
  1314. }
  1315. if ref := jstr(items, "$ref"); ref != "" {
  1316. return "[]*" + ref, nil // TODO: wrong; delete this whole function
  1317. }
  1318. if atype := jstr(items, "type"); atype != "" {
  1319. return "[]" + mustSimpleTypeConvert(atype, jstr(items, "format")), nil
  1320. }
  1321. return "", errors.New("unsupported 'array' type")
  1322. case "object":
  1323. return "*" + outerName + "_" + memberName, nil
  1324. //return "", os.NewError("unsupported 'object' type")
  1325. }
  1326. return mustSimpleTypeConvert(apitype, jstr(m, "format")), nil
  1327. }
  1328. func responseType(api *API, m map[string]interface{}) string {
  1329. ro := jobj(m, "response")
  1330. if ro != nil {
  1331. if ref := jstr(ro, "$ref"); ref != "" {
  1332. if s := api.schemas[ref]; s != nil {
  1333. return "*" + s.GoName()
  1334. }
  1335. return "*" + ref
  1336. }
  1337. }
  1338. return ""
  1339. }
  1340. // initialCap returns the identifier with a leading capital letter.
  1341. // it also maps "foo-bar" to "FooBar".
  1342. func initialCap(ident string) string {
  1343. if ident == "" {
  1344. panic("blank identifier")
  1345. }
  1346. return depunct(ident, true)
  1347. }
  1348. func validGoIdentifer(ident string) string {
  1349. id := depunct(ident, false)
  1350. switch id {
  1351. case "break", "default", "func", "interface", "select",
  1352. "case", "defer", "go", "map", "struct",
  1353. "chan", "else", "goto", "package", "switch",
  1354. "const", "fallthrough", "if", "range", "type",
  1355. "continue", "for", "import", "return", "var":
  1356. return id + "_"
  1357. }
  1358. return id
  1359. }
  1360. // depunct removes '-', '.', '$', '/' from identifers, making the
  1361. // following character uppercase
  1362. func depunct(ident string, needCap bool) string {
  1363. var buf bytes.Buffer
  1364. for _, c := range ident {
  1365. if c == '-' || c == '.' || c == '$' || c == '/' {
  1366. needCap = true
  1367. continue
  1368. }
  1369. if needCap {
  1370. c = unicode.ToUpper(c)
  1371. needCap = false
  1372. }
  1373. buf.WriteByte(byte(c))
  1374. }
  1375. return buf.String()
  1376. }
  1377. func prettyJSON(m map[string]interface{}) string {
  1378. bs, err := json.MarshalIndent(m, "", " ")
  1379. if err != nil {
  1380. return fmt.Sprintf("[JSON error %v on %#v]", err, m)
  1381. }
  1382. return string(bs)
  1383. }
  1384. func jstr(m map[string]interface{}, key string) string {
  1385. if s, ok := m[key].(string); ok {
  1386. return s
  1387. }
  1388. return ""
  1389. }
  1390. func sortedKeys(m map[string]interface{}) (keys []string) {
  1391. for key := range m {
  1392. keys = append(keys, key)
  1393. }
  1394. sort.Strings(keys)
  1395. return
  1396. }
  1397. func jobj(m map[string]interface{}, key string) map[string]interface{} {
  1398. if m, ok := m[key].(map[string]interface{}); ok {
  1399. return m
  1400. }
  1401. return nil
  1402. }
  1403. func jstrlist(m map[string]interface{}, key string) []string {
  1404. si, ok := m[key].([]interface{})
  1405. if !ok {
  1406. return nil
  1407. }
  1408. sl := make([]string, 0)
  1409. for _, si := range si {
  1410. sl = append(sl, si.(string))
  1411. }
  1412. return sl
  1413. }