swagger.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "go/ast"
  6. "go/parser"
  7. "go/token"
  8. "go/types"
  9. "io"
  10. "io/ioutil"
  11. "log"
  12. "os"
  13. "path"
  14. "reflect"
  15. "strconv"
  16. "strings"
  17. "golang.org/x/tools/go/ast/astutil"
  18. "golang.org/x/tools/go/packages"
  19. "gopkg.in/yaml.v3"
  20. )
  21. var swagger = map[string]interface{}{
  22. "openapi": "3.0.0",
  23. "info": map[string]string{
  24. "title": "CRM",
  25. "description": "CRM",
  26. "version": "0.0.1",
  27. },
  28. "paths": map[string]interface{}{},
  29. "security": []map[string]interface{}{
  30. {
  31. "bearerAuth": []interface{}{},
  32. },
  33. },
  34. "components": map[string]interface{}{
  35. "securitySchemes": map[string]interface{}{
  36. "basicAuth": map[string]interface{}{
  37. "type": "http",
  38. "scheme": "basic",
  39. },
  40. "bearerAuth": map[string]interface{}{
  41. "type": "http",
  42. "scheme": "bearer",
  43. },
  44. },
  45. "schemas": map[string]interface{}{},
  46. "examples": map[string]interface{}{
  47. "success": map[string]interface{}{
  48. "summary": "请求成功",
  49. "value": map[string]interface{}{
  50. "code": 200,
  51. "msg": "success",
  52. },
  53. },
  54. },
  55. },
  56. }
  57. type pathModel struct {
  58. OperationId string
  59. Summary string
  60. Tags []string
  61. Request string
  62. }
  63. func newPath(m pathModel) interface{} {
  64. schemaref := fmt.Sprintf("#/components/schemas/%s", m.Request)
  65. examplesref := fmt.Sprintf("#/components/examples/%s", m.Request)
  66. return map[string]interface{}{
  67. "post": map[string]interface{}{
  68. "tags": m.Tags,
  69. "operationId": m.OperationId,
  70. "summary": m.Summary,
  71. "requestBody": map[string]interface{}{
  72. "required": true,
  73. "content": map[string]interface{}{
  74. "application/json": map[string]interface{}{
  75. "schema": map[string]interface{}{
  76. "oneOf": []map[string]interface{}{
  77. {
  78. "$ref": schemaref,
  79. },
  80. },
  81. },
  82. "examples": map[string]interface{}{
  83. m.Request: map[string]interface{}{
  84. "$ref": examplesref,
  85. },
  86. },
  87. },
  88. },
  89. "responses": map[string]interface{}{
  90. "200": map[string]interface{}{
  91. "description": "请求成功",
  92. "content": map[string]interface{}{
  93. "application/json": map[string]interface{}{
  94. "examples": map[string]interface{}{
  95. "success": map[string]interface{}{
  96. "$ref": "#/components/examples/success",
  97. },
  98. },
  99. },
  100. },
  101. },
  102. },
  103. },
  104. },
  105. }
  106. }
  107. func testGoParser() error {
  108. sourceFile, err := os.Open("/home/lai/code/working/opms_backend/opms_parent/app/handler/contract/ctr_contract.go")
  109. if err != nil {
  110. return err
  111. }
  112. sourceContent, err := io.ReadAll(sourceFile)
  113. if err != nil {
  114. return err
  115. }
  116. // Create the AST by parsing src.
  117. fset := token.NewFileSet() // positions are relative to fset
  118. f, err := parser.ParseFile(fset, "", sourceContent, parser.ParseComments)
  119. if err != nil {
  120. panic(err)
  121. }
  122. // Print the imports from the file's AST.
  123. for _, s := range f.Imports {
  124. debugLog(s.Path.Value)
  125. fmt.Printf("%#v\n", s.Path)
  126. }
  127. debugLog("-----------------------")
  128. if Debug {
  129. ast.Print(fset, f)
  130. }
  131. debugLog("-----------------------")
  132. for _, c := range f.Comments {
  133. for _, l := range c.List {
  134. fmt.Printf("%#v%#v\n", l.Text, l.Slash)
  135. }
  136. }
  137. return nil
  138. }
  139. type SwaggerModel struct {
  140. Type string `yaml:"type"`
  141. Description string `yaml:"description"`
  142. Properties map[string]*SwaggerModel `yaml:"properties,omitempty"`
  143. Items *SwaggerModel `yaml:"items,omitempty"`
  144. Ref string `yaml:"ref,omitempty"`
  145. Embedded bool `yaml:"embedded,omitempty"`
  146. }
  147. func RangeSwaggerModel(node *SwaggerModel) []*SwaggerModel {
  148. if node == nil {
  149. return nil
  150. }
  151. allnode := []*SwaggerModel{}
  152. nodes := []*SwaggerModel{node}
  153. for len(nodes) != 0 {
  154. newnode := []*SwaggerModel{}
  155. for _, n := range nodes {
  156. allnode = append(allnode, n)
  157. if n.Items != nil {
  158. newnode = append(newnode, n.Items)
  159. }
  160. for _, p := range n.Properties {
  161. newnode = append(newnode, p)
  162. }
  163. }
  164. nodes = newnode
  165. }
  166. return allnode
  167. }
  168. const pkgLoadMode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo
  169. var AllPackages = map[string]*packages.Package{}
  170. var Models = map[*ast.Ident]*entityDecl{}
  171. var AllStructModel = map[string]*SwaggerModel{}
  172. var AllSwaggerModel = map[string]*SwaggerModel{}
  173. var SpecifiedSwaggerModel = []map[string]string{}
  174. var Routes = map[string]pathModel{}
  175. type entityDecl struct {
  176. Type *types.Named
  177. File *ast.File
  178. Pkg *packages.Package
  179. }
  180. var outfile string
  181. var modname string
  182. func init() {
  183. flag.StringVar(&outfile, "o", "swagger.yml", "output file")
  184. flag.StringVar(&modname, "m", "dashoo.cn/micro", "mod name")
  185. }
  186. func main() {
  187. // testGoParser()
  188. // return
  189. flag.Parse()
  190. toload := []string{
  191. // "dashoo.cn/micro/app/model/contract",
  192. // "dashoo.cn/micro/app/handler/contract",
  193. }
  194. for _, d := range []string{"app/model", "app/handler"} {
  195. files, err := ioutil.ReadDir(d)
  196. if err != nil {
  197. if os.IsNotExist(err) {
  198. fmt.Printf("%s not found\n", d)
  199. return
  200. }
  201. panic(err)
  202. }
  203. for _, f := range files {
  204. if f.IsDir() {
  205. toload = append(toload, fmt.Sprintf("%s/%s/%s", modname, d, f.Name()))
  206. }
  207. info, err := os.Stat(path.Join(d, f.Name(), "internal"))
  208. if os.IsNotExist(err) {
  209. continue
  210. }
  211. if info.IsDir() {
  212. toload = append(toload, fmt.Sprintf("%s/%s/%s/internal", modname, d, f.Name()))
  213. }
  214. }
  215. }
  216. for _, p := range toload {
  217. fmt.Println(p)
  218. }
  219. cfg := &packages.Config{Mode: pkgLoadMode}
  220. pkgs, err := packages.Load(cfg, toload...)
  221. if err != nil {
  222. fmt.Fprintf(os.Stderr, "load: %v\n", err)
  223. os.Exit(1)
  224. }
  225. if packages.PrintErrors(pkgs) > 0 {
  226. os.Exit(1)
  227. }
  228. for _, pkg := range pkgs {
  229. AllPackages[pkg.PkgPath] = pkg
  230. for _, file := range pkg.Syntax {
  231. for _, dt := range file.Decls {
  232. switch fd := dt.(type) {
  233. case *ast.GenDecl:
  234. for _, sp := range fd.Specs {
  235. switch ts := sp.(type) {
  236. case *ast.TypeSpec:
  237. def, ok := pkg.TypesInfo.Defs[ts.Name]
  238. if !ok {
  239. debugLog("couldn't find type info for %s", ts.Name)
  240. continue
  241. }
  242. nt, isNamed := def.Type().(*types.Named)
  243. if !isNamed {
  244. debugLog("%s is not a named type but a %T", ts.Name, def.Type())
  245. continue
  246. }
  247. key := ts.Name
  248. Models[key] = &entityDecl{
  249. Type: nt,
  250. File: file,
  251. Pkg: pkg,
  252. }
  253. }
  254. }
  255. }
  256. }
  257. }
  258. }
  259. // Print the names of the source files
  260. // for each package listed on the command line.
  261. for _, pkg := range pkgs {
  262. debugLog("-----------%s", pkg.Name)
  263. for _, f := range pkg.Syntax {
  264. detectModels(pkg, f)
  265. }
  266. }
  267. for n := range AllStructModel {
  268. m := AllStructModel[n]
  269. if m == nil {
  270. continue
  271. }
  272. // fmt.Println(m.Ref, AllStructModel[m.Ref])
  273. for _, subm := range RangeSwaggerModel(m) {
  274. // 属性引用自己的循环引用,两个不同 struct 的循环引用这里没有检测
  275. if n == subm.Ref {
  276. continue
  277. }
  278. if subm.Ref != "" && AllStructModel[subm.Ref] != nil {
  279. subm.Items = AllStructModel[subm.Ref].Items
  280. subm.Properties = AllStructModel[subm.Ref].Properties
  281. }
  282. }
  283. // b, err := yaml.Marshal(m)
  284. // if err != nil {
  285. // panic(err)
  286. // }
  287. // fmt.Println(n)
  288. // fmt.Println(string(b))
  289. }
  290. // 展开嵌入字段
  291. for n := range AllStructModel {
  292. m := AllStructModel[n]
  293. if m == nil {
  294. continue
  295. }
  296. for _, subm := range RangeSwaggerModel(m) {
  297. for pn, p := range subm.Properties {
  298. if !p.Embedded {
  299. continue
  300. }
  301. // fmt.Println("--", n, "|", pn)
  302. delete(subm.Properties, pn)
  303. for ssn, ssp := range p.Properties {
  304. if _, ok := subm.Properties[ssn]; ok {
  305. continue
  306. }
  307. subm.Properties[ssn] = ssp
  308. }
  309. }
  310. }
  311. }
  312. genconfig(outfile)
  313. }
  314. func genconfig(filename string) {
  315. f, err := os.Create(filename)
  316. if err != nil {
  317. panic(err)
  318. }
  319. defer f.Close()
  320. debugLog("genconfig AllStructModel")
  321. for n := range AllStructModel {
  322. m := AllStructModel[n]
  323. if m == nil {
  324. continue
  325. }
  326. namelist := strings.Split(n, ".")
  327. name := namelist[len(namelist)-1]
  328. swagger["components"].(map[string]interface{})["schemas"].(map[string]interface{})[name] = m
  329. swagger["components"].(map[string]interface{})["examples"].(map[string]interface{})[name] = map[string]interface{}{
  330. "value": map[string]interface{}{
  331. "placeholder": "",
  332. },
  333. }
  334. }
  335. debugLog("genconfig Routes")
  336. for p, m := range Routes {
  337. swagger["paths"].(map[string]interface{})[p] = newPath(m)
  338. }
  339. encoder := yaml.NewEncoder(f)
  340. encoder.SetIndent(2)
  341. debugLog("genconfig Encode")
  342. err = encoder.Encode(swagger)
  343. if err != nil {
  344. panic(err)
  345. }
  346. }
  347. func getFuncRecv(fd *ast.FuncDecl) string {
  348. defer func() {
  349. if r := recover(); r != nil {
  350. debugLog("%s", r)
  351. }
  352. }()
  353. return fd.Recv.List[0].Type.(*ast.StarExpr).X.(*ast.Ident).Name
  354. }
  355. func getFuncParams(fd *ast.FuncDecl) string {
  356. defer func() {
  357. if r := recover(); r != nil {
  358. debugLog("%s", r)
  359. }
  360. }()
  361. return fd.Type.Params.List[1].Type.(*ast.StarExpr).X.(*ast.SelectorExpr).Sel.Name
  362. }
  363. func detectPath(fd *ast.FuncDecl) {
  364. recv := getFuncRecv(fd)
  365. fname := fd.Name.String()
  366. req := getFuncParams(fd)
  367. debugLog("//////////%v %s %s %s %s", fd, fname, recv, req, fd.Doc.Text())
  368. commentText := fd.Doc.Text()
  369. for _, c := range strings.Split(commentText, "\n") {
  370. c = strings.TrimSpace(c)
  371. if !strings.HasPrefix(c, "Swagger:") {
  372. continue
  373. }
  374. names := strings.Split(c, " ")
  375. tags := []string{}
  376. summary := ""
  377. if len(names) == 3 {
  378. tags = strings.Split(names[1], ",")
  379. summary = names[2]
  380. recvNamed := strings.Split(names[0], ":")
  381. if len(recvNamed) == 2 {
  382. recv = recvNamed[1]
  383. }
  384. }
  385. path := fmt.Sprintf("/%s.%s", recv, fname)
  386. operationId := recv + fname
  387. Routes[path] = pathModel{
  388. OperationId: operationId,
  389. Summary: summary,
  390. Tags: tags,
  391. Request: req,
  392. }
  393. break
  394. }
  395. }
  396. func detectModels(pkg *packages.Package, file *ast.File) {
  397. for _, dt := range file.Decls {
  398. switch fd := dt.(type) {
  399. case *ast.BadDecl:
  400. continue
  401. case *ast.FuncDecl:
  402. detectPath(fd)
  403. case *ast.GenDecl:
  404. processDecl(pkg, file, fd)
  405. }
  406. }
  407. }
  408. func processDecl(pkg *packages.Package, file *ast.File, gd *ast.GenDecl) error {
  409. for _, sp := range gd.Specs {
  410. switch ts := sp.(type) {
  411. case *ast.ValueSpec:
  412. return nil
  413. case *ast.ImportSpec:
  414. return nil
  415. case *ast.TypeSpec:
  416. def, ok := pkg.TypesInfo.Defs[ts.Name]
  417. if !ok {
  418. debugLog("couldn't find type info for %s", ts.Name)
  419. continue
  420. }
  421. nt, isNamed := def.Type().(*types.Named)
  422. if !isNamed {
  423. debugLog("%s is not a named type but a %T", ts.Name, def.Type())
  424. continue
  425. }
  426. comments := ts.Doc // type ( /* doc */ Foo struct{} )
  427. if comments == nil {
  428. comments = gd.Doc // /* doc */ type ( Foo struct{} )
  429. }
  430. // decl := &entityDecl{
  431. // Comments: comments,
  432. // Type: nt,
  433. // Ident: ts.Name,
  434. // Spec: ts,
  435. // File: file,
  436. // Pkg: pkg,
  437. // }
  438. // key := ts.Name
  439. // if n&modelNode != 0 && decl.HasModelAnnotation() {
  440. // a.Models[key] = decl
  441. // }
  442. // if n&parametersNode != 0 && decl.HasParameterAnnotation() {
  443. // a.Parameters = append(a.Parameters, decl)
  444. // }
  445. // if n&responseNode != 0 && decl.HasResponseAnnotation() {
  446. // a.Responses = append(a.Responses, decl)
  447. // }
  448. switch tpe := nt.Obj().Type().(type) {
  449. case *types.Struct:
  450. st := tpe
  451. for i := 0; i < st.NumFields(); i++ {
  452. fld := st.Field(i)
  453. if !fld.Anonymous() {
  454. debugLog("skipping field %q for allOf scan because not anonymous\n", fld.Name())
  455. continue
  456. }
  457. tg := st.Tag(i)
  458. debugLog(fld.Name(), tg)
  459. }
  460. case *types.Slice:
  461. debugLog("Slice")
  462. case *types.Basic:
  463. debugLog("Basic")
  464. case *types.Interface:
  465. debugLog("Interface")
  466. case *types.Array:
  467. debugLog("Array")
  468. case *types.Map:
  469. debugLog("Map")
  470. case *types.Named:
  471. debugLog("Named")
  472. o := tpe.Obj()
  473. if o != nil {
  474. debugLog("got the named type object: %s.%s | isAlias: %t | exported: %t //////////// %s", o.Pkg().Path(), o.Name(), o.IsAlias(), o.Exported(), comments.Text())
  475. if o.Pkg().Name() == "time" && o.Name() == "Time" {
  476. // schema.Typed("string", "date-time")
  477. return nil
  478. }
  479. for {
  480. ti := pkg.TypesInfo.Types[ts.Type]
  481. if ti.IsBuiltin() {
  482. break
  483. }
  484. if ti.IsType() {
  485. err, ret := buildFromType(ti.Type, file, comments.Text())
  486. if err != nil {
  487. return err
  488. }
  489. AllStructModel[fmt.Sprintf("%s.%s", o.Pkg().Path(), o.Name())] = ret
  490. break
  491. }
  492. }
  493. }
  494. }
  495. }
  496. }
  497. return nil
  498. }
  499. func buildFromType(tpe types.Type, declFile *ast.File, desc string) (error, *SwaggerModel) {
  500. switch titpe := tpe.(type) {
  501. case *types.Basic:
  502. return nil, &SwaggerModel{
  503. Type: swaggerSchemaForType(titpe.String()),
  504. Description: desc,
  505. }
  506. case *types.Pointer:
  507. return buildFromType(titpe.Elem(), declFile, desc)
  508. case *types.Struct:
  509. return buildFromStruct(declFile, titpe, desc)
  510. case *types.Slice:
  511. err, ret := buildFromType(titpe.Elem(), declFile, desc)
  512. if err != nil {
  513. return err, nil
  514. }
  515. return nil, &SwaggerModel{
  516. Type: "array",
  517. Items: ret,
  518. Description: desc,
  519. }
  520. case *types.Named:
  521. tio := titpe.Obj()
  522. debugLog("%s", tio)
  523. if tio.Pkg() == nil && tio.Name() == "error" {
  524. return nil, &SwaggerModel{
  525. Type: swaggerSchemaForType(tio.Name()),
  526. Description: desc,
  527. }
  528. }
  529. if tpe.String() == "github.com/gogf/gf/os/gtime.Time" {
  530. return nil, &SwaggerModel{
  531. Type: "string",
  532. Description: desc + "DATETIME",
  533. }
  534. }
  535. debugLog("named refined type %s.%s", tio.Pkg().Path(), tio.Name())
  536. pkg, found := PkgForType(tpe)
  537. if !found {
  538. // this must be a builtin
  539. debugLog("skipping because package is nil: %s", tpe.String())
  540. return nil, nil
  541. }
  542. if pkg.Name == "time" && tio.Name() == "Time" {
  543. return nil, &SwaggerModel{
  544. Type: "string",
  545. Description: desc,
  546. }
  547. }
  548. switch utitpe := tpe.Underlying().(type) {
  549. case *types.Struct:
  550. if decl, ok := FindModel(tio.Pkg().Path(), tio.Name()); ok {
  551. if decl.Type.Obj().Pkg().Path() == "time" && decl.Type.Obj().Name() == "Time" {
  552. return nil, &SwaggerModel{
  553. Type: "string",
  554. Description: desc,
  555. }
  556. }
  557. debugLog("!!!!!!!%s", decl.Type.String())
  558. return nil, &SwaggerModel{
  559. Type: "object",
  560. Ref: decl.Type.String(),
  561. }
  562. }
  563. case *types.Slice:
  564. debugLog("!!!!!!!slice %s", utitpe.Elem())
  565. // decl, ok := FindModel(tio.Pkg().Path(), tio.Name())
  566. // return buildFromType(utitpe.Elem(), tgt.Items())
  567. }
  568. case *types.Interface:
  569. return nil, &SwaggerModel{
  570. Type: "object",
  571. Description: desc,
  572. }
  573. default:
  574. panic(fmt.Sprintf("WARNING: can't determine refined type %s (%T)", titpe.String(), titpe))
  575. }
  576. return nil, nil
  577. }
  578. func buildFromStruct(declFile *ast.File, st *types.Struct, desc string) (error, *SwaggerModel) {
  579. // for i := 0; i < st.NumFields(); i++ {
  580. // fld := st.Field(i)
  581. // if !fld.Anonymous() {
  582. // debugLog("skipping field %q for allOf scan because not anonymous", fld.Name())
  583. // continue
  584. // }
  585. // tg := st.Tag(i)
  586. // debugLog("maybe allof field(%t) %s: %s (%T) [%q](anon: %t, embedded: %t)", fld.IsField(), fld.Name(), fld.Type().String(), fld.Type(), tg, fld.Anonymous(), fld.Embedded())
  587. // var afld *ast.Field
  588. // ans, _ := astutil.PathEnclosingInterval(declFile, fld.Pos(), fld.Pos())
  589. // // debugLog("got %d nodes (exact: %t)", len(ans), isExact)
  590. // for _, an := range ans {
  591. // at, valid := an.(*ast.Field)
  592. // if !valid {
  593. // continue
  594. // }
  595. // debugLog("maybe allof field %s: %s(%T) [%q]", fld.Name(), fld.Type().String(), fld.Type(), tg)
  596. // afld = at
  597. // break
  598. // }
  599. // if afld == nil {
  600. // debugLog("can't find source associated with %s for %s", fld.String(), st.String())
  601. // continue
  602. // }
  603. // _, ignore, _, err := parseJSONTag(afld)
  604. // if err != nil {
  605. // return err
  606. // }
  607. // if ignore {
  608. // continue
  609. // }
  610. // }
  611. properties := map[string]*SwaggerModel{}
  612. for i := 0; i < st.NumFields(); i++ {
  613. fld := st.Field(i)
  614. tg := st.Tag(i)
  615. if fld.Embedded() {
  616. if fld.Name() == "PageReq" {
  617. properties["beginTime"] = &SwaggerModel{Type: "string", Description: "开始时间"}
  618. properties["endTime"] = &SwaggerModel{Type: "string", Description: "结束时间"}
  619. properties["pageNum"] = &SwaggerModel{Type: "int", Description: "当前页码"}
  620. properties["pageSize"] = &SwaggerModel{Type: "int", Description: "每页数"}
  621. properties["orderBy"] = &SwaggerModel{Type: "string", Description: "排序方式"}
  622. }
  623. debugLog("Embedded %s, %s", fld.Name(), fld.Pkg())
  624. // continue
  625. }
  626. if !fld.Exported() {
  627. debugLog("skipping field %s because it's not exported", fld.Name())
  628. continue
  629. }
  630. var afld *ast.Field
  631. ans, _ := astutil.PathEnclosingInterval(declFile, fld.Pos(), fld.Pos())
  632. // debugLog("got %d nodes (exact: %t)", len(ans), isExact)
  633. for _, an := range ans {
  634. at, valid := an.(*ast.Field)
  635. if !valid {
  636. continue
  637. }
  638. debugLog("field %s: %s(%T) [%q] ==> %s", fld.Name(), fld.Type().String(), fld.Type(), tg, at.Doc.Text())
  639. afld = at
  640. break
  641. }
  642. if afld == nil {
  643. debugLog("can't find source associated with %s", fld.String())
  644. continue
  645. }
  646. name, _, isString, err := parseJSONTag(afld)
  647. if err != nil {
  648. return err, nil
  649. }
  650. if name == "" {
  651. name = fld.Name()
  652. }
  653. commentText := ""
  654. if afld.Comment != nil {
  655. commentText = afld.Comment.Text()
  656. }
  657. err, ret := buildFromType(fld.Type(), declFile, commentText)
  658. if err != nil {
  659. return err, nil
  660. }
  661. // if fld.Embedded() {
  662. // fmt.Println(fld.Name(), ret)
  663. // }
  664. if ret == nil {
  665. continue
  666. }
  667. ret.Embedded = fld.Embedded()
  668. debugLog("****** %s %v %s", fld.Type(), ret, commentText)
  669. if isString {
  670. return nil, &SwaggerModel{
  671. Type: "string",
  672. Description: desc,
  673. }
  674. }
  675. properties[name] = ret
  676. }
  677. return nil, &SwaggerModel{
  678. Type: "object",
  679. Properties: properties,
  680. Description: desc,
  681. }
  682. }
  683. func swaggerSchemaForType(typeName string) string {
  684. switch typeName {
  685. case "bool":
  686. return "boolean"
  687. case "byte":
  688. return "integer"
  689. case "float32":
  690. return "number"
  691. case "float64":
  692. return "number"
  693. case "int":
  694. return "integer"
  695. case "int16":
  696. return "integer"
  697. case "int32":
  698. return "integer"
  699. case "int64":
  700. return "integer"
  701. case "int8":
  702. return "integer"
  703. case "rune":
  704. return "integer"
  705. case "string":
  706. return "string"
  707. case "uint":
  708. return "integer"
  709. case "uint16":
  710. return "integer"
  711. case "uint32":
  712. return "integer"
  713. case "uint64":
  714. return "integer"
  715. case "uint8":
  716. return "integer"
  717. }
  718. return ""
  719. }
  720. func FindModel(pkgPath, name string) (*entityDecl, bool) {
  721. for _, cand := range Models {
  722. ct := cand.Type.Obj()
  723. if ct.Name() == name && ct.Pkg().Path() == pkgPath {
  724. return cand, true
  725. }
  726. }
  727. return nil, false
  728. }
  729. func PkgForType(t types.Type) (*packages.Package, bool) {
  730. switch tpe := t.(type) {
  731. // case *types.Basic:
  732. // case *types.Struct:
  733. // case *types.Pointer:
  734. // case *types.Interface:
  735. // case *types.Array:
  736. // case *types.Slice:
  737. // case *types.Map:
  738. case *types.Named:
  739. v, ok := AllPackages[tpe.Obj().Pkg().Path()]
  740. return v, ok
  741. default:
  742. log.Printf("unknown type to find the package for [%T]: %s", t, t.String())
  743. return nil, false
  744. }
  745. }
  746. func parseJSONTag(field *ast.Field) (name string, ignore bool, isString bool, err error) {
  747. if len(field.Names) > 0 {
  748. name = field.Names[0].Name
  749. }
  750. if field.Tag == nil || len(strings.TrimSpace(field.Tag.Value)) == 0 {
  751. return name, false, false, nil
  752. }
  753. tv, err := strconv.Unquote(field.Tag.Value)
  754. if err != nil {
  755. return name, false, false, err
  756. }
  757. if strings.TrimSpace(tv) != "" {
  758. st := reflect.StructTag(tv)
  759. jsonParts := tagOptions(strings.Split(st.Get("json"), ","))
  760. if jsonParts.Contain("string") {
  761. // Need to check if the field type is a scalar. Otherwise, the
  762. // ",string" directive doesn't apply.
  763. isString = isFieldStringable(field.Type)
  764. }
  765. switch jsonParts.Name() {
  766. case "-":
  767. return name, true, isString, nil
  768. case "":
  769. return name, false, isString, nil
  770. default:
  771. return jsonParts.Name(), false, isString, nil
  772. }
  773. }
  774. return name, false, false, nil
  775. }
  776. func isFieldStringable(tpe ast.Expr) bool {
  777. if ident, ok := tpe.(*ast.Ident); ok {
  778. switch ident.Name {
  779. case "int", "int8", "int16", "int32", "int64",
  780. "uint", "uint8", "uint16", "uint32", "uint64",
  781. "float64", "string", "bool":
  782. return true
  783. }
  784. } else if starExpr, ok := tpe.(*ast.StarExpr); ok {
  785. return isFieldStringable(starExpr.X)
  786. } else {
  787. return false
  788. }
  789. return false
  790. }
  791. type tagOptions []string
  792. func (t tagOptions) Contain(option string) bool {
  793. for i := 1; i < len(t); i++ {
  794. if t[i] == option {
  795. return true
  796. }
  797. }
  798. return false
  799. }
  800. func (t tagOptions) Name() string {
  801. return t[0]
  802. }
  803. var Debug = false
  804. func debugLog(format string, args ...interface{}) {
  805. if Debug {
  806. log.Printf(format, args...)
  807. }
  808. }