github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/codescan/parser.go (about)

     1  package codescan
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"go/ast"
     7  	"reflect"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/go-openapi/loads/fmts"
    13  	"github.com/go-openapi/spec"
    14  	"github.com/pkg/errors"
    15  	"gopkg.in/yaml.v3"
    16  )
    17  
    18  func shouldAcceptTag(tags []string, includeTags map[string]bool, excludeTags map[string]bool) bool {
    19  	for _, tag := range tags {
    20  		if len(includeTags) > 0 {
    21  			if includeTags[tag] {
    22  				return true
    23  			}
    24  		} else if len(excludeTags) > 0 {
    25  			if excludeTags[tag] {
    26  				return false
    27  			}
    28  		}
    29  	}
    30  	return len(includeTags) == 0
    31  }
    32  
    33  func shouldAcceptPkg(path string, includePkgs, excludePkgs []string) bool {
    34  	if len(includePkgs) == 0 && len(excludePkgs) == 0 {
    35  		return true
    36  	}
    37  	for _, pkgName := range includePkgs {
    38  		matched, _ := regexp.MatchString(pkgName, path)
    39  		if matched {
    40  			return true
    41  		}
    42  	}
    43  	for _, pkgName := range excludePkgs {
    44  		matched, _ := regexp.MatchString(pkgName, path)
    45  		if matched {
    46  			return false
    47  		}
    48  	}
    49  	return len(includePkgs) == 0
    50  }
    51  
    52  // Many thanks go to https://github.com/yvasiyarov/swagger
    53  // this is loosely based on that implementation but for swagger 2.0
    54  
    55  func joinDropLast(lines []string) string {
    56  	l := len(lines)
    57  	lns := lines
    58  	if l > 0 && len(strings.TrimSpace(lines[l-1])) == 0 {
    59  		lns = lines[:l-1]
    60  	}
    61  	return strings.Join(lns, "\n")
    62  }
    63  
    64  func removeEmptyLines(lines []string) (notEmpty []string) {
    65  	for _, l := range lines {
    66  		if len(strings.TrimSpace(l)) > 0 {
    67  			notEmpty = append(notEmpty, l)
    68  		}
    69  	}
    70  	return
    71  }
    72  
    73  func rxf(rxp, ar string) *regexp.Regexp {
    74  	return regexp.MustCompile(fmt.Sprintf(rxp, ar))
    75  }
    76  
    77  func allOfMember(comments *ast.CommentGroup) bool {
    78  	if comments != nil {
    79  		for _, cmt := range comments.List {
    80  			for _, ln := range strings.Split(cmt.Text, "\n") {
    81  				if rxAllOf.MatchString(ln) {
    82  					return true
    83  				}
    84  			}
    85  		}
    86  	}
    87  	return false
    88  }
    89  
    90  func fileParam(comments *ast.CommentGroup) bool {
    91  	if comments != nil {
    92  		for _, cmt := range comments.List {
    93  			for _, ln := range strings.Split(cmt.Text, "\n") {
    94  				if rxFileUpload.MatchString(ln) {
    95  					return true
    96  				}
    97  			}
    98  		}
    99  	}
   100  	return false
   101  }
   102  
   103  func strfmtName(comments *ast.CommentGroup) (string, bool) {
   104  	if comments != nil {
   105  		for _, cmt := range comments.List {
   106  			for _, ln := range strings.Split(cmt.Text, "\n") {
   107  				matches := rxStrFmt.FindStringSubmatch(ln)
   108  				if len(matches) > 1 && len(strings.TrimSpace(matches[1])) > 0 {
   109  					return strings.TrimSpace(matches[1]), true
   110  				}
   111  			}
   112  		}
   113  	}
   114  	return "", false
   115  }
   116  
   117  func ignored(comments *ast.CommentGroup) bool {
   118  	if comments != nil {
   119  		for _, cmt := range comments.List {
   120  			for _, ln := range strings.Split(cmt.Text, "\n") {
   121  				if rxIgnoreOverride.MatchString(ln) {
   122  					return true
   123  				}
   124  			}
   125  		}
   126  	}
   127  	return false
   128  }
   129  
   130  func enumName(comments *ast.CommentGroup) (string, bool) {
   131  	if comments != nil {
   132  		for _, cmt := range comments.List {
   133  			for _, ln := range strings.Split(cmt.Text, "\n") {
   134  				matches := rxEnum.FindStringSubmatch(ln)
   135  				if len(matches) > 1 && len(strings.TrimSpace(matches[1])) > 0 {
   136  					return strings.TrimSpace(matches[1]), true
   137  				}
   138  			}
   139  		}
   140  	}
   141  	return "", false
   142  }
   143  
   144  func aliasParam(comments *ast.CommentGroup) bool {
   145  	if comments != nil {
   146  		for _, cmt := range comments.List {
   147  			for _, ln := range strings.Split(cmt.Text, "\n") {
   148  				if rxAlias.MatchString(ln) {
   149  					return true
   150  				}
   151  			}
   152  		}
   153  	}
   154  	return false
   155  }
   156  
   157  func isAliasParam(prop swaggerTypable) bool {
   158  	var isParam bool
   159  	if param, ok := prop.(paramTypable); ok {
   160  		isParam = param.param.In == "query" ||
   161  			param.param.In == "path" ||
   162  			param.param.In == "formData"
   163  	}
   164  	return isParam
   165  }
   166  
   167  func defaultName(comments *ast.CommentGroup) (string, bool) {
   168  	if comments != nil {
   169  		for _, cmt := range comments.List {
   170  			for _, ln := range strings.Split(cmt.Text, "\n") {
   171  				matches := rxDefault.FindStringSubmatch(ln)
   172  				if len(matches) > 1 && len(strings.TrimSpace(matches[1])) > 0 {
   173  					return strings.TrimSpace(matches[1]), true
   174  				}
   175  			}
   176  		}
   177  	}
   178  	return "", false
   179  }
   180  
   181  func typeName(comments *ast.CommentGroup) (string, bool) {
   182  	var typ string
   183  	if comments != nil {
   184  		for _, cmt := range comments.List {
   185  			for _, ln := range strings.Split(cmt.Text, "\n") {
   186  				matches := rxType.FindStringSubmatch(ln)
   187  				if len(matches) > 1 && len(strings.TrimSpace(matches[1])) > 0 {
   188  					typ = strings.TrimSpace(matches[1])
   189  					return typ, true
   190  				}
   191  			}
   192  		}
   193  	}
   194  	return "", false
   195  }
   196  
   197  type swaggerTypable interface {
   198  	Typed(string, string)
   199  	SetRef(spec.Ref)
   200  	Items() swaggerTypable
   201  	Schema() *spec.Schema
   202  	Level() int
   203  	AddExtension(key string, value interface{})
   204  	WithEnum(...interface{})
   205  	WithEnumDescription(desc string)
   206  }
   207  
   208  // Map all Go builtin types that have Json representation to Swagger/Json types.
   209  // See https://golang.org/pkg/builtin/ and http://swagger.io/specification/
   210  func swaggerSchemaForType(typeName string, prop swaggerTypable) error {
   211  	switch typeName {
   212  	case "bool":
   213  		prop.Typed("boolean", "")
   214  	case "byte":
   215  		prop.Typed("integer", "uint8")
   216  	case "complex128", "complex64":
   217  		return fmt.Errorf("unsupported builtin %q (no JSON marshaller)", typeName)
   218  	case "error":
   219  		// TODO: error is often marshalled into a string but not always (e.g. errors package creates
   220  		// errors that are marshalled into an empty object), this could be handled the same way
   221  		// custom JSON marshallers are handled (in future)
   222  		prop.Typed("string", "")
   223  	case "float32":
   224  		prop.Typed("number", "float")
   225  	case "float64":
   226  		prop.Typed("number", "double")
   227  	case "int":
   228  		prop.Typed("integer", "int64")
   229  	case "int16":
   230  		prop.Typed("integer", "int16")
   231  	case "int32":
   232  		prop.Typed("integer", "int32")
   233  	case "int64":
   234  		prop.Typed("integer", "int64")
   235  	case "int8":
   236  		prop.Typed("integer", "int8")
   237  	case "rune":
   238  		prop.Typed("integer", "int32")
   239  	case "string":
   240  		prop.Typed("string", "")
   241  	case "uint":
   242  		prop.Typed("integer", "uint64")
   243  	case "uint16":
   244  		prop.Typed("integer", "uint16")
   245  	case "uint32":
   246  		prop.Typed("integer", "uint32")
   247  	case "uint64":
   248  		prop.Typed("integer", "uint64")
   249  	case "uint8":
   250  		prop.Typed("integer", "uint8")
   251  	case "uintptr":
   252  		prop.Typed("integer", "uint64")
   253  	case "object":
   254  		prop.Typed("object", "")
   255  	default:
   256  		return fmt.Errorf("unsupported type %q", typeName)
   257  	}
   258  	return nil
   259  }
   260  
   261  func newMultiLineTagParser(name string, parser valueParser, skipCleanUp bool) tagParser {
   262  	return tagParser{
   263  		Name:        name,
   264  		MultiLine:   true,
   265  		SkipCleanUp: skipCleanUp,
   266  		Parser:      parser,
   267  	}
   268  }
   269  
   270  func newSingleLineTagParser(name string, parser valueParser) tagParser {
   271  	return tagParser{
   272  		Name:        name,
   273  		MultiLine:   false,
   274  		SkipCleanUp: false,
   275  		Parser:      parser,
   276  	}
   277  }
   278  
   279  type tagParser struct {
   280  	Name        string
   281  	MultiLine   bool
   282  	SkipCleanUp bool
   283  	Lines       []string
   284  	Parser      valueParser
   285  }
   286  
   287  func (st *tagParser) Matches(line string) bool {
   288  	return st.Parser.Matches(line)
   289  }
   290  
   291  func (st *tagParser) Parse(lines []string) error {
   292  	return st.Parser.Parse(lines)
   293  }
   294  
   295  func newYamlParser(rx *regexp.Regexp, setter func(json.RawMessage) error) valueParser {
   296  	return &yamlParser{
   297  		set: setter,
   298  		rx:  rx,
   299  	}
   300  }
   301  
   302  type yamlParser struct {
   303  	set func(json.RawMessage) error
   304  	rx  *regexp.Regexp
   305  }
   306  
   307  func (y *yamlParser) Parse(lines []string) error {
   308  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   309  		return nil
   310  	}
   311  
   312  	var uncommented []string
   313  	uncommented = append(uncommented, removeYamlIndent(lines)...)
   314  
   315  	yamlContent := strings.Join(uncommented, "\n")
   316  	var yamlValue interface{}
   317  	err := yaml.Unmarshal([]byte(yamlContent), &yamlValue)
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	var jsonValue json.RawMessage
   323  	jsonValue, err = fmts.YAMLToJSON(yamlValue)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	return y.set(jsonValue)
   329  }
   330  
   331  func (y *yamlParser) Matches(line string) bool {
   332  	return y.rx.MatchString(line)
   333  }
   334  
   335  // aggregates lines in header until it sees `---`,
   336  // the beginning of a YAML spec
   337  type yamlSpecScanner struct {
   338  	header         []string
   339  	yamlSpec       []string
   340  	setTitle       func([]string)
   341  	setDescription func([]string)
   342  	workedOutTitle bool
   343  	title          []string
   344  	skipHeader     bool
   345  }
   346  
   347  func cleanupScannerLines(lines []string, ur *regexp.Regexp, yamlBlock *regexp.Regexp) []string {
   348  	// bail early when there is nothing to parse
   349  	if len(lines) == 0 {
   350  		return lines
   351  	}
   352  	seenLine := -1
   353  	var lastContent int
   354  	var uncommented []string
   355  	var startBlock bool
   356  	var yamlLines []string
   357  	for i, v := range lines {
   358  		if yamlBlock != nil && yamlBlock.MatchString(v) && !startBlock {
   359  			startBlock = true
   360  			if seenLine < 0 {
   361  				seenLine = i
   362  			}
   363  			continue
   364  		}
   365  		if startBlock {
   366  			if yamlBlock != nil && yamlBlock.MatchString(v) {
   367  				startBlock = false
   368  				uncommented = append(uncommented, removeIndent(yamlLines)...)
   369  				continue
   370  			}
   371  			yamlLines = append(yamlLines, v)
   372  			if v != "" {
   373  				if seenLine < 0 {
   374  					seenLine = i
   375  				}
   376  				lastContent = i
   377  			}
   378  			continue
   379  		}
   380  		str := ur.ReplaceAllString(v, "")
   381  		uncommented = append(uncommented, str)
   382  		if str != "" {
   383  			if seenLine < 0 {
   384  				seenLine = i
   385  			}
   386  			lastContent = i
   387  		}
   388  	}
   389  
   390  	// fixes issue #50
   391  	if seenLine == -1 {
   392  		return nil
   393  	}
   394  	return uncommented[seenLine : lastContent+1]
   395  }
   396  
   397  func (sp *yamlSpecScanner) collectTitleDescription() {
   398  	if sp.workedOutTitle {
   399  		return
   400  	}
   401  	if sp.setTitle == nil {
   402  		sp.header = cleanupScannerLines(sp.header, rxUncommentHeaders, nil)
   403  		return
   404  	}
   405  
   406  	sp.workedOutTitle = true
   407  	sp.title, sp.header = collectScannerTitleDescription(sp.header)
   408  }
   409  
   410  func (sp *yamlSpecScanner) Title() []string {
   411  	sp.collectTitleDescription()
   412  	return sp.title
   413  }
   414  
   415  func (sp *yamlSpecScanner) Description() []string {
   416  	sp.collectTitleDescription()
   417  	return sp.header
   418  }
   419  
   420  func (sp *yamlSpecScanner) Parse(doc *ast.CommentGroup) error {
   421  	if doc == nil {
   422  		return nil
   423  	}
   424  	var startedYAMLSpec bool
   425  COMMENTS:
   426  	for _, c := range doc.List {
   427  		for _, line := range strings.Split(c.Text, "\n") {
   428  			if rxSwaggerAnnotation.MatchString(line) {
   429  				break COMMENTS // a new swagger: annotation terminates this parser
   430  			}
   431  
   432  			if !startedYAMLSpec {
   433  				if rxBeginYAMLSpec.MatchString(line) {
   434  					startedYAMLSpec = true
   435  					sp.yamlSpec = append(sp.yamlSpec, line)
   436  					continue
   437  				}
   438  
   439  				if !sp.skipHeader {
   440  					sp.header = append(sp.header, line)
   441  				}
   442  
   443  				// no YAML spec yet, moving on
   444  				continue
   445  			}
   446  
   447  			sp.yamlSpec = append(sp.yamlSpec, line)
   448  		}
   449  	}
   450  	if sp.setTitle != nil {
   451  		sp.setTitle(sp.Title())
   452  	}
   453  	if sp.setDescription != nil {
   454  		sp.setDescription(sp.Description())
   455  	}
   456  	return nil
   457  }
   458  
   459  func (sp *yamlSpecScanner) UnmarshalSpec(u func([]byte) error) (err error) {
   460  	specYaml := cleanupScannerLines(sp.yamlSpec, rxUncommentYAML, nil)
   461  	if len(specYaml) == 0 {
   462  		return errors.New("no spec available to unmarshal")
   463  	}
   464  
   465  	if !strings.Contains(specYaml[0], "---") {
   466  		return errors.New("yaml spec has to start with `---`")
   467  	}
   468  
   469  	// remove indentation
   470  	specYaml = removeIndent(specYaml)
   471  
   472  	// 1. parse yaml lines
   473  	yamlValue := make(map[interface{}]interface{})
   474  
   475  	yamlContent := strings.Join(specYaml, "\n")
   476  	err = yaml.Unmarshal([]byte(yamlContent), &yamlValue)
   477  	if err != nil {
   478  		return
   479  	}
   480  
   481  	// 2. convert to json
   482  	var jsonValue json.RawMessage
   483  	jsonValue, err = fmts.YAMLToJSON(yamlValue)
   484  	if err != nil {
   485  		return
   486  	}
   487  
   488  	// 3. unmarshal the json into an interface
   489  	var data []byte
   490  	data, err = jsonValue.MarshalJSON()
   491  	if err != nil {
   492  		return
   493  	}
   494  	err = u(data)
   495  	if err != nil {
   496  		return
   497  	}
   498  
   499  	// all parsed, returning...
   500  	sp.yamlSpec = nil // spec is now consumed, so let's erase the parsed lines
   501  	return
   502  }
   503  
   504  // removes indent base on the first line
   505  func removeIndent(spec []string) []string {
   506  	loc := rxIndent.FindStringIndex(spec[0])
   507  	if loc[1] == 0 {
   508  		return spec
   509  	}
   510  	for i := range spec {
   511  		if len(spec[i]) >= loc[1] {
   512  			spec[i] = spec[i][loc[1]-1:]
   513  			start := rxNotIndent.FindStringIndex(spec[i])
   514  			if start[1] == 0 {
   515  				continue
   516  			}
   517  
   518  			spec[i] = strings.Replace(spec[i], "\t", "  ", start[1])
   519  		}
   520  	}
   521  	return spec
   522  }
   523  
   524  // removes indent base on the first line
   525  func removeYamlIndent(spec []string) []string {
   526  	loc := rxIndent.FindStringIndex(spec[0])
   527  	if loc[1] == 0 {
   528  		return nil
   529  	}
   530  	var s []string
   531  	for i := range spec {
   532  		if len(spec[i]) >= loc[1] {
   533  			s = append(s, spec[i][loc[1]-1:])
   534  		}
   535  	}
   536  	return s
   537  }
   538  
   539  // aggregates lines in header until it sees a tag.
   540  type sectionedParser struct {
   541  	header     []string
   542  	matched    map[string]tagParser
   543  	annotation valueParser
   544  
   545  	seenTag        bool
   546  	skipHeader     bool
   547  	setTitle       func([]string)
   548  	setDescription func([]string)
   549  	workedOutTitle bool
   550  	taggers        []tagParser
   551  	currentTagger  *tagParser
   552  	title          []string
   553  	ignored        bool
   554  }
   555  
   556  func (st *sectionedParser) collectTitleDescription() {
   557  	if st.workedOutTitle {
   558  		return
   559  	}
   560  	if st.setTitle == nil {
   561  		st.header = cleanupScannerLines(st.header, rxUncommentHeaders, nil)
   562  		return
   563  	}
   564  
   565  	st.workedOutTitle = true
   566  	st.title, st.header = collectScannerTitleDescription(st.header)
   567  }
   568  
   569  func (st *sectionedParser) Title() []string {
   570  	st.collectTitleDescription()
   571  	return st.title
   572  }
   573  
   574  func (st *sectionedParser) Description() []string {
   575  	st.collectTitleDescription()
   576  	return st.header
   577  }
   578  
   579  func (st *sectionedParser) Parse(doc *ast.CommentGroup) error {
   580  	if doc == nil {
   581  		return nil
   582  	}
   583  COMMENTS:
   584  	for _, c := range doc.List {
   585  		for _, line := range strings.Split(c.Text, "\n") {
   586  			if rxSwaggerAnnotation.MatchString(line) {
   587  				if rxIgnoreOverride.MatchString(line) {
   588  					st.ignored = true
   589  					break COMMENTS // an explicit ignore terminates this parser
   590  				}
   591  				if st.annotation == nil || !st.annotation.Matches(line) {
   592  					break COMMENTS // a new swagger: annotation terminates this parser
   593  				}
   594  
   595  				_ = st.annotation.Parse([]string{line})
   596  				if len(st.header) > 0 {
   597  					st.seenTag = true
   598  				}
   599  				continue
   600  			}
   601  
   602  			var matched bool
   603  			for _, tg := range st.taggers {
   604  				tagger := tg
   605  				if tagger.Matches(line) {
   606  					st.seenTag = true
   607  					st.currentTagger = &tagger
   608  					matched = true
   609  					break
   610  				}
   611  			}
   612  
   613  			if st.currentTagger == nil {
   614  				if !st.skipHeader && !st.seenTag {
   615  					st.header = append(st.header, line)
   616  				}
   617  				// didn't match a tag, moving on
   618  				continue
   619  			}
   620  
   621  			if st.currentTagger.MultiLine && matched {
   622  				// the first line of a multiline tagger doesn't count
   623  				continue
   624  			}
   625  
   626  			ts, ok := st.matched[st.currentTagger.Name]
   627  			if !ok {
   628  				ts = *st.currentTagger
   629  			}
   630  			ts.Lines = append(ts.Lines, line)
   631  			if st.matched == nil {
   632  				st.matched = make(map[string]tagParser)
   633  			}
   634  			st.matched[st.currentTagger.Name] = ts
   635  
   636  			if !st.currentTagger.MultiLine {
   637  				st.currentTagger = nil
   638  			}
   639  		}
   640  	}
   641  	if st.setTitle != nil {
   642  		st.setTitle(st.Title())
   643  	}
   644  	if st.setDescription != nil {
   645  		st.setDescription(st.Description())
   646  	}
   647  	for _, mt := range st.matched {
   648  		if !mt.SkipCleanUp {
   649  			mt.Lines = cleanupScannerLines(mt.Lines, rxUncommentHeaders, nil)
   650  		}
   651  		if err := mt.Parse(mt.Lines); err != nil {
   652  			return err
   653  		}
   654  	}
   655  	return nil
   656  }
   657  
   658  type validationBuilder interface {
   659  	SetMaximum(float64, bool)
   660  	SetMinimum(float64, bool)
   661  	SetMultipleOf(float64)
   662  
   663  	SetMinItems(int64)
   664  	SetMaxItems(int64)
   665  
   666  	SetMinLength(int64)
   667  	SetMaxLength(int64)
   668  	SetPattern(string)
   669  
   670  	SetUnique(bool)
   671  	SetEnum(string)
   672  	SetDefault(interface{})
   673  	SetExample(interface{})
   674  }
   675  
   676  type valueParser interface {
   677  	Parse([]string) error
   678  	Matches(string) bool
   679  }
   680  
   681  type operationValidationBuilder interface {
   682  	validationBuilder
   683  	SetCollectionFormat(string)
   684  }
   685  
   686  type setMaximum struct {
   687  	builder validationBuilder
   688  	rx      *regexp.Regexp
   689  }
   690  
   691  func (sm *setMaximum) Parse(lines []string) error {
   692  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   693  		return nil
   694  	}
   695  	matches := sm.rx.FindStringSubmatch(lines[0])
   696  	if len(matches) > 2 && len(matches[2]) > 0 {
   697  		max, err := strconv.ParseFloat(matches[2], 64)
   698  		if err != nil {
   699  			return err
   700  		}
   701  		sm.builder.SetMaximum(max, matches[1] == "<")
   702  	}
   703  	return nil
   704  }
   705  
   706  func (sm *setMaximum) Matches(line string) bool {
   707  	return sm.rx.MatchString(line)
   708  }
   709  
   710  type setMinimum struct {
   711  	builder validationBuilder
   712  	rx      *regexp.Regexp
   713  }
   714  
   715  func (sm *setMinimum) Matches(line string) bool {
   716  	return sm.rx.MatchString(line)
   717  }
   718  
   719  func (sm *setMinimum) Parse(lines []string) error {
   720  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   721  		return nil
   722  	}
   723  	matches := sm.rx.FindStringSubmatch(lines[0])
   724  	if len(matches) > 2 && len(matches[2]) > 0 {
   725  		min, err := strconv.ParseFloat(matches[2], 64)
   726  		if err != nil {
   727  			return err
   728  		}
   729  		sm.builder.SetMinimum(min, matches[1] == ">")
   730  	}
   731  	return nil
   732  }
   733  
   734  type setMultipleOf struct {
   735  	builder validationBuilder
   736  	rx      *regexp.Regexp
   737  }
   738  
   739  func (sm *setMultipleOf) Matches(line string) bool {
   740  	return sm.rx.MatchString(line)
   741  }
   742  
   743  func (sm *setMultipleOf) Parse(lines []string) error {
   744  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   745  		return nil
   746  	}
   747  	matches := sm.rx.FindStringSubmatch(lines[0])
   748  	if len(matches) > 2 && len(matches[1]) > 0 {
   749  		multipleOf, err := strconv.ParseFloat(matches[1], 64)
   750  		if err != nil {
   751  			return err
   752  		}
   753  		sm.builder.SetMultipleOf(multipleOf)
   754  	}
   755  	return nil
   756  }
   757  
   758  type setMaxItems struct {
   759  	builder validationBuilder
   760  	rx      *regexp.Regexp
   761  }
   762  
   763  func (sm *setMaxItems) Matches(line string) bool {
   764  	return sm.rx.MatchString(line)
   765  }
   766  
   767  func (sm *setMaxItems) Parse(lines []string) error {
   768  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   769  		return nil
   770  	}
   771  	matches := sm.rx.FindStringSubmatch(lines[0])
   772  	if len(matches) > 1 && len(matches[1]) > 0 {
   773  		maxItems, err := strconv.ParseInt(matches[1], 10, 64)
   774  		if err != nil {
   775  			return err
   776  		}
   777  		sm.builder.SetMaxItems(maxItems)
   778  	}
   779  	return nil
   780  }
   781  
   782  type setMinItems struct {
   783  	builder validationBuilder
   784  	rx      *regexp.Regexp
   785  }
   786  
   787  func (sm *setMinItems) Matches(line string) bool {
   788  	return sm.rx.MatchString(line)
   789  }
   790  
   791  func (sm *setMinItems) Parse(lines []string) error {
   792  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   793  		return nil
   794  	}
   795  	matches := sm.rx.FindStringSubmatch(lines[0])
   796  	if len(matches) > 1 && len(matches[1]) > 0 {
   797  		minItems, err := strconv.ParseInt(matches[1], 10, 64)
   798  		if err != nil {
   799  			return err
   800  		}
   801  		sm.builder.SetMinItems(minItems)
   802  	}
   803  	return nil
   804  }
   805  
   806  type setMaxLength struct {
   807  	builder validationBuilder
   808  	rx      *regexp.Regexp
   809  }
   810  
   811  func (sm *setMaxLength) Parse(lines []string) error {
   812  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   813  		return nil
   814  	}
   815  	matches := sm.rx.FindStringSubmatch(lines[0])
   816  	if len(matches) > 1 && len(matches[1]) > 0 {
   817  		maxLength, err := strconv.ParseInt(matches[1], 10, 64)
   818  		if err != nil {
   819  			return err
   820  		}
   821  		sm.builder.SetMaxLength(maxLength)
   822  	}
   823  	return nil
   824  }
   825  
   826  func (sm *setMaxLength) Matches(line string) bool {
   827  	return sm.rx.MatchString(line)
   828  }
   829  
   830  type setMinLength struct {
   831  	builder validationBuilder
   832  	rx      *regexp.Regexp
   833  }
   834  
   835  func (sm *setMinLength) Parse(lines []string) error {
   836  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   837  		return nil
   838  	}
   839  	matches := sm.rx.FindStringSubmatch(lines[0])
   840  	if len(matches) > 1 && len(matches[1]) > 0 {
   841  		minLength, err := strconv.ParseInt(matches[1], 10, 64)
   842  		if err != nil {
   843  			return err
   844  		}
   845  		sm.builder.SetMinLength(minLength)
   846  	}
   847  	return nil
   848  }
   849  
   850  func (sm *setMinLength) Matches(line string) bool {
   851  	return sm.rx.MatchString(line)
   852  }
   853  
   854  type setPattern struct {
   855  	builder validationBuilder
   856  	rx      *regexp.Regexp
   857  }
   858  
   859  func (sm *setPattern) Parse(lines []string) error {
   860  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   861  		return nil
   862  	}
   863  	matches := sm.rx.FindStringSubmatch(lines[0])
   864  	if len(matches) > 1 && len(matches[1]) > 0 {
   865  		sm.builder.SetPattern(matches[1])
   866  	}
   867  	return nil
   868  }
   869  
   870  func (sm *setPattern) Matches(line string) bool {
   871  	return sm.rx.MatchString(line)
   872  }
   873  
   874  type setCollectionFormat struct {
   875  	builder operationValidationBuilder
   876  	rx      *regexp.Regexp
   877  }
   878  
   879  func (sm *setCollectionFormat) Parse(lines []string) error {
   880  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   881  		return nil
   882  	}
   883  	matches := sm.rx.FindStringSubmatch(lines[0])
   884  	if len(matches) > 1 && len(matches[1]) > 0 {
   885  		sm.builder.SetCollectionFormat(matches[1])
   886  	}
   887  	return nil
   888  }
   889  
   890  func (sm *setCollectionFormat) Matches(line string) bool {
   891  	return sm.rx.MatchString(line)
   892  }
   893  
   894  type setUnique struct {
   895  	builder validationBuilder
   896  	rx      *regexp.Regexp
   897  }
   898  
   899  func (su *setUnique) Matches(line string) bool {
   900  	return su.rx.MatchString(line)
   901  }
   902  
   903  func (su *setUnique) Parse(lines []string) error {
   904  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   905  		return nil
   906  	}
   907  	matches := su.rx.FindStringSubmatch(lines[0])
   908  	if len(matches) > 1 && len(matches[1]) > 0 {
   909  		req, err := strconv.ParseBool(matches[1])
   910  		if err != nil {
   911  			return err
   912  		}
   913  		su.builder.SetUnique(req)
   914  	}
   915  	return nil
   916  }
   917  
   918  type setEnum struct {
   919  	builder validationBuilder
   920  	rx      *regexp.Regexp
   921  }
   922  
   923  func (se *setEnum) Matches(line string) bool {
   924  	return se.rx.MatchString(line)
   925  }
   926  
   927  func (se *setEnum) Parse(lines []string) error {
   928  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   929  		return nil
   930  	}
   931  	matches := se.rx.FindStringSubmatch(lines[0])
   932  	if len(matches) > 1 && len(matches[1]) > 0 {
   933  		se.builder.SetEnum(matches[1])
   934  	}
   935  	return nil
   936  }
   937  
   938  func parseValueFromSchema(s string, schema *spec.SimpleSchema) (interface{}, error) {
   939  	if schema != nil {
   940  		switch strings.Trim(schema.TypeName(), "\"") {
   941  		case "integer", "int", "int64", "int32", "int16":
   942  			return strconv.Atoi(s)
   943  		case "bool", "boolean":
   944  			return strconv.ParseBool(s)
   945  		case "number", "float64", "float32":
   946  			return strconv.ParseFloat(s, 64)
   947  		case "object":
   948  			var obj map[string]interface{}
   949  			if err := json.Unmarshal([]byte(s), &obj); err != nil {
   950  				// If we can't parse it, just return the string.
   951  				return s, nil
   952  			}
   953  			return obj, nil
   954  		case "array":
   955  			var slice []interface{}
   956  			if err := json.Unmarshal([]byte(s), &slice); err != nil {
   957  				// If we can't parse it, just return the string.
   958  				return s, nil
   959  			}
   960  			return slice, nil
   961  		default:
   962  			return s, nil
   963  		}
   964  	} else {
   965  		return s, nil
   966  	}
   967  }
   968  
   969  type setDefault struct {
   970  	scheme  *spec.SimpleSchema
   971  	builder validationBuilder
   972  	rx      *regexp.Regexp
   973  }
   974  
   975  func (sd *setDefault) Matches(line string) bool {
   976  	return sd.rx.MatchString(line)
   977  }
   978  
   979  func (sd *setDefault) Parse(lines []string) error {
   980  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   981  		return nil
   982  	}
   983  	matches := sd.rx.FindStringSubmatch(lines[0])
   984  	if len(matches) > 1 && len(matches[1]) > 0 {
   985  		d, err := parseValueFromSchema(matches[1], sd.scheme)
   986  		if err != nil {
   987  			return err
   988  		}
   989  		sd.builder.SetDefault(d)
   990  	}
   991  	return nil
   992  }
   993  
   994  type setExample struct {
   995  	scheme  *spec.SimpleSchema
   996  	builder validationBuilder
   997  	rx      *regexp.Regexp
   998  }
   999  
  1000  func (se *setExample) Matches(line string) bool {
  1001  	return se.rx.MatchString(line)
  1002  }
  1003  
  1004  func (se *setExample) Parse(lines []string) error {
  1005  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1006  		return nil
  1007  	}
  1008  	matches := se.rx.FindStringSubmatch(lines[0])
  1009  	if len(matches) > 1 && len(matches[1]) > 0 {
  1010  		d, err := parseValueFromSchema(matches[1], se.scheme)
  1011  		if err != nil {
  1012  			return err
  1013  		}
  1014  		se.builder.SetExample(d)
  1015  	}
  1016  	return nil
  1017  }
  1018  
  1019  type matchOnlyParam struct {
  1020  	tgt *spec.Parameter
  1021  	rx  *regexp.Regexp
  1022  }
  1023  
  1024  func (mo *matchOnlyParam) Matches(line string) bool {
  1025  	return mo.rx.MatchString(line)
  1026  }
  1027  
  1028  func (mo *matchOnlyParam) Parse(lines []string) error {
  1029  	return nil
  1030  }
  1031  
  1032  type setRequiredParam struct {
  1033  	tgt *spec.Parameter
  1034  }
  1035  
  1036  func (su *setRequiredParam) Matches(line string) bool {
  1037  	return rxRequired.MatchString(line)
  1038  }
  1039  
  1040  func (su *setRequiredParam) Parse(lines []string) error {
  1041  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1042  		return nil
  1043  	}
  1044  	matches := rxRequired.FindStringSubmatch(lines[0])
  1045  	if len(matches) > 1 && len(matches[1]) > 0 {
  1046  		req, err := strconv.ParseBool(matches[1])
  1047  		if err != nil {
  1048  			return err
  1049  		}
  1050  		su.tgt.Required = req
  1051  	}
  1052  	return nil
  1053  }
  1054  
  1055  type setReadOnlySchema struct {
  1056  	tgt *spec.Schema
  1057  }
  1058  
  1059  func (su *setReadOnlySchema) Matches(line string) bool {
  1060  	return rxReadOnly.MatchString(line)
  1061  }
  1062  
  1063  func (su *setReadOnlySchema) Parse(lines []string) error {
  1064  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1065  		return nil
  1066  	}
  1067  	matches := rxReadOnly.FindStringSubmatch(lines[0])
  1068  	if len(matches) > 1 && len(matches[1]) > 0 {
  1069  		req, err := strconv.ParseBool(matches[1])
  1070  		if err != nil {
  1071  			return err
  1072  		}
  1073  		su.tgt.ReadOnly = req
  1074  	}
  1075  	return nil
  1076  }
  1077  
  1078  type setDeprecatedOp struct {
  1079  	tgt *spec.Operation
  1080  }
  1081  
  1082  func (su *setDeprecatedOp) Matches(line string) bool {
  1083  	return rxDeprecated.MatchString(line)
  1084  }
  1085  
  1086  func (su *setDeprecatedOp) Parse(lines []string) error {
  1087  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1088  		return nil
  1089  	}
  1090  	matches := rxDeprecated.FindStringSubmatch(lines[0])
  1091  	if len(matches) > 1 && len(matches[1]) > 0 {
  1092  		req, err := strconv.ParseBool(matches[1])
  1093  		if err != nil {
  1094  			return err
  1095  		}
  1096  		su.tgt.Deprecated = req
  1097  	}
  1098  	return nil
  1099  }
  1100  
  1101  type setDiscriminator struct {
  1102  	schema *spec.Schema
  1103  	field  string
  1104  }
  1105  
  1106  func (su *setDiscriminator) Matches(line string) bool {
  1107  	return rxDiscriminator.MatchString(line)
  1108  }
  1109  
  1110  func (su *setDiscriminator) Parse(lines []string) error {
  1111  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1112  		return nil
  1113  	}
  1114  	matches := rxDiscriminator.FindStringSubmatch(lines[0])
  1115  	if len(matches) > 1 && len(matches[1]) > 0 {
  1116  		req, err := strconv.ParseBool(matches[1])
  1117  		if err != nil {
  1118  			return err
  1119  		}
  1120  		if req {
  1121  			su.schema.Discriminator = su.field
  1122  		} else if su.schema.Discriminator == su.field {
  1123  			su.schema.Discriminator = ""
  1124  		}
  1125  	}
  1126  	return nil
  1127  }
  1128  
  1129  type setRequiredSchema struct {
  1130  	schema *spec.Schema
  1131  	field  string
  1132  }
  1133  
  1134  func (su *setRequiredSchema) Matches(line string) bool {
  1135  	return rxRequired.MatchString(line)
  1136  }
  1137  
  1138  func (su *setRequiredSchema) Parse(lines []string) error {
  1139  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1140  		return nil
  1141  	}
  1142  	matches := rxRequired.FindStringSubmatch(lines[0])
  1143  	if len(matches) > 1 && len(matches[1]) > 0 {
  1144  		req, err := strconv.ParseBool(matches[1])
  1145  		if err != nil {
  1146  			return err
  1147  		}
  1148  		midx := -1
  1149  		for i, nm := range su.schema.Required {
  1150  			if nm == su.field {
  1151  				midx = i
  1152  				break
  1153  			}
  1154  		}
  1155  		if req {
  1156  			if midx < 0 {
  1157  				su.schema.Required = append(su.schema.Required, su.field)
  1158  			}
  1159  		} else if midx >= 0 {
  1160  			su.schema.Required = append(su.schema.Required[:midx], su.schema.Required[midx+1:]...)
  1161  		}
  1162  	}
  1163  	return nil
  1164  }
  1165  
  1166  func newMultilineDropEmptyParser(rx *regexp.Regexp, set func([]string)) *multiLineDropEmptyParser {
  1167  	return &multiLineDropEmptyParser{
  1168  		rx:  rx,
  1169  		set: set,
  1170  	}
  1171  }
  1172  
  1173  type multiLineDropEmptyParser struct {
  1174  	set func([]string)
  1175  	rx  *regexp.Regexp
  1176  }
  1177  
  1178  func (m *multiLineDropEmptyParser) Matches(line string) bool {
  1179  	return m.rx.MatchString(line)
  1180  }
  1181  
  1182  func (m *multiLineDropEmptyParser) Parse(lines []string) error {
  1183  	m.set(removeEmptyLines(lines))
  1184  	return nil
  1185  }
  1186  
  1187  func newSetSchemes(set func([]string)) *setSchemes {
  1188  	return &setSchemes{
  1189  		set: set,
  1190  		rx:  rxSchemes,
  1191  	}
  1192  }
  1193  
  1194  type setSchemes struct {
  1195  	set func([]string)
  1196  	rx  *regexp.Regexp
  1197  }
  1198  
  1199  func (ss *setSchemes) Matches(line string) bool {
  1200  	return ss.rx.MatchString(line)
  1201  }
  1202  
  1203  func (ss *setSchemes) Parse(lines []string) error {
  1204  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1205  		return nil
  1206  	}
  1207  	matches := ss.rx.FindStringSubmatch(lines[0])
  1208  	if len(matches) > 1 && len(matches[1]) > 0 {
  1209  		sch := strings.Split(matches[1], ", ")
  1210  
  1211  		schemes := []string{}
  1212  		for _, s := range sch {
  1213  			ts := strings.TrimSpace(s)
  1214  			if ts != "" {
  1215  				schemes = append(schemes, ts)
  1216  			}
  1217  		}
  1218  		ss.set(schemes)
  1219  	}
  1220  	return nil
  1221  }
  1222  
  1223  func newSetSecurity(rx *regexp.Regexp, setter func([]map[string][]string)) *setSecurity {
  1224  	return &setSecurity{
  1225  		set: setter,
  1226  		rx:  rx,
  1227  	}
  1228  }
  1229  
  1230  type setSecurity struct {
  1231  	set func([]map[string][]string)
  1232  	rx  *regexp.Regexp
  1233  }
  1234  
  1235  func (ss *setSecurity) Matches(line string) bool {
  1236  	return ss.rx.MatchString(line)
  1237  }
  1238  
  1239  func (ss *setSecurity) Parse(lines []string) error {
  1240  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1241  		return nil
  1242  	}
  1243  
  1244  	var result []map[string][]string
  1245  	for _, line := range lines {
  1246  		kv := strings.SplitN(line, ":", 2)
  1247  		scopes := []string{}
  1248  		var key string
  1249  
  1250  		if len(kv) > 1 {
  1251  			scs := strings.Split(kv[1], ",")
  1252  			for _, scope := range scs {
  1253  				tr := strings.TrimSpace(scope)
  1254  				if tr != "" {
  1255  					tr = strings.SplitAfter(tr, " ")[0]
  1256  					scopes = append(scopes, strings.TrimSpace(tr))
  1257  				}
  1258  			}
  1259  
  1260  			key = strings.TrimSpace(kv[0])
  1261  
  1262  			result = append(result, map[string][]string{key: scopes})
  1263  		}
  1264  	}
  1265  	ss.set(result)
  1266  	return nil
  1267  }
  1268  
  1269  func newSetResponses(definitions map[string]spec.Schema, responses map[string]spec.Response, setter func(*spec.Response, map[int]spec.Response)) *setOpResponses {
  1270  	return &setOpResponses{
  1271  		set:         setter,
  1272  		rx:          rxResponses,
  1273  		definitions: definitions,
  1274  		responses:   responses,
  1275  	}
  1276  }
  1277  
  1278  type setOpResponses struct {
  1279  	set         func(*spec.Response, map[int]spec.Response)
  1280  	rx          *regexp.Regexp
  1281  	definitions map[string]spec.Schema
  1282  	responses   map[string]spec.Response
  1283  }
  1284  
  1285  func (ss *setOpResponses) Matches(line string) bool {
  1286  	return ss.rx.MatchString(line)
  1287  }
  1288  
  1289  // ResponseTag used when specifying a response to point to a defined swagger:response
  1290  const ResponseTag = "response"
  1291  
  1292  // BodyTag used when specifying a response to point to a model/schema
  1293  const BodyTag = "body"
  1294  
  1295  // DescriptionTag used when specifying a response that gives a description of the response
  1296  const DescriptionTag = "description"
  1297  
  1298  func parseTags(line string) (modelOrResponse string, arrays int, isDefinitionRef bool, description string, err error) {
  1299  	tags := strings.Split(line, " ")
  1300  	parsedModelOrResponse := false
  1301  
  1302  	for i, tagAndValue := range tags {
  1303  		tagValList := strings.SplitN(tagAndValue, ":", 2)
  1304  		var tag, value string
  1305  		if len(tagValList) > 1 {
  1306  			tag = tagValList[0]
  1307  			value = tagValList[1]
  1308  		} else {
  1309  			// TODO: Print a warning, and in the long term, do not support not tagged values
  1310  			// Add a default tag if none is supplied
  1311  			if i == 0 {
  1312  				tag = ResponseTag
  1313  			} else {
  1314  				tag = DescriptionTag
  1315  			}
  1316  			value = tagValList[0]
  1317  		}
  1318  
  1319  		foundModelOrResponse := false
  1320  		if !parsedModelOrResponse {
  1321  			if tag == BodyTag {
  1322  				foundModelOrResponse = true
  1323  				isDefinitionRef = true
  1324  			}
  1325  			if tag == ResponseTag {
  1326  				foundModelOrResponse = true
  1327  				isDefinitionRef = false
  1328  			}
  1329  		}
  1330  		if foundModelOrResponse {
  1331  			// Read the model or response tag
  1332  			parsedModelOrResponse = true
  1333  			// Check for nested arrays
  1334  			arrays = 0
  1335  			for strings.HasPrefix(value, "[]") {
  1336  				arrays++
  1337  				value = value[2:]
  1338  			}
  1339  			// What's left over is the model name
  1340  			modelOrResponse = value
  1341  		} else {
  1342  			foundDescription := false
  1343  			if tag == DescriptionTag {
  1344  				foundDescription = true
  1345  			}
  1346  			if foundDescription {
  1347  				// Descriptions are special, they make they read the rest of the line
  1348  				descriptionWords := []string{value}
  1349  				if i < len(tags)-1 {
  1350  					descriptionWords = append(descriptionWords, tags[i+1:]...)
  1351  				}
  1352  				description = strings.Join(descriptionWords, " ")
  1353  				break
  1354  			} else {
  1355  				if tag == ResponseTag || tag == BodyTag || tag == DescriptionTag {
  1356  					err = fmt.Errorf("valid tag %s, but not in a valid position", tag)
  1357  				} else {
  1358  					err = fmt.Errorf("invalid tag: %s", tag)
  1359  				}
  1360  				// return error
  1361  				return
  1362  			}
  1363  		}
  1364  	}
  1365  
  1366  	// TODO: Maybe do, if !parsedModelOrResponse {return some error}
  1367  	return
  1368  }
  1369  
  1370  func (ss *setOpResponses) Parse(lines []string) error {
  1371  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1372  		return nil
  1373  	}
  1374  
  1375  	var def *spec.Response
  1376  	var scr map[int]spec.Response
  1377  
  1378  	for _, line := range lines {
  1379  		kv := strings.SplitN(line, ":", 2)
  1380  		var key, value string
  1381  
  1382  		if len(kv) > 1 {
  1383  			key = strings.TrimSpace(kv[0])
  1384  			if key == "" {
  1385  				// this must be some weird empty line
  1386  				continue
  1387  			}
  1388  			value = strings.TrimSpace(kv[1])
  1389  			if value == "" {
  1390  				var resp spec.Response
  1391  				if strings.EqualFold("default", key) {
  1392  					if def == nil {
  1393  						def = &resp
  1394  					}
  1395  				} else {
  1396  					if sc, err := strconv.Atoi(key); err == nil {
  1397  						if scr == nil {
  1398  							scr = make(map[int]spec.Response)
  1399  						}
  1400  						scr[sc] = resp
  1401  					}
  1402  				}
  1403  				continue
  1404  			}
  1405  			refTarget, arrays, isDefinitionRef, description, err := parseTags(value)
  1406  			if err != nil {
  1407  				return err
  1408  			}
  1409  			// A possible exception for having a definition
  1410  			if _, ok := ss.responses[refTarget]; !ok {
  1411  				if _, ok := ss.definitions[refTarget]; ok {
  1412  					isDefinitionRef = true
  1413  				}
  1414  			}
  1415  
  1416  			var ref spec.Ref
  1417  			if isDefinitionRef {
  1418  				if description == "" {
  1419  					description = refTarget
  1420  				}
  1421  				ref, err = spec.NewRef("#/definitions/" + refTarget)
  1422  			} else {
  1423  				ref, err = spec.NewRef("#/responses/" + refTarget)
  1424  			}
  1425  			if err != nil {
  1426  				return err
  1427  			}
  1428  
  1429  			// description should used on anyway.
  1430  			resp := spec.Response{ResponseProps: spec.ResponseProps{Description: description}}
  1431  
  1432  			if isDefinitionRef {
  1433  				resp.Schema = new(spec.Schema)
  1434  				resp.Description = description
  1435  				if arrays == 0 {
  1436  					resp.Schema.Ref = ref
  1437  				} else {
  1438  					cs := resp.Schema
  1439  					for i := 0; i < arrays; i++ {
  1440  						cs.Typed("array", "")
  1441  						cs.Items = new(spec.SchemaOrArray)
  1442  						cs.Items.Schema = new(spec.Schema)
  1443  						cs = cs.Items.Schema
  1444  					}
  1445  					cs.Ref = ref
  1446  				}
  1447  				// ref. could be empty while use description tag
  1448  			} else if len(refTarget) > 0 {
  1449  				resp.Ref = ref
  1450  			}
  1451  
  1452  			if strings.EqualFold("default", key) {
  1453  				if def == nil {
  1454  					def = &resp
  1455  				}
  1456  			} else {
  1457  				if sc, err := strconv.Atoi(key); err == nil {
  1458  					if scr == nil {
  1459  						scr = make(map[int]spec.Response)
  1460  					}
  1461  					scr[sc] = resp
  1462  				}
  1463  			}
  1464  		}
  1465  	}
  1466  	ss.set(def, scr)
  1467  	return nil
  1468  }
  1469  
  1470  func parseEnum(val string, s *spec.SimpleSchema) []interface{} {
  1471  	list := strings.Split(val, ",")
  1472  	interfaceSlice := make([]interface{}, len(list))
  1473  	for i, d := range list {
  1474  		v, err := parseValueFromSchema(d, s)
  1475  		if err != nil {
  1476  			interfaceSlice[i] = d
  1477  			continue
  1478  		}
  1479  
  1480  		interfaceSlice[i] = v
  1481  	}
  1482  	return interfaceSlice
  1483  }
  1484  
  1485  // AlphaChars used when parsing for Vendor Extensions
  1486  const AlphaChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  1487  
  1488  func newSetExtensions(setter func(*spec.Extensions)) *setOpExtensions {
  1489  	return &setOpExtensions{
  1490  		set: setter,
  1491  		rx:  rxExtensions,
  1492  	}
  1493  }
  1494  
  1495  type setOpExtensions struct {
  1496  	set func(*spec.Extensions)
  1497  	rx  *regexp.Regexp
  1498  }
  1499  
  1500  type extensionObject struct {
  1501  	Extension string
  1502  	Root      interface{}
  1503  }
  1504  
  1505  type extensionParsingStack []interface{}
  1506  
  1507  // Helper function to walk back through extensions until the proper nest level is reached
  1508  func (stack *extensionParsingStack) walkBack(rawLines []string, lineIndex int) {
  1509  	indent := strings.IndexAny(rawLines[lineIndex], AlphaChars)
  1510  	nextIndent := strings.IndexAny(rawLines[lineIndex+1], AlphaChars)
  1511  	if nextIndent < indent {
  1512  		// Pop elements off the stack until we're back where we need to be
  1513  		runbackIndex := 0
  1514  		poppedIndent := 1000
  1515  		for {
  1516  			checkIndent := strings.IndexAny(rawLines[lineIndex-runbackIndex], AlphaChars)
  1517  			if nextIndent == checkIndent {
  1518  				break
  1519  			}
  1520  			if checkIndent < poppedIndent {
  1521  				*stack = (*stack)[:len(*stack)-1]
  1522  				poppedIndent = checkIndent
  1523  			}
  1524  			runbackIndex++
  1525  		}
  1526  	}
  1527  }
  1528  
  1529  // Recursively parses through the given extension lines, building and adding extension objects as it goes.
  1530  // Extensions may be key:value pairs, arrays, or objects.
  1531  func buildExtensionObjects(rawLines []string, cleanLines []string, lineIndex int, extObjs *[]extensionObject, stack *extensionParsingStack) {
  1532  	if lineIndex >= len(rawLines) {
  1533  		if stack != nil {
  1534  			if ext, ok := (*stack)[0].(extensionObject); ok {
  1535  				*extObjs = append(*extObjs, ext)
  1536  			}
  1537  		}
  1538  		return
  1539  	}
  1540  	kv := strings.SplitN(cleanLines[lineIndex], ":", 2)
  1541  	key := strings.TrimSpace(kv[0])
  1542  	if key == "" {
  1543  		// Some odd empty line
  1544  		return
  1545  	}
  1546  
  1547  	nextIsList := false
  1548  	if lineIndex < len(rawLines)-1 {
  1549  		next := strings.SplitAfterN(cleanLines[lineIndex+1], ":", 2)
  1550  		nextIsList = len(next) == 1
  1551  	}
  1552  
  1553  	if len(kv) > 1 {
  1554  		// Should be the start of a map or a key:value pair
  1555  		value := strings.TrimSpace(kv[1])
  1556  
  1557  		if rxAllowedExtensions.MatchString(key) {
  1558  			// New extension started
  1559  			if stack != nil {
  1560  				if ext, ok := (*stack)[0].(extensionObject); ok {
  1561  					*extObjs = append(*extObjs, ext)
  1562  				}
  1563  			}
  1564  
  1565  			if value != "" {
  1566  				ext := extensionObject{
  1567  					Extension: key,
  1568  				}
  1569  				// Extension is simple key:value pair, no stack
  1570  				ext.Root = make(map[string]string)
  1571  				ext.Root.(map[string]string)[key] = value
  1572  				*extObjs = append(*extObjs, ext)
  1573  				buildExtensionObjects(rawLines, cleanLines, lineIndex+1, extObjs, nil)
  1574  			} else {
  1575  				ext := extensionObject{
  1576  					Extension: key,
  1577  				}
  1578  				if nextIsList {
  1579  					// Extension is an array
  1580  					ext.Root = make(map[string]*[]string)
  1581  					rootList := make([]string, 0)
  1582  					ext.Root.(map[string]*[]string)[key] = &rootList
  1583  					stack = &extensionParsingStack{}
  1584  					*stack = append(*stack, ext)
  1585  					*stack = append(*stack, ext.Root.(map[string]*[]string)[key])
  1586  				} else {
  1587  					// Extension is an object
  1588  					ext.Root = make(map[string]interface{})
  1589  					rootMap := make(map[string]interface{})
  1590  					ext.Root.(map[string]interface{})[key] = rootMap
  1591  					stack = &extensionParsingStack{}
  1592  					*stack = append(*stack, ext)
  1593  					*stack = append(*stack, rootMap)
  1594  				}
  1595  				buildExtensionObjects(rawLines, cleanLines, lineIndex+1, extObjs, stack)
  1596  			}
  1597  		} else if stack != nil && len(*stack) != 0 {
  1598  			stackIndex := len(*stack) - 1
  1599  			if value == "" {
  1600  				if nextIsList {
  1601  					// start of new list
  1602  					newList := make([]string, 0)
  1603  					(*stack)[stackIndex].(map[string]interface{})[key] = &newList
  1604  					*stack = append(*stack, &newList)
  1605  				} else {
  1606  					// start of new map
  1607  					newMap := make(map[string]interface{})
  1608  					(*stack)[stackIndex].(map[string]interface{})[key] = newMap
  1609  					*stack = append(*stack, newMap)
  1610  				}
  1611  			} else {
  1612  				// key:value
  1613  				if reflect.TypeOf((*stack)[stackIndex]).Kind() == reflect.Map {
  1614  					(*stack)[stackIndex].(map[string]interface{})[key] = value
  1615  				}
  1616  				if lineIndex < len(rawLines)-1 && !rxAllowedExtensions.MatchString(cleanLines[lineIndex+1]) {
  1617  					stack.walkBack(rawLines, lineIndex)
  1618  				}
  1619  			}
  1620  			buildExtensionObjects(rawLines, cleanLines, lineIndex+1, extObjs, stack)
  1621  		}
  1622  	} else if stack != nil && len(*stack) != 0 {
  1623  		// Should be a list item
  1624  		stackIndex := len(*stack) - 1
  1625  		list := (*stack)[stackIndex].(*[]string)
  1626  		*list = append(*list, key)
  1627  		(*stack)[stackIndex] = list
  1628  		if lineIndex < len(rawLines)-1 && !rxAllowedExtensions.MatchString(cleanLines[lineIndex+1]) {
  1629  			stack.walkBack(rawLines, lineIndex)
  1630  		}
  1631  		buildExtensionObjects(rawLines, cleanLines, lineIndex+1, extObjs, stack)
  1632  	}
  1633  }
  1634  
  1635  func (ss *setOpExtensions) Matches(line string) bool {
  1636  	return ss.rx.MatchString(line)
  1637  }
  1638  
  1639  func (ss *setOpExtensions) Parse(lines []string) error {
  1640  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
  1641  		return nil
  1642  	}
  1643  
  1644  	cleanLines := cleanupScannerLines(lines, rxUncommentHeaders, nil)
  1645  
  1646  	exts := new(spec.VendorExtensible)
  1647  	extList := make([]extensionObject, 0)
  1648  	buildExtensionObjects(lines, cleanLines, 0, &extList, nil)
  1649  
  1650  	// Extensions can be one of the following:
  1651  	// key:value pair
  1652  	// list/array
  1653  	// object
  1654  	for _, ext := range extList {
  1655  		if _, ok := ext.Root.(map[string]string); ok {
  1656  			exts.AddExtension(ext.Extension, ext.Root.(map[string]string)[ext.Extension])
  1657  		} else if _, ok := ext.Root.(map[string]*[]string); ok {
  1658  			exts.AddExtension(ext.Extension, *(ext.Root.(map[string]*[]string)[ext.Extension]))
  1659  		} else if _, ok := ext.Root.(map[string]interface{}); ok {
  1660  			exts.AddExtension(ext.Extension, ext.Root.(map[string]interface{})[ext.Extension])
  1661  		} else {
  1662  			debugLog("Unknown Extension type: %s", fmt.Sprint(reflect.TypeOf(ext.Root)))
  1663  		}
  1664  	}
  1665  
  1666  	ss.set(&exts.Extensions)
  1667  	return nil
  1668  }