github.com/mattn/gom@v0.0.0-20190726063113-0ebf2b5d812d/gomfile.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"regexp"
     9  	"runtime"
    10  	"sort"
    11  	"strings"
    12  )
    13  
    14  var qx = `'[^']*'|"[^"]*"`
    15  var kx = `:[a-z][a-z0-9_]*`
    16  var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)`
    17  var re_group = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`)
    18  var re_end = regexp.MustCompile(`\s*end\s*$`)
    19  var re_gom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`)
    20  var re_options = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`)
    21  
    22  func unquote(name string) string {
    23  	name = strings.TrimSpace(name)
    24  	if len(name) > 2 {
    25  		if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') {
    26  			return name[1 : len(name)-1]
    27  		}
    28  	}
    29  	return name
    30  }
    31  
    32  func matchOS(any interface{}) bool {
    33  	var envs []string
    34  	if as, ok := any.([]string); ok {
    35  		envs = as
    36  	} else if s, ok := any.(string); ok {
    37  		envs = []string{s}
    38  	} else {
    39  		return false
    40  	}
    41  
    42  	if has(envs, runtime.GOOS) {
    43  		return true
    44  	}
    45  	return false
    46  }
    47  func matchEnv(any interface{}) bool {
    48  	var envs []string
    49  	if as, ok := any.([]string); ok {
    50  		envs = as
    51  	} else if s, ok := any.(string); ok {
    52  		envs = []string{s}
    53  	} else {
    54  		return false
    55  	}
    56  
    57  	switch {
    58  	case has(envs, "production") && *productionEnv:
    59  		return true
    60  	case has(envs, "development") && *developmentEnv:
    61  		return true
    62  	case has(envs, "test") && *testEnv:
    63  		return true
    64  	}
    65  
    66  	for _, g := range customGroupList {
    67  		if has(envs, g) {
    68  			return true
    69  		}
    70  	}
    71  
    72  	return false
    73  }
    74  
    75  func parseOptions(line string, options map[string]interface{}) {
    76  	ss := re_options.FindAllStringSubmatch(line, -1)
    77  	re_a := regexp.MustCompile(ax)
    78  	for _, s := range ss {
    79  		kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2)
    80  		kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1])
    81  		if kvs[1][0] == '[' {
    82  			as := re_a.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1)
    83  			a := []string{}
    84  			for i := range as {
    85  				it := strings.TrimSpace(as[i][0])
    86  				if strings.HasPrefix(it, ",") {
    87  					it = strings.TrimSpace(it[1:])
    88  				}
    89  				if strings.HasPrefix(it, ":") {
    90  					it = strings.TrimSpace(it[1:])
    91  				}
    92  				a = append(a, it)
    93  			}
    94  			options[kvs[0][1:]] = a
    95  		} else {
    96  			options[kvs[0][1:]] = unquote(kvs[1])
    97  		}
    98  	}
    99  }
   100  
   101  type Gom struct {
   102  	name    string
   103  	options map[string]interface{}
   104  }
   105  
   106  func parseGomfile(filename string) ([]Gom, error) {
   107  	f, err := os.Open(filename + ".lock")
   108  	if err != nil {
   109  		f, err = os.Open(filename)
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  	}
   114  	br := bufio.NewReader(f)
   115  
   116  	goms := make([]Gom, 0)
   117  
   118  	n := 0
   119  	skip := 0
   120  	valid := true
   121  	var envs []string
   122  	for {
   123  		n++
   124  		lb, _, err := br.ReadLine()
   125  		if err != nil {
   126  			if err == io.EOF {
   127  				return goms, nil
   128  			}
   129  			return nil, err
   130  		}
   131  		line := strings.TrimSpace(string(lb))
   132  		if line == "" || strings.HasPrefix(line, "#") {
   133  			continue
   134  		}
   135  
   136  		name := ""
   137  		options := make(map[string]interface{})
   138  		var items []string
   139  		if re_group.MatchString(line) {
   140  			envs = strings.Split(re_group.FindStringSubmatch(line)[1], ",")
   141  			for i := range envs {
   142  				envs[i] = strings.TrimSpace(envs[i])[1:]
   143  			}
   144  			if matchEnv(envs) {
   145  				valid = true
   146  				continue
   147  			}
   148  			valid = false
   149  			skip++
   150  			continue
   151  		} else if re_end.MatchString(line) {
   152  			if !valid {
   153  				skip--
   154  				if skip < 0 {
   155  					return nil, fmt.Errorf("Syntax Error at line %d", n)
   156  				}
   157  			}
   158  			valid = false
   159  			envs = nil
   160  			continue
   161  		} else if skip > 0 {
   162  			continue
   163  		} else if re_gom.MatchString(line) {
   164  			items = re_gom.FindStringSubmatch(line)[1:]
   165  			name = unquote(items[0])
   166  			parseOptions(items[1], options)
   167  		} else {
   168  			return nil, fmt.Errorf("Syntax Error at line %d", n)
   169  		}
   170  		if envs != nil {
   171  			options["group"] = envs
   172  		}
   173  		goms = append(goms, Gom{name, options})
   174  	}
   175  	return goms, nil
   176  }
   177  
   178  func keys(m map[string]interface{}) []string {
   179  	ks := []string{}
   180  	for k := range m {
   181  		ks = append(ks, k)
   182  	}
   183  	sort.Strings(ks)
   184  	return ks
   185  }
   186  
   187  func writeGomfile(filename string, goms []Gom) error {
   188  	f, err := os.Create(filename)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	defer f.Close()
   193  	envn := map[string]interface{}{"": true}
   194  	for _, gom := range goms {
   195  		if e, ok := gom.options["group"]; ok {
   196  			switch kv := e.(type) {
   197  			case string:
   198  				envn[kv] = true
   199  			case []string:
   200  				for _, kk := range kv {
   201  					envn[kk] = true
   202  				}
   203  			}
   204  		}
   205  	}
   206  	for _, env := range keys(envn) {
   207  		indent := ""
   208  		if env != "" {
   209  			fmt.Fprintf(f, "\n")
   210  			fmt.Fprintf(f, "group :%s do\n", env)
   211  			indent = "  "
   212  		}
   213  		for _, gom := range goms {
   214  			if e, ok := gom.options["group"]; ok {
   215  				found := false
   216  				switch kv := e.(type) {
   217  				case string:
   218  					if kv == env {
   219  						found = true
   220  					}
   221  				case []string:
   222  					for _, kk := range kv {
   223  						if kk == env {
   224  							found = true
   225  							break
   226  						}
   227  					}
   228  				}
   229  				if !found {
   230  					continue
   231  				}
   232  			} else if env != "" {
   233  				continue
   234  			}
   235  			fmt.Fprintf(f, indent+"gom '%s'", gom.name)
   236  			for _, key := range keys(gom.options) {
   237  				if key == "group" {
   238  					continue
   239  				}
   240  				v := gom.options[key]
   241  				switch kv := v.(type) {
   242  				case string:
   243  					fmt.Fprintf(f, ", :%s => '%s'", key, kv)
   244  				case []string:
   245  					ks := ""
   246  					for _, vv := range kv {
   247  						if ks == "" {
   248  							ks = "["
   249  						} else {
   250  							ks += ", "
   251  						}
   252  						ks += ":" + vv
   253  					}
   254  					ks += "]"
   255  					fmt.Fprintf(f, ", :%s => %s", key, ks)
   256  				}
   257  			}
   258  			fmt.Fprintf(f, "\n")
   259  		}
   260  		if env != "" {
   261  			fmt.Fprintf(f, "end\n")
   262  		}
   263  	}
   264  	return nil
   265  }