swagger.go 21 KB

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