github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/pkg/protoc/language_rule_config.go (about) 1 package protoc 2 3 import ( 4 "fmt" 5 "sort" 6 "strconv" 7 "strings" 8 9 "github.com/bazelbuild/bazel-gazelle/config" 10 ) 11 12 // LanguageRuleConfig carries metadata about a rule and its dependencies. 13 type LanguageRuleConfig struct { 14 // Config is the parent gazelle Config 15 Config *config.Config 16 // Deps is a mapping from label to +/- intent. 17 Deps map[string]bool 18 // Attr is a mapping from string to intent map. 19 Attrs map[string]map[string]bool 20 // Options is a generic key -> value string mapping. Various rule 21 // implementations are free to document/interpret options in an 22 // implementation-dependent manner. TODO: deprecate this and rename to 'Flags' 23 Options map[string]bool 24 // Resolves is a mapping from resolve mapping spec to rewrite. Negative 25 // intent is represented by the empty rewrite value. 26 Resolves []Rewrite 27 // Enabled is a flag that marks language generation as enabled or not 28 Enabled bool 29 // Implementation is the registry identifier for the Rule 30 Implementation string 31 // Impl is the actual implementation 32 Impl LanguageRule 33 // Name is the name of the Rule config 34 Name string 35 // Deps is a mapping from visibility label to +/- intent. 36 Visibility map[string]bool 37 } 38 39 // NewLanguageRuleConfig returns a pointer to a new LanguageRule config with the 40 // 'Enabled' bit set to true. 41 func NewLanguageRuleConfig(config *config.Config, name string) *LanguageRuleConfig { 42 return &LanguageRuleConfig{ 43 Config: config, 44 Name: name, 45 Enabled: true, 46 Attrs: make(map[string]map[string]bool), 47 Deps: make(map[string]bool), 48 Options: make(map[string]bool), 49 Resolves: make([]Rewrite, 0), 50 Visibility: make(map[string]bool), 51 } 52 } 53 54 // GetDeps returns the sorted list of dependencies 55 func (c *LanguageRuleConfig) GetDeps() []string { 56 return ForIntent(c.Deps, true) 57 } 58 59 // GetOptions returns the rule options having positive intent. 60 func (c *LanguageRuleConfig) GetOptions() []string { 61 return ForIntent(c.Options, true) 62 } 63 64 // GetVisibility returns the sorted list of visibility attrs 65 func (c *LanguageRuleConfig) GetVisibility() []string { 66 return ForIntent(c.Visibility, true) 67 } 68 69 // GetAttrNames returns the names of attributes that have configuration. 70 func (c *LanguageRuleConfig) GetAttrNames() []string { 71 names := make([]string, 0) 72 for name := range c.Attrs { 73 names = append(names, name) 74 } 75 sort.Strings(names) 76 return names 77 } 78 79 // GetAttr returns the positive-intent attr values under the given key. 80 func (c *LanguageRuleConfig) GetAttr(name string) []string { 81 return ForIntent(c.Attrs[name], true) 82 } 83 84 // GetRewrites returns a copy of the resolve mappings 85 func (c *LanguageRuleConfig) GetRewrites() []Rewrite { 86 return c.Resolves[:] 87 } 88 89 // clone copies this config to a new one 90 func (c *LanguageRuleConfig) clone() *LanguageRuleConfig { 91 clone := NewLanguageRuleConfig(c.Config, c.Name) 92 clone.Enabled = c.Enabled 93 clone.Implementation = c.Implementation 94 for name, vals := range c.Attrs { 95 clone.Attrs[name] = make(map[string]bool) 96 for k, v := range vals { 97 clone.Attrs[name][k] = v 98 } 99 } 100 for k, v := range c.Deps { 101 clone.Deps[k] = v 102 } 103 for k, v := range c.Options { 104 clone.Options[k] = v 105 } 106 clone.Resolves = c.Resolves[:] 107 for k, v := range c.Visibility { 108 clone.Visibility[k] = v 109 } 110 return clone 111 } 112 113 // parseDirective parses the directive string or returns error. 114 func (c *LanguageRuleConfig) parseDirective(cfg *PackageConfig, d, param, value string) error { 115 intent := parseIntent(param) 116 switch intent.Value { 117 case "dep", "deps": 118 if intent.Want { 119 c.Deps[value] = true 120 } else { 121 delete(c.Deps, value) 122 } 123 case "resolve": 124 rw, err := ParseRewrite(value) 125 if err != nil { 126 return fmt.Errorf("invalid resolve rewrite %s: %w", value, err) 127 } 128 c.Resolves = append(c.Resolves, *rw) 129 case "option": 130 if intent.Want { 131 c.Options[value] = true 132 } else { 133 delete(c.Options, value) 134 } 135 case "attr": 136 kv := strings.Fields(value) 137 if len(kv) == 0 { 138 return fmt.Errorf("malformed attr (missing attr name and value) %q: expected form is 'gazelle:proto_rule {RULE_NAME} attr {ATTR_NAME} [+/-]{VALUE}'", value) 139 } 140 key := parseIntent(kv[0]) 141 142 if len(kv) == 1 { 143 if intent.Want { 144 return fmt.Errorf("malformed attr %q (missing named attr value): expected form is 'gazelle:proto_rule {RULE_NAME} attr {ATTR_NAME} [+/-]{VALUE}'", value) 145 } else { 146 delete(c.Attrs, key.Value) 147 return nil 148 } 149 } 150 151 val := strings.Join(kv[1:], " ") 152 153 if intent.Want { 154 values, ok := c.Attrs[key.Value] 155 if !ok { 156 values = make(map[string]bool) 157 c.Attrs[key.Value] = values 158 } 159 values[val] = key.Want 160 } else { 161 delete(c.Attrs, key.Value) 162 } 163 case "visibility": 164 c.Visibility[value] = intent.Want 165 case "implementation": 166 c.Implementation = value 167 case "enabled": 168 enabled, err := strconv.ParseBool(value) 169 if err != nil { 170 return fmt.Errorf("enabled %s: %w", value, err) 171 } 172 c.Enabled = enabled 173 default: 174 return fmt.Errorf("unknown parameter %q", intent.Value) 175 } 176 177 return nil 178 } 179 180 func (c *LanguageRuleConfig) addRewrite(r Rewrite) { 181 c.Resolves = append([]Rewrite{r}, c.Resolves...) 182 } 183 184 // fromYAML loads configuration from the yaml rule confug. 185 func (c *LanguageRuleConfig) fromYAML(y *YRule) error { 186 if c.Name != y.Name { 187 return fmt.Errorf("yaml rule mismatch: want %q got %q", c.Name, y.Name) 188 } 189 c.Implementation = y.Implementation 190 for _, dep := range y.Deps { 191 c.Deps[dep] = true 192 } 193 for _, opt := range y.Option { 194 c.Options[opt] = true 195 } 196 for _, resolve := range y.Resolves { 197 rw, err := ParseRewrite(resolve) 198 if err != nil { 199 return fmt.Errorf("invalid resolve rewrite %s: %w", resolve, err) 200 } 201 c.addRewrite(*rw) 202 } 203 for _, v := range y.Visibility { 204 c.Visibility[v] = true 205 } 206 if y.Enabled != nil { 207 c.Enabled = *y.Enabled 208 } 209 return nil 210 }