github.com/Diggs/controller-tools@v0.4.2/pkg/genall/options.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package genall 18 19 import ( 20 "fmt" 21 "strings" 22 23 "github.com/Diggs/controller-tools/pkg/markers" 24 ) 25 26 var ( 27 InputPathsMarker = markers.Must(markers.MakeDefinition("paths", markers.DescribesPackage, InputPaths(nil))) 28 ) 29 30 // +controllertools:marker:generateHelp:category="" 31 32 // InputPaths represents paths and go-style path patterns to use as package roots. 33 type InputPaths []string 34 35 // RegisterOptionsMarkers registers "mandatory" options markers for FromOptions into the given registry. 36 // At this point, that's just InputPaths. 37 func RegisterOptionsMarkers(into *markers.Registry) error { 38 if err := into.Register(InputPathsMarker); err != nil { 39 return err 40 } 41 // NB(directxman12): we make this optional so we don't have a bootstrap problem with helpgen 42 if helpGiver, hasHelp := ((interface{})(InputPaths(nil))).(HasHelp); hasHelp { 43 into.AddHelp(InputPathsMarker, helpGiver.Help()) 44 } 45 return nil 46 } 47 48 // RegistryFromOptions produces just the marker registry that would be used by FromOptions, without 49 // attempting to produce a full Runtime. This can be useful if you want to display help without 50 // trying to load roots. 51 func RegistryFromOptions(optionsRegistry *markers.Registry, options []string) (*markers.Registry, error) { 52 protoRt, err := protoFromOptions(optionsRegistry, options) 53 if err != nil { 54 return nil, err 55 } 56 reg := &markers.Registry{} 57 if err := protoRt.Generators.RegisterMarkers(reg); err != nil { 58 return nil, err 59 } 60 return reg, nil 61 } 62 63 // FromOptions parses the options from markers stored in the given registry out into a runtime. 64 // The markers in the registry must be either 65 // 66 // a) Generators 67 // b) OutputRules 68 // c) InputPaths 69 // 70 // The paths specified in InputPaths are loaded as package roots, and the combined with 71 // the generators and the specified output rules to produce a runtime that can be run or 72 // further modified. Not default generators are used if none are specified -- you can check 73 // the output and rerun for that. 74 func FromOptions(optionsRegistry *markers.Registry, options []string) (*Runtime, error) { 75 76 protoRt, err := protoFromOptions(optionsRegistry, options) 77 if err != nil { 78 return nil, err 79 } 80 81 // make the runtime 82 genRuntime, err := protoRt.Generators.ForRoots(protoRt.Paths...) 83 if err != nil { 84 return nil, err 85 } 86 87 // attempt to figure out what the user wants without a lot of verbose specificity: 88 // if the user specifies a default rule, assume that they probably want to fall back 89 // to that. Otherwise, assume that they just wanted to customize one option from the 90 // set, and leave the rest in the standard configuration. 91 if protoRt.OutputRules.Default != nil { 92 genRuntime.OutputRules = protoRt.OutputRules 93 return genRuntime, nil 94 } 95 96 outRules := DirectoryPerGenerator("config", protoRt.GeneratorsByName) 97 for gen, rule := range protoRt.OutputRules.ByGenerator { 98 outRules.ByGenerator[gen] = rule 99 } 100 101 genRuntime.OutputRules = outRules 102 return genRuntime, nil 103 } 104 105 // protoFromOptions returns a proto-Runtime from the given options registry and 106 // options set. This can then be used to construct an actual Runtime. See the 107 // FromOptions function for more details about how the options work. 108 func protoFromOptions(optionsRegistry *markers.Registry, options []string) (protoRuntime, error) { 109 var gens Generators 110 rules := OutputRules{ 111 ByGenerator: make(map[*Generator]OutputRule), 112 } 113 var paths []string 114 115 // collect the generators first, so that we can key the output on the actual 116 // generator, which matters if there's settings in the gen object and it's not a pointer. 117 outputByGen := make(map[string]OutputRule) 118 gensByName := make(map[string]*Generator) 119 120 for _, rawOpt := range options { 121 if rawOpt[0] != '+' { 122 rawOpt = "+" + rawOpt // add a `+` to make it acceptable for usage with the registry 123 } 124 defn := optionsRegistry.Lookup(rawOpt, markers.DescribesPackage) 125 if defn == nil { 126 return protoRuntime{}, fmt.Errorf("unknown option %q", rawOpt[1:]) 127 } 128 129 val, err := defn.Parse(rawOpt) 130 if err != nil { 131 return protoRuntime{}, fmt.Errorf("unable to parse option %q: %w", rawOpt[1:], err) 132 } 133 134 switch val := val.(type) { 135 case Generator: 136 gens = append(gens, &val) 137 gensByName[defn.Name] = &val 138 case OutputRule: 139 _, genName := splitOutputRuleOption(defn.Name) 140 if genName == "" { 141 // it's a default rule 142 rules.Default = val 143 continue 144 } 145 146 outputByGen[genName] = val 147 continue 148 case InputPaths: 149 paths = append(paths, val...) 150 default: 151 return protoRuntime{}, fmt.Errorf("unknown option marker %q", defn.Name) 152 } 153 } 154 155 // actually associate the rules now that we know the generators 156 for genName, outputRule := range outputByGen { 157 gen, knownGen := gensByName[genName] 158 if !knownGen { 159 return protoRuntime{}, fmt.Errorf("non-invoked generator %q", genName) 160 } 161 162 rules.ByGenerator[gen] = outputRule 163 } 164 165 return protoRuntime{ 166 Paths: paths, 167 Generators: Generators(gens), 168 OutputRules: rules, 169 GeneratorsByName: gensByName, 170 }, nil 171 } 172 173 // protoRuntime represents the raw pieces needed to compose a runtime, as 174 // parsed from some options. 175 type protoRuntime struct { 176 Paths []string 177 Generators Generators 178 OutputRules OutputRules 179 GeneratorsByName map[string]*Generator 180 } 181 182 // splitOutputRuleOption splits a marker name of "output:rule:gen" or "output:rule" 183 // into its compontent rule and generator name. 184 func splitOutputRuleOption(name string) (ruleName string, genName string) { 185 parts := strings.SplitN(name, ":", 3) 186 if len(parts) == 3 { 187 // output:<generator>:<rule> 188 return parts[2], parts[1] 189 } 190 // output:<rule> 191 return parts[1], "" 192 }