config.go raw

   1  package lintcmd
   2  
   3  import (
   4  	"bufio"
   5  	"errors"
   6  	"fmt"
   7  	"io"
   8  	"strings"
   9  	"unicode"
  10  )
  11  
  12  type parseBuildConfigError struct {
  13  	line int
  14  	err  error
  15  }
  16  
  17  func (err parseBuildConfigError) Error() string { return err.err.Error() }
  18  
  19  func parseBuildConfigs(r io.Reader) ([]buildConfig, error) {
  20  	var builds []buildConfig
  21  	br := bufio.NewReader(r)
  22  	i := 0
  23  	for {
  24  		line, err := br.ReadString('\n')
  25  		if err != nil {
  26  			if err == io.EOF {
  27  				break
  28  			} else {
  29  				return nil, err
  30  			}
  31  		}
  32  		line = strings.TrimSpace(line)
  33  		if line == "" {
  34  			continue
  35  		}
  36  		name, envs, flags, err := parseBuildConfig(line)
  37  		if err != nil {
  38  			return nil, parseBuildConfigError{line: i + 1, err: err}
  39  		}
  40  
  41  		bc := buildConfig{
  42  			Name:  name,
  43  			Envs:  envs,
  44  			Flags: flags,
  45  		}
  46  		builds = append(builds, bc)
  47  
  48  		i++
  49  	}
  50  	return builds, nil
  51  }
  52  
  53  func parseBuildConfig(line string) (name string, envs []string, flags []string, err error) {
  54  	if line == "" {
  55  		return "", nil, nil, errors.New("couldn't parse empty build config")
  56  	}
  57  	if strings.Index(line, ":") == len(line)-1 {
  58  		name = line[:len(line)-1]
  59  	} else {
  60  		idx := strings.Index(line, ": ")
  61  		if idx == -1 {
  62  			return name, envs, flags, errors.New("missing build name")
  63  		}
  64  		name = line[:idx]
  65  
  66  		var buf []rune
  67  		var inQuote bool
  68  		args := &envs
  69  		for _, r := range strings.TrimSpace(line[idx+2:]) {
  70  			switch r {
  71  			case ' ':
  72  				if inQuote {
  73  					buf = append(buf, r)
  74  				} else if len(buf) != 0 {
  75  					if buf[0] == '-' {
  76  						args = &flags
  77  					}
  78  					*args = append(*args, string(buf))
  79  					buf = buf[:0]
  80  				}
  81  			case '"':
  82  				inQuote = !inQuote
  83  			default:
  84  				buf = append(buf, r)
  85  			}
  86  		}
  87  
  88  		if len(buf) > 0 {
  89  			if inQuote {
  90  				return "", nil, nil, errors.New("unterminated quoted string")
  91  			}
  92  			if buf[0] == '-' {
  93  				args = &flags
  94  			}
  95  			*args = append(*args, string(buf))
  96  		}
  97  	}
  98  
  99  	for _, r := range name {
 100  		if !(r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r)) {
 101  			return "", nil, nil, fmt.Errorf("invalid build name %q", name)
 102  		}
 103  	}
 104  	return name, envs, flags, nil
 105  }
 106