github.com/terraform-linters/tflint-ruleset-azurerm@v0.26.0/rules/generator/main.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "go/format" 7 "io/ioutil" 8 "os" 9 "regexp" 10 "strings" 11 "text/template" 12 13 "github.com/dave/dst" 14 "github.com/dave/dst/decorator" 15 ) 16 17 type metadata struct { 18 RuleName string 19 RuleNameCC string 20 } 21 22 func main() { 23 buf := bufio.NewReader(os.Stdin) 24 fmt.Print("Rule name? (e.g. azurerm_linux_virtual_machine_invalid_size): ") 25 ruleName, err := buf.ReadString('\n') 26 if err != nil { 27 panic(err) 28 } 29 ruleName = strings.Trim(ruleName, "\n") 30 31 meta := &metadata{RuleNameCC: toCamelCase(ruleName), RuleName: ruleName} 32 33 GenerateFileWithLogs(fmt.Sprintf("rules/%s.go", ruleName), "rules/rule.go.tmpl", meta) 34 GenerateFileWithLogs(fmt.Sprintf("rules/%s_test.go", ruleName), "rules/rule_test.go.tmpl", meta) 35 GenerateFileWithLogs(fmt.Sprintf("docs/rules/%s.md", ruleName), "docs/rules/rule.md.tmpl", meta) 36 37 src, err := ioutil.ReadFile("rules/provider.go") 38 if err != nil { 39 panic(err) 40 } 41 dstf, err := decorator.Parse(src) 42 if err != nil { 43 panic(err) 44 } 45 46 dst.Inspect(dstf, func(n dst.Node) bool { 47 switch node := n.(type) { 48 case *dst.CompositeLit: 49 expr := &dst.CallExpr{ 50 Fun: &dst.Ident{ 51 Name: fmt.Sprintf("New%sRule", meta.RuleNameCC), 52 }, 53 } 54 expr.Decs.Before = dst.NewLine 55 expr.Decs.After = dst.NewLine 56 node.Elts = append(node.Elts, expr) 57 } 58 return true 59 }) 60 61 fset, astf, err := decorator.RestoreFile(dstf) 62 if err != nil { 63 panic(err) 64 } 65 66 fp, err := os.OpenFile("rules/provider.go", os.O_RDWR, 0755) 67 if err != nil { 68 panic(err) 69 } 70 if err := format.Node(fp, fset, astf); err != nil { 71 panic(err) 72 } 73 fmt.Println("Modified: rules/provider.go") 74 75 fmt.Println(` 76 TODO: 77 1. Remove all "TODO" comments from generated files. 78 2. Write implementation of the rule. 79 3. Add a link to the generated documentation into docs/rules/README.md`) 80 } 81 82 // GenerateFile generates a new file from the passed template and metadata 83 func GenerateFile(fileName string, tmplName string, meta interface{}) { 84 file, err := os.Create(fileName) 85 if err != nil { 86 panic(err) 87 } 88 89 tmpl := template.Must(template.ParseFiles(tmplName)) 90 err = tmpl.Execute(file, meta) 91 if err != nil { 92 panic(err) 93 } 94 } 95 96 // GenerateFileWithLogs generates a new file from the passed template and metadata 97 // The difference from GenerateFile function is to output logs 98 func GenerateFileWithLogs(fileName string, tmplName string, meta interface{}) { 99 GenerateFile(fileName, tmplName, meta) 100 fmt.Printf("Created: %s\n", fileName) 101 } 102 103 var heading = regexp.MustCompile("(^[A-Za-z])|_([A-Za-z])") 104 105 func toCamelCase(str string) string { 106 exceptions := map[string]string{ 107 "ip": "IP", 108 "sql": "SQL", 109 "vm": "VM", 110 "os": "OS", 111 "id": "ID", 112 "tls": "TLS", 113 } 114 parts := strings.Split(str, "_") 115 replaced := make([]string, len(parts)) 116 for i, s := range parts { 117 conv, ok := exceptions[s] 118 if ok { 119 replaced[i] = conv 120 } else { 121 replaced[i] = s 122 } 123 } 124 str = strings.Join(replaced, "_") 125 126 return heading.ReplaceAllStringFunc(str, func(s string) string { 127 return strings.ToUpper(strings.Replace(s, "_", "", -1)) 128 }) 129 }