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  }