github.com/alex123012/deckhouse-controller-tools@v0.0.0-20230510090815-d594daf1af8c/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 "sigs.k8s.io/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 // 34 // Multiple paths can be specified using "{path1, path2, path3}". 35 type InputPaths []string 36 37 // RegisterOptionsMarkers registers "mandatory" options markers for FromOptions into the given registry. 38 // At this point, that's just InputPaths. 39 func RegisterOptionsMarkers(into *markers.Registry) error { 40 if err := into.Register(InputPathsMarker); err != nil { 41 return err 42 } 43 // NB(directxman12): we make this optional so we don't have a bootstrap problem with helpgen 44 if helpGiver, hasHelp := ((interface{})(InputPaths(nil))).(HasHelp); hasHelp { 45 into.AddHelp(InputPathsMarker, helpGiver.Help()) 46 } 47 return nil 48 } 49 50 // RegistryFromOptions produces just the marker registry that would be used by FromOptions, without 51 // attempting to produce a full Runtime. This can be useful if you want to display help without 52 // trying to load roots. 53 func RegistryFromOptions(optionsRegistry *markers.Registry, options []string) (*markers.Registry, error) { 54 protoRt, err := protoFromOptions(optionsRegistry, options) 55 if err != nil { 56 return nil, err 57 } 58 reg := &markers.Registry{} 59 if err := protoRt.Generators.RegisterMarkers(reg); err != nil { 60 return nil, err 61 } 62 return reg, nil 63 } 64 65 // FromOptions parses the options from markers stored in the given registry out into a runtime. 66 // The markers in the registry must be either 67 // 68 // a) Generators 69 // b) OutputRules 70 // c) InputPaths 71 // 72 // The paths specified in InputPaths are loaded as package roots, and the combined with 73 // the generators and the specified output rules to produce a runtime that can be run or 74 // further modified. Not default generators are used if none are specified -- you can check 75 // the output and rerun for that. 76 func FromOptions(optionsRegistry *markers.Registry, options []string) (*Runtime, error) { 77 78 protoRt, err := protoFromOptions(optionsRegistry, options) 79 if err != nil { 80 return nil, err 81 } 82 83 // make the runtime 84 genRuntime, err := protoRt.Generators.ForRoots(protoRt.Paths...) 85 if err != nil { 86 return nil, err 87 } 88 89 // attempt to figure out what the user wants without a lot of verbose specificity: 90 // if the user specifies a default rule, assume that they probably want to fall back 91 // to that. Otherwise, assume that they just wanted to customize one option from the 92 // set, and leave the rest in the standard configuration. 93 if protoRt.OutputRules.Default != nil { 94 genRuntime.OutputRules = protoRt.OutputRules 95 return genRuntime, nil 96 } 97 98 outRules := DirectoryPerGenerator("config", protoRt.GeneratorsByName) 99 for gen, rule := range protoRt.OutputRules.ByGenerator { 100 outRules.ByGenerator[gen] = rule 101 } 102 103 genRuntime.OutputRules = outRules 104 return genRuntime, nil 105 } 106 107 // protoFromOptions returns a proto-Runtime from the given options registry and 108 // options set. This can then be used to construct an actual Runtime. See the 109 // FromOptions function for more details about how the options work. 110 func protoFromOptions(optionsRegistry *markers.Registry, options []string) (protoRuntime, error) { 111 var gens Generators 112 rules := OutputRules{ 113 ByGenerator: make(map[*Generator]OutputRule), 114 } 115 var paths []string 116 117 // collect the generators first, so that we can key the output on the actual 118 // generator, which matters if there's settings in the gen object and it's not a pointer. 119 outputByGen := make(map[string]OutputRule) 120 gensByName := make(map[string]*Generator) 121 122 for _, rawOpt := range options { 123 if rawOpt[0] != '+' { 124 rawOpt = "+" + rawOpt // add a `+` to make it acceptable for usage with the registry 125 } 126 defn := optionsRegistry.Lookup(rawOpt, markers.DescribesPackage) 127 if defn == nil { 128 return protoRuntime{}, fmt.Errorf("unknown option %q", rawOpt[1:]) 129 } 130 131 val, err := defn.Parse(rawOpt) 132 if err != nil { 133 return protoRuntime{}, fmt.Errorf("unable to parse option %q: %w", rawOpt[1:], err) 134 } 135 136 switch val := val.(type) { 137 case Generator: 138 gens = append(gens, &val) 139 gensByName[defn.Name] = &val 140 case OutputRule: 141 _, genName := splitOutputRuleOption(defn.Name) 142 if genName == "" { 143 // it's a default rule 144 rules.Default = val 145 continue 146 } 147 148 outputByGen[genName] = val 149 continue 150 case InputPaths: 151 paths = append(paths, val...) 152 default: 153 return protoRuntime{}, fmt.Errorf("unknown option marker %q", defn.Name) 154 } 155 } 156 157 // actually associate the rules now that we know the generators 158 for genName, outputRule := range outputByGen { 159 gen, knownGen := gensByName[genName] 160 if !knownGen { 161 return protoRuntime{}, fmt.Errorf("non-invoked generator %q", genName) 162 } 163 164 rules.ByGenerator[gen] = outputRule 165 } 166 167 return protoRuntime{ 168 Paths: paths, 169 Generators: Generators(gens), 170 OutputRules: rules, 171 GeneratorsByName: gensByName, 172 }, nil 173 } 174 175 // protoRuntime represents the raw pieces needed to compose a runtime, as 176 // parsed from some options. 177 type protoRuntime struct { 178 Paths []string 179 Generators Generators 180 OutputRules OutputRules 181 GeneratorsByName map[string]*Generator 182 } 183 184 // splitOutputRuleOption splits a marker name of "output:rule:gen" or "output:rule" 185 // into its compontent rule and generator name. 186 func splitOutputRuleOption(name string) (ruleName string, genName string) { 187 parts := strings.SplitN(name, ":", 3) 188 if len(parts) == 3 { 189 // output:<generator>:<rule> 190 return parts[2], parts[1] 191 } 192 // output:<rule> 193 return parts[1], "" 194 }