Skip to content

proposal: reflect: allow multi-line struct tags #42182

Closed
@gtrevg

Description

@gtrevg

Overview

Go's struct tags has been wildly successful. It has been so successful, in fact, that many projects leverage the reflect.StructTag system to support code generation, struct value validation, storage and retrieval, UI presentation, and a number of other use cases. Tagging allows one structure to be used throughout the layers of the program resulting in having a central location to modify the attributes, yet also allowing customizations to be applied to each of the layers.

The following example is a use case where the struct is used to:

  • Generate a presentation layer (label, description)
  • Generate REST API docs (label, description, json, example, pattern)
  • Deserialize and validate data coming into a REST API (json, pattern)
  • Define how to persist the data in a SQL database (sql)
  • Serialize a response back to the client (json)

All of that meta information together could easily be as complicated as the following:

type Machine struct {
    ID string `json:"id" yaml:"id" description:"UUID auto-generated to identify the machine" pattern:"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" sql:"type:varchar(36);not null;unique;primary_key" label:"Machine Id" example="1328149e-15a1-11eb-adc1-0242ac120002"`

    Host string `json:"host,omitempty" yaml:"host,omitempty" pattern:"(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])" description:"Host name for the machine" label:"Host Name" example:"myserver.domain.com" sql:"type:varchar(255)"`

    IPv4 string `json:"ipv4,omitempty" yaml:"ipv4,omitempty" pattern:"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}" description:"IPv4 address for the machine." label:"IPv4 Address" example:"127.0.0.1" sql:"type:varchar(15)"`
}

Proposal

To improve readability, reduce mistakes, and allow the meta information to continue to scale, a minor backwards compatible change could be applied to the struct tag spec: allow newlines and tabs to exist within the tag. This would enable the following struct definition:

type Machine struct {
    ID string `
        label:"Machine ID"
        description:"UUID auto-generated to identify the machine"
        json:"id"
        yaml:"id"
        pattern:"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
        example="1328149e-15a1-11eb-adc1-0242ac120002"
        sql:"type:varchar(36);not null;unique;primary_key"
    `

    Host string `
        label:"Host Name"
        description:"Host name for the machine"
        json:"host,omitempty"
        yaml:"host,omitempty"
        pattern:"(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])"
        sql:"type:varchar(255)"
    `

    IPv4 string `
        label:"IPv4 Address"
        description:"IPv4 address for the machine"
        json:"ipv4,omitempty"
        yaml:"ipv4,omitempty"
        pattern:"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
        sql:"type:varchar(15)"
    `
}

The resulting structure is much more readable and can help prevent mistakes (ironically, just converting the above example into the more readable format exposed my own mistake of missing an end quote). This allowance would still be backwards compatible with all previous tag definitions. In addition, denoting multi-line content with `` is already a well understood and widely used pattern in the Go community. This would be a natural fit that some probably already expect to work.

These new characters should only be allowed in the spaces between the key:"value" pairs. This would be consistent with the documentation on StructTag:

https://golang.org/src/reflect/type.go?s=30914:30935#L1101

// A StructTag is the tag string in a struct field.
//
// By convention, tag strings are a concatenation of
// optionally space-separated key:"value" pairs.
// Each key is a non-empty string consisting of non-control
// characters other than space (U+0020 ' '), quote (U+0022 '"'),
// and colon (U+003A ':'). Each value is quoted using U+0022 '"'
// characters and Go string literal syntax.

Dependencies

  • The Go formatter would need to enforce a style for multi-line tags

Related:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions