swagger.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  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. if !strings.HasPrefix(c, "swagger:route") {
  371. continue
  372. }
  373. names := strings.Split(c, " ")
  374. tags := []string{}
  375. summary := ""
  376. if len(names) == 3 {
  377. tags = strings.Split(names[1], ",")
  378. summary = names[2]
  379. }
  380. path := fmt.Sprintf("/%s.%s", recv, fname)
  381. operationId := recv + fname
  382. Routes[path] = pathModel{
  383. OperationId: operationId,
  384. Summary: summary,
  385. Tags: tags,
  386. Request: req,
  387. }
  388. break
  389. }
  390. }
  391. func detectModels(pkg *packages.Package, file *ast.File) {
  392. for _, dt := range file.Decls {
  393. switch fd := dt.(type) {
  394. case *ast.BadDecl:
  395. continue
  396. case *ast.FuncDecl:
  397. detectPath(fd)
  398. case *ast.GenDecl:
  399. processDecl(pkg, file, fd)
  400. }
  401. }
  402. }
  403. func processDecl(pkg *packages.Package, file *ast.File, gd *ast.GenDecl) error {
  404. for _, sp := range gd.Specs {
  405. switch ts := sp.(type) {
  406. case *ast.ValueSpec:
  407. return nil
  408. case *ast.ImportSpec:
  409. return nil
  410. case *ast.TypeSpec:
  411. def, ok := pkg.TypesInfo.Defs[ts.Name]
  412. if !ok {
  413. debugLog("couldn't find type info for %s", ts.Name)
  414. continue
  415. }
  416. nt, isNamed := def.Type().(*types.Named)
  417. if !isNamed {
  418. debugLog("%s is not a named type but a %T", ts.Name, def.Type())
  419. continue
  420. }
  421. comments := ts.Doc // type ( /* doc */ Foo struct{} )
  422. if comments == nil {
  423. comments = gd.Doc // /* doc */ type ( Foo struct{} )
  424. }
  425. // decl := &entityDecl{
  426. // Comments: comments,
  427. // Type: nt,
  428. // Ident: ts.Name,
  429. // Spec: ts,
  430. // File: file,
  431. // Pkg: pkg,
  432. // }
  433. // key := ts.Name
  434. // if n&modelNode != 0 && decl.HasModelAnnotation() {
  435. // a.Models[key] = decl
  436. // }
  437. // if n&parametersNode != 0 && decl.HasParameterAnnotation() {
  438. // a.Parameters = append(a.Parameters, decl)
  439. // }
  440. // if n&responseNode != 0 && decl.HasResponseAnnotation() {
  441. // a.Responses = append(a.Responses, decl)
  442. // }
  443. switch tpe := nt.Obj().Type().(type) {
  444. case *types.Struct:
  445. st := tpe
  446. for i := 0; i < st.NumFields(); i++ {
  447. fld := st.Field(i)
  448. if !fld.Anonymous() {
  449. debugLog("skipping field %q for allOf scan because not anonymous\n", fld.Name())
  450. continue
  451. }
  452. tg := st.Tag(i)
  453. debugLog(fld.Name(), tg)
  454. }
  455. case *types.Slice:
  456. debugLog("Slice")
  457. case *types.Basic:
  458. debugLog("Basic")
  459. case *types.Interface:
  460. debugLog("Interface")
  461. case *types.Array:
  462. debugLog("Array")
  463. case *types.Map:
  464. debugLog("Map")
  465. case *types.Named:
  466. debugLog("Named")
  467. o := tpe.Obj()
  468. if o != nil {
  469. debugLog("got the named type object: %s.%s | isAlias: %t | exported: %t //////////// %s", o.Pkg().Path(), o.Name(), o.IsAlias(), o.Exported(), comments.Text())
  470. if o.Pkg().Name() == "time" && o.Name() == "Time" {
  471. // schema.Typed("string", "date-time")
  472. return nil
  473. }
  474. for {
  475. ti := pkg.TypesInfo.Types[ts.Type]
  476. if ti.IsBuiltin() {
  477. break
  478. }
  479. if ti.IsType() {
  480. err, ret := buildFromType(ti.Type, file, comments.Text())
  481. if err != nil {
  482. return err
  483. }
  484. AllStructModel[fmt.Sprintf("%s.%s", o.Pkg().Path(), o.Name())] = ret
  485. break
  486. }
  487. }
  488. }
  489. }
  490. }
  491. }
  492. return nil
  493. }
  494. func buildFromType(tpe types.Type, declFile *ast.File, desc string) (error, *SwaggerModel) {
  495. switch titpe := tpe.(type) {
  496. case *types.Basic:
  497. return nil, &SwaggerModel{
  498. Type: swaggerSchemaForType(titpe.String()),
  499. Description: desc,
  500. }
  501. case *types.Pointer:
  502. return buildFromType(titpe.Elem(), declFile, desc)
  503. case *types.Struct:
  504. return buildFromStruct(declFile, titpe, desc)
  505. case *types.Slice:
  506. err, ret := buildFromType(titpe.Elem(), declFile, desc)
  507. if err != nil {
  508. return err, nil
  509. }
  510. return nil, &SwaggerModel{
  511. Type: "array",
  512. Items: ret,
  513. Description: desc,
  514. }
  515. case *types.Named:
  516. tio := titpe.Obj()
  517. debugLog("%s", tio)
  518. if tio.Pkg() == nil && tio.Name() == "error" {
  519. return nil, &SwaggerModel{
  520. Type: swaggerSchemaForType(tio.Name()),
  521. Description: desc,
  522. }
  523. }
  524. if tpe.String() == "github.com/gogf/gf/os/gtime.Time" {
  525. return nil, &SwaggerModel{
  526. Type: "string",
  527. Description: desc + "DATETIME",
  528. }
  529. }
  530. debugLog("named refined type %s.%s", tio.Pkg().Path(), tio.Name())
  531. pkg, found := PkgForType(tpe)
  532. if !found {
  533. // this must be a builtin
  534. debugLog("skipping because package is nil: %s", tpe.String())
  535. return nil, nil
  536. }
  537. if pkg.Name == "time" && tio.Name() == "Time" {
  538. return nil, &SwaggerModel{
  539. Type: "string",
  540. Description: desc,
  541. }
  542. }
  543. switch utitpe := tpe.Underlying().(type) {
  544. case *types.Struct:
  545. if decl, ok := FindModel(tio.Pkg().Path(), tio.Name()); ok {
  546. if decl.Type.Obj().Pkg().Path() == "time" && decl.Type.Obj().Name() == "Time" {
  547. return nil, &SwaggerModel{
  548. Type: "string",
  549. Description: desc,
  550. }
  551. }
  552. debugLog("!!!!!!!%s", decl.Type.String())
  553. return nil, &SwaggerModel{
  554. Type: "object",
  555. Ref: decl.Type.String(),
  556. }
  557. }
  558. case *types.Slice:
  559. debugLog("!!!!!!!slice %s", utitpe.Elem())
  560. // decl, ok := FindModel(tio.Pkg().Path(), tio.Name())
  561. // return buildFromType(utitpe.Elem(), tgt.Items())
  562. }
  563. case *types.Interface:
  564. return nil, &SwaggerModel{
  565. Type: "object",
  566. Description: desc,
  567. }
  568. default:
  569. panic(fmt.Sprintf("WARNING: can't determine refined type %s (%T)", titpe.String(), titpe))
  570. }
  571. return nil, nil
  572. }
  573. func buildFromStruct(declFile *ast.File, st *types.Struct, desc string) (error, *SwaggerModel) {
  574. // for i := 0; i < st.NumFields(); i++ {
  575. // fld := st.Field(i)
  576. // if !fld.Anonymous() {
  577. // debugLog("skipping field %q for allOf scan because not anonymous", fld.Name())
  578. // continue
  579. // }
  580. // tg := st.Tag(i)
  581. // 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())
  582. // var afld *ast.Field
  583. // ans, _ := astutil.PathEnclosingInterval(declFile, fld.Pos(), fld.Pos())
  584. // // debugLog("got %d nodes (exact: %t)", len(ans), isExact)
  585. // for _, an := range ans {
  586. // at, valid := an.(*ast.Field)
  587. // if !valid {
  588. // continue
  589. // }
  590. // debugLog("maybe allof field %s: %s(%T) [%q]", fld.Name(), fld.Type().String(), fld.Type(), tg)
  591. // afld = at
  592. // break
  593. // }
  594. // if afld == nil {
  595. // debugLog("can't find source associated with %s for %s", fld.String(), st.String())
  596. // continue
  597. // }
  598. // _, ignore, _, err := parseJSONTag(afld)
  599. // if err != nil {
  600. // return err
  601. // }
  602. // if ignore {
  603. // continue
  604. // }
  605. // }
  606. properties := map[string]*SwaggerModel{}
  607. for i := 0; i < st.NumFields(); i++ {
  608. fld := st.Field(i)
  609. tg := st.Tag(i)
  610. if fld.Embedded() {
  611. if fld.Name() == "PageReq" {
  612. properties["beginTime"] = &SwaggerModel{Type: "string", Description: "开始时间"}
  613. properties["endTime"] = &SwaggerModel{Type: "string", Description: "结束时间"}
  614. properties["pageNum"] = &SwaggerModel{Type: "int", Description: "当前页码"}
  615. properties["pageSize"] = &SwaggerModel{Type: "int", Description: "每页数"}
  616. properties["orderBy"] = &SwaggerModel{Type: "string", Description: "排序方式"}
  617. }
  618. debugLog("Embedded %s, %s", fld.Name(), fld.Pkg())
  619. // continue
  620. }
  621. if !fld.Exported() {
  622. debugLog("skipping field %s because it's not exported", fld.Name())
  623. continue
  624. }
  625. var afld *ast.Field
  626. ans, _ := astutil.PathEnclosingInterval(declFile, fld.Pos(), fld.Pos())
  627. // debugLog("got %d nodes (exact: %t)", len(ans), isExact)
  628. for _, an := range ans {
  629. at, valid := an.(*ast.Field)
  630. if !valid {
  631. continue
  632. }
  633. debugLog("field %s: %s(%T) [%q] ==> %s", fld.Name(), fld.Type().String(), fld.Type(), tg, at.Doc.Text())
  634. afld = at
  635. break
  636. }
  637. if afld == nil {
  638. debugLog("can't find source associated with %s", fld.String())
  639. continue
  640. }
  641. name, _, isString, err := parseJSONTag(afld)
  642. if err != nil {
  643. return err, nil
  644. }
  645. if name == "" {
  646. name = fld.Name()
  647. }
  648. commentText := ""
  649. if afld.Comment != nil {
  650. commentText = afld.Comment.Text()
  651. }
  652. err, ret := buildFromType(fld.Type(), declFile, commentText)
  653. if err != nil {
  654. return err, nil
  655. }
  656. // if fld.Embedded() {
  657. // fmt.Println(fld.Name(), ret)
  658. // }
  659. if ret == nil {
  660. continue
  661. }
  662. ret.Embedded = fld.Embedded()
  663. debugLog("****** %s %v %s", fld.Type(), ret, commentText)
  664. if isString {
  665. return nil, &SwaggerModel{
  666. Type: "string",
  667. Description: desc,
  668. }
  669. }
  670. properties[name] = ret
  671. }
  672. return nil, &SwaggerModel{
  673. Type: "object",
  674. Properties: properties,
  675. Description: desc,
  676. }
  677. }
  678. func swaggerSchemaForType(typeName string) string {
  679. switch typeName {
  680. case "bool":
  681. return "boolean"
  682. case "byte":
  683. return "integer"
  684. case "float32":
  685. return "number"
  686. case "float64":
  687. return "number"
  688. case "int":
  689. return "integer"
  690. case "int16":
  691. return "integer"
  692. case "int32":
  693. return "integer"
  694. case "int64":
  695. return "integer"
  696. case "int8":
  697. return "integer"
  698. case "rune":
  699. return "integer"
  700. case "string":
  701. return "string"
  702. case "uint":
  703. return "integer"
  704. case "uint16":
  705. return "integer"
  706. case "uint32":
  707. return "integer"
  708. case "uint64":
  709. return "integer"
  710. case "uint8":
  711. return "integer"
  712. }
  713. return ""
  714. }
  715. func FindModel(pkgPath, name string) (*entityDecl, bool) {
  716. for _, cand := range Models {
  717. ct := cand.Type.Obj()
  718. if ct.Name() == name && ct.Pkg().Path() == pkgPath {
  719. return cand, true
  720. }
  721. }
  722. return nil, false
  723. }
  724. func PkgForType(t types.Type) (*packages.Package, bool) {
  725. switch tpe := t.(type) {
  726. // case *types.Basic:
  727. // case *types.Struct:
  728. // case *types.Pointer:
  729. // case *types.Interface:
  730. // case *types.Array:
  731. // case *types.Slice:
  732. // case *types.Map:
  733. case *types.Named:
  734. v, ok := AllPackages[tpe.Obj().Pkg().Path()]
  735. return v, ok
  736. default:
  737. log.Printf("unknown type to find the package for [%T]: %s", t, t.String())
  738. return nil, false
  739. }
  740. }
  741. func parseJSONTag(field *ast.Field) (name string, ignore bool, isString bool, err error) {
  742. if len(field.Names) > 0 {
  743. name = field.Names[0].Name
  744. }
  745. if field.Tag == nil || len(strings.TrimSpace(field.Tag.Value)) == 0 {
  746. return name, false, false, nil
  747. }
  748. tv, err := strconv.Unquote(field.Tag.Value)
  749. if err != nil {
  750. return name, false, false, err
  751. }
  752. if strings.TrimSpace(tv) != "" {
  753. st := reflect.StructTag(tv)
  754. jsonParts := tagOptions(strings.Split(st.Get("json"), ","))
  755. if jsonParts.Contain("string") {
  756. // Need to check if the field type is a scalar. Otherwise, the
  757. // ",string" directive doesn't apply.
  758. isString = isFieldStringable(field.Type)
  759. }
  760. switch jsonParts.Name() {
  761. case "-":
  762. return name, true, isString, nil
  763. case "":
  764. return name, false, isString, nil
  765. default:
  766. return jsonParts.Name(), false, isString, nil
  767. }
  768. }
  769. return name, false, false, nil
  770. }
  771. func isFieldStringable(tpe ast.Expr) bool {
  772. if ident, ok := tpe.(*ast.Ident); ok {
  773. switch ident.Name {
  774. case "int", "int8", "int16", "int32", "int64",
  775. "uint", "uint8", "uint16", "uint32", "uint64",
  776. "float64", "string", "bool":
  777. return true
  778. }
  779. } else if starExpr, ok := tpe.(*ast.StarExpr); ok {
  780. return isFieldStringable(starExpr.X)
  781. } else {
  782. return false
  783. }
  784. return false
  785. }
  786. type tagOptions []string
  787. func (t tagOptions) Contain(option string) bool {
  788. for i := 1; i < len(t); i++ {
  789. if t[i] == option {
  790. return true
  791. }
  792. }
  793. return false
  794. }
  795. func (t tagOptions) Name() string {
  796. return t[0]
  797. }
  798. var Debug = false
  799. func debugLog(format string, args ...interface{}) {
  800. if Debug {
  801. log.Printf(format, args...)
  802. }
  803. }