github.com/jhump/protoreflect@v1.16.0/desc/protoparse/ast/options.go (about)

     1  package ast
     2  
     3  import "fmt"
     4  
     5  // OptionDeclNode is a placeholder interface for AST nodes that represent
     6  // options. This allows NoSourceNode to be used in place of *OptionNode
     7  // for some usages.
     8  type OptionDeclNode interface {
     9  	Node
    10  	GetName() Node
    11  	GetValue() ValueNode
    12  }
    13  
    14  var _ OptionDeclNode = (*OptionNode)(nil)
    15  var _ OptionDeclNode = NoSourceNode{}
    16  
    17  // OptionNode represents the declaration of a single option for an element.
    18  // It is used both for normal option declarations (start with "option" keyword
    19  // and end with semicolon) and for compact options found in fields, enum values,
    20  // and extension ranges. Example:
    21  //
    22  //	option (custom.option) = "foo";
    23  type OptionNode struct {
    24  	compositeNode
    25  	Keyword   *KeywordNode // absent for compact options
    26  	Name      *OptionNameNode
    27  	Equals    *RuneNode
    28  	Val       ValueNode
    29  	Semicolon *RuneNode // absent for compact options
    30  }
    31  
    32  func (e *OptionNode) fileElement()    {}
    33  func (e *OptionNode) msgElement()     {}
    34  func (e *OptionNode) oneOfElement()   {}
    35  func (e *OptionNode) enumElement()    {}
    36  func (e *OptionNode) serviceElement() {}
    37  func (e *OptionNode) methodElement()  {}
    38  
    39  // NewOptionNode creates a new *OptionNode for a full option declaration (as
    40  // used in files, messages, oneofs, enums, services, and methods). All arguments
    41  // must be non-nil. (Also see NewCompactOptionNode.)
    42  //   - keyword: The token corresponding to the "option" keyword.
    43  //   - name: The token corresponding to the name of the option.
    44  //   - equals: The token corresponding to the "=" rune after the name.
    45  //   - val: The token corresponding to the option value.
    46  //   - semicolon: The token corresponding to the ";" rune that ends the declaration.
    47  func NewOptionNode(keyword *KeywordNode, name *OptionNameNode, equals *RuneNode, val ValueNode, semicolon *RuneNode) *OptionNode {
    48  	if keyword == nil {
    49  		panic("keyword is nil")
    50  	}
    51  	if name == nil {
    52  		panic("name is nil")
    53  	}
    54  	if equals == nil {
    55  		panic("equals is nil")
    56  	}
    57  	if val == nil {
    58  		panic("val is nil")
    59  	}
    60  	if semicolon == nil {
    61  		panic("semicolon is nil")
    62  	}
    63  	children := []Node{keyword, name, equals, val, semicolon}
    64  	return &OptionNode{
    65  		compositeNode: compositeNode{
    66  			children: children,
    67  		},
    68  		Keyword:   keyword,
    69  		Name:      name,
    70  		Equals:    equals,
    71  		Val:       val,
    72  		Semicolon: semicolon,
    73  	}
    74  }
    75  
    76  // NewCompactOptionNode creates a new *OptionNode for a full compact declaration
    77  // (as used in fields, enum values, and extension ranges). All arguments must be
    78  // non-nil.
    79  //   - name: The token corresponding to the name of the option.
    80  //   - equals: The token corresponding to the "=" rune after the name.
    81  //   - val: The token corresponding to the option value.
    82  func NewCompactOptionNode(name *OptionNameNode, equals *RuneNode, val ValueNode) *OptionNode {
    83  	if name == nil {
    84  		panic("name is nil")
    85  	}
    86  	if equals == nil {
    87  		panic("equals is nil")
    88  	}
    89  	if val == nil {
    90  		panic("val is nil")
    91  	}
    92  	children := []Node{name, equals, val}
    93  	return &OptionNode{
    94  		compositeNode: compositeNode{
    95  			children: children,
    96  		},
    97  		Name:   name,
    98  		Equals: equals,
    99  		Val:    val,
   100  	}
   101  }
   102  
   103  func (n *OptionNode) GetName() Node {
   104  	return n.Name
   105  }
   106  
   107  func (n *OptionNode) GetValue() ValueNode {
   108  	return n.Val
   109  }
   110  
   111  // OptionNameNode represents an option name or even a traversal through message
   112  // types to name a nested option field. Example:
   113  //
   114  //	(foo.bar).baz.(bob)
   115  type OptionNameNode struct {
   116  	compositeNode
   117  	Parts []*FieldReferenceNode
   118  	// Dots represent the separating '.' characters between name parts. The
   119  	// length of this slice must be exactly len(Parts)-1, each item in Parts
   120  	// having a corresponding item in this slice *except the last* (since a
   121  	// trailing dot is not allowed).
   122  	//
   123  	// These do *not* include dots that are inside of an extension name. For
   124  	// example: (foo.bar).baz.(bob) has three parts:
   125  	//    1. (foo.bar)  - an extension name
   126  	//    2. baz        - a regular field in foo.bar
   127  	//    3. (bob)      - an extension field in baz
   128  	// Note that the dot in foo.bar will thus not be present in Dots but is
   129  	// instead in Parts[0].
   130  	Dots []*RuneNode
   131  }
   132  
   133  // NewOptionNameNode creates a new *OptionNameNode. The dots arg must have a
   134  // length that is one less than the length of parts. The parts arg must not be
   135  // empty.
   136  func NewOptionNameNode(parts []*FieldReferenceNode, dots []*RuneNode) *OptionNameNode {
   137  	if len(parts) == 0 {
   138  		panic("must have at least one part")
   139  	}
   140  	if len(dots) != len(parts)-1 {
   141  		panic(fmt.Sprintf("%d parts requires %d dots, not %d", len(parts), len(parts)-1, len(dots)))
   142  	}
   143  	children := make([]Node, 0, len(parts)*2-1)
   144  	for i, part := range parts {
   145  		if part == nil {
   146  			panic(fmt.Sprintf("parts[%d] is nil", i))
   147  		}
   148  		if i > 0 {
   149  			if dots[i-1] == nil {
   150  				panic(fmt.Sprintf("dots[%d] is nil", i-1))
   151  			}
   152  			children = append(children, dots[i-1])
   153  		}
   154  		children = append(children, part)
   155  	}
   156  	return &OptionNameNode{
   157  		compositeNode: compositeNode{
   158  			children: children,
   159  		},
   160  		Parts: parts,
   161  		Dots:  dots,
   162  	}
   163  }
   164  
   165  // FieldReferenceNode is a reference to a field name. It can indicate a regular
   166  // field (simple unqualified name), an extension field (possibly-qualified name
   167  // that is enclosed either in brackets or parentheses), or an "any" type
   168  // reference (a type URL in the form "server.host/fully.qualified.Name" that is
   169  // enclosed in brackets).
   170  //
   171  // Extension names are used in options to refer to custom options (which are
   172  // actually extensions), in which case the name is enclosed in parentheses "("
   173  // and ")". They can also be used to refer to extension fields of options.
   174  //
   175  // Extension names are also used in message literals to set extension fields,
   176  // in which case the name is enclosed in square brackets "[" and "]".
   177  //
   178  // "Any" type references can only be used in message literals, and are not
   179  // allowed in option names. They are always enclosed in square brackets. An
   180  // "any" type reference is distinguished from an extension name by the presence
   181  // of a slash, which must be present in an "any" type reference and must be
   182  // absent in an extension name.
   183  //
   184  // Examples:
   185  //
   186  //	foobar
   187  //	(foo.bar)
   188  //	[foo.bar]
   189  //	[type.googleapis.com/foo.bar]
   190  type FieldReferenceNode struct {
   191  	compositeNode
   192  	Open *RuneNode // only present for extension names and "any" type references
   193  
   194  	// only present for "any" type references
   195  	UrlPrefix IdentValueNode
   196  	Slash     *RuneNode
   197  
   198  	Name IdentValueNode
   199  
   200  	Close *RuneNode // only present for extension names and "any" type references
   201  }
   202  
   203  // NewFieldReferenceNode creates a new *FieldReferenceNode for a regular field.
   204  // The name arg must not be nil.
   205  func NewFieldReferenceNode(name *IdentNode) *FieldReferenceNode {
   206  	if name == nil {
   207  		panic("name is nil")
   208  	}
   209  	children := []Node{name}
   210  	return &FieldReferenceNode{
   211  		compositeNode: compositeNode{
   212  			children: children,
   213  		},
   214  		Name: name,
   215  	}
   216  }
   217  
   218  // NewExtensionFieldReferenceNode creates a new *FieldReferenceNode for an
   219  // extension field. All args must be non-nil. The openSym and closeSym runes
   220  // should be "(" and ")" or "[" and "]".
   221  func NewExtensionFieldReferenceNode(openSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode {
   222  	if name == nil {
   223  		panic("name is nil")
   224  	}
   225  	if openSym == nil {
   226  		panic("openSym is nil")
   227  	}
   228  	if closeSym == nil {
   229  		panic("closeSym is nil")
   230  	}
   231  	children := []Node{openSym, name, closeSym}
   232  	return &FieldReferenceNode{
   233  		compositeNode: compositeNode{
   234  			children: children,
   235  		},
   236  		Open:  openSym,
   237  		Name:  name,
   238  		Close: closeSym,
   239  	}
   240  }
   241  
   242  // NewAnyTypeReferenceNode creates a new *FieldReferenceNode for an "any"
   243  // type reference. All args must be non-nil. The openSym and closeSym runes
   244  // should be "[" and "]". The slashSym run should be "/".
   245  func NewAnyTypeReferenceNode(openSym *RuneNode, urlPrefix IdentValueNode, slashSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode {
   246  	if name == nil {
   247  		panic("name is nil")
   248  	}
   249  	if openSym == nil {
   250  		panic("openSym is nil")
   251  	}
   252  	if closeSym == nil {
   253  		panic("closeSym is nil")
   254  	}
   255  	if urlPrefix == nil {
   256  		panic("urlPrefix is nil")
   257  	}
   258  	if slashSym == nil {
   259  		panic("slashSym is nil")
   260  	}
   261  	children := []Node{openSym, urlPrefix, slashSym, name, closeSym}
   262  	return &FieldReferenceNode{
   263  		compositeNode: compositeNode{
   264  			children: children,
   265  		},
   266  		Open:      openSym,
   267  		UrlPrefix: urlPrefix,
   268  		Slash:     slashSym,
   269  		Name:      name,
   270  		Close:     closeSym,
   271  	}
   272  }
   273  
   274  // IsExtension reports if this is an extension name or not (e.g. enclosed in
   275  // punctuation, such as parentheses or brackets).
   276  func (a *FieldReferenceNode) IsExtension() bool {
   277  	return a.Open != nil && a.Slash == nil
   278  }
   279  
   280  // IsExtension reports if this is an extension name or not (e.g. enclosed in
   281  // punctuation, such as parentheses or brackets).
   282  func (a *FieldReferenceNode) IsAnyTypeReference() bool {
   283  	return a.Slash != nil
   284  }
   285  
   286  func (a *FieldReferenceNode) Value() string {
   287  	if a.Open != nil {
   288  		if a.Slash != nil {
   289  			return string(a.Open.Rune) + string(a.UrlPrefix.AsIdentifier()) + string(a.Slash.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune)
   290  		}
   291  		return string(a.Open.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune)
   292  	} else {
   293  		return string(a.Name.AsIdentifier())
   294  	}
   295  }
   296  
   297  // CompactOptionsNode represents a compact options declaration, as used with
   298  // fields, enum values, and extension ranges. Example:
   299  //
   300  //	[deprecated = true, json_name = "foo_bar"]
   301  type CompactOptionsNode struct {
   302  	compositeNode
   303  	OpenBracket *RuneNode
   304  	Options     []*OptionNode
   305  	// Commas represent the separating ',' characters between options. The
   306  	// length of this slice must be exactly len(Options)-1, with each item
   307  	// in Options having a corresponding item in this slice *except the last*
   308  	// (since a trailing comma is not allowed).
   309  	Commas       []*RuneNode
   310  	CloseBracket *RuneNode
   311  }
   312  
   313  // NewCompactOptionsNode creates a *CompactOptionsNode. All args must be
   314  // non-nil. The commas arg must have a length that is one less than the
   315  // length of opts. The opts arg must not be empty.
   316  func NewCompactOptionsNode(openBracket *RuneNode, opts []*OptionNode, commas []*RuneNode, closeBracket *RuneNode) *CompactOptionsNode {
   317  	if openBracket == nil {
   318  		panic("openBracket is nil")
   319  	}
   320  	if closeBracket == nil {
   321  		panic("closeBracket is nil")
   322  	}
   323  	if len(opts) == 0 {
   324  		panic("must have at least one part")
   325  	}
   326  	if len(commas) != len(opts)-1 {
   327  		panic(fmt.Sprintf("%d opts requires %d commas, not %d", len(opts), len(opts)-1, len(commas)))
   328  	}
   329  	children := make([]Node, 0, len(opts)*2+1)
   330  	children = append(children, openBracket)
   331  	for i, opt := range opts {
   332  		if i > 0 {
   333  			if commas[i-1] == nil {
   334  				panic(fmt.Sprintf("commas[%d] is nil", i-1))
   335  			}
   336  			children = append(children, commas[i-1])
   337  		}
   338  		if opt == nil {
   339  			panic(fmt.Sprintf("opts[%d] is nil", i))
   340  		}
   341  		children = append(children, opt)
   342  	}
   343  	children = append(children, closeBracket)
   344  
   345  	return &CompactOptionsNode{
   346  		compositeNode: compositeNode{
   347  			children: children,
   348  		},
   349  		OpenBracket:  openBracket,
   350  		Options:      opts,
   351  		Commas:       commas,
   352  		CloseBracket: closeBracket,
   353  	}
   354  }
   355  
   356  func (e *CompactOptionsNode) GetElements() []*OptionNode {
   357  	if e == nil {
   358  		return nil
   359  	}
   360  	return e.Options
   361  }