swagger.go 21 KB

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