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 }