Tag is a lightweight, zero-dependency library that transforms raw Go struct tag strings into strongly typed, well-structured Go values.
It eliminates repetitive parsing logic, reduces boilerplate, and provides a clean, declarative way to define your tag schema — while keeping performance predictable and implementation overhead minimal.
To install Tag, run the following command:
go get github.com/codnect/tagGo’s reflect.StructTag exposes tag values only as raw strings:
prop:"'database.host',default=5432"In most projects, this leads to:
- Manual string splitting
- Repetitive parsing logic
- Custom flag handling
- Default values
- Lists, maps, typed values
- Edge cases everywhere
Tag centralizes this logic into a single, safe, strongly typed mechanism.
You define a struct describing your tag. Tag handles parsing, type inference, nested structures, and value assignment.
Define the structure of your tag and implement the Tagger interface:
type PropTag struct {
Key string `option:"value"`
Optional bool `option:"optional"`
Default int `option:"default"`
}
// Tag identifies the tag name.
func (t PropTag) Tag() string {
return "prop"
}Parse a raw tag string:
propTag := &PropTag{}
err := tag.Parse(`prop:"'database.host',default=5432"`, propTag)
if err != nil {
// handle error
}A tag expression is composed of a primary value followed by zero or more options. Tag recognizes three option forms:
- Primary Value — always the first item
- Boolean Flags — option names without a value (e.g., optional)
- Key/Value Options — options in the form name=value
This structure applies uniformly to all tags and defines how each parsed item is mapped onto the fields of your tag struct.
The first item of a tag expression is always treated as the primary value.
prop:"'database.host'"This value implicitly maps to the field whose option name is value — a reserved keyword used
by Tag to represent the primary value.
type PropTag struct {
Key string `option:"value"` // primary value
}📌 Important: value is reserved option name and should not be used for other option names.
Only one field in a tag struct can map to option:"value".
Any option written without an equals sign (=) is parsed as a boolean flag and evaluated to true.
prop:"'database.host',optional"`Flags must always map to boolean fields in your tag struct:
type PropTag struct {
Key string `option:"value"` // primary value
Optional bool `option:"optional"` // bool flag
}Options written in the form key=value attach typed configuration values.
prop:"'database.host',default=5432"These options map directly to the struct fields that use the same option name:
type PropTag struct {
Key string `option:"value"` // primary value
Optional bool `option:"optional"` // bool flag option
Default any `option:"default"` // key=value option
}Tag automatically infers the type of each option value (string, int, float, bool, slice, map, struct, etc.).
Slices are expressed using square brackets [...].
Strings are written with quotes; numeric values can be written directly:
roles:"['admin','editor','viewer']"This maps to a slice field in your tag struct:
type RolesTag struct {
Value []string `option:"value"`
}
func (t RolesTag) Tag() string {
return "roles"
}Slices can contain other slices:
matrix:"[[1,2,3],[4,5,6],[7,8,9]]"This maps to a nested slice field in your tag struct:
type MatrixTag struct {
Value [][]int `option:"value"`
}
func (t MatrixTag) Tag() string {
return "matrix"
}Maps are expressed using curly braces {...} with key:value pairs.
Keys must be strings; values can be any supported type:
limits:"{'cpu':'2','memory':'4Gi'}"This maps to a map field in your tag struct:
type LimitsTag struct {
Value map[string]string `option:"value"`
}
func (t LimitsTag) Tag() string {
return "limits"
}Maps can mix different value types as well:
config:"{'debug':true,'max_connections':100,'timeout':30.5}"This maps to a map with any values:
type ConfigTag struct {
Value map[string]any `option:"value"`
}
func (t ConfigTag) Tag() string {
return "config"
}Map values can be slices or other maps:
settings:"{'servers':['server1','server2'],'thresholds':{'cpu':80,'memory':70}}"This maps to a complex nested structure:
type SettingsTag struct {
Value map[string]any `option:"value"`
}
func (t SettingsTag) Tag() string {
return "settings"
}Custom struct fields use the same syntax as maps — key/value pairs inside {...}. Tag parses it as a map internally, then binds it into your struct using field option names.
database:"{'host':'localhost','port':5432,'credentials':{'user':'admin','password':'secret'}}"This maps to a nested struct field:
type Credentials struct {
User string `option:"user"`
Password string `option:"password"`
}
type DatabaseTag struct {
Host string `option:"host"`
Port int `option:"port"`
Credentials Credentials `option:"credentials"`
}
func (t DatabaseTag) Tag() string {
return "database"
}You can combine flags, key/value options, slices, maps, and nested structs in a single tag:
service:"'my-service',optional,replicas=3,labels={'env':'prod','tier':'backend'},ports=[80,443]"This maps to a complex tag struct:
type ServiceTag struct {
Name string `option:"value"`
Optional bool `option:"optional"`
Replicas int `option:"replicas"`
Labels map[string]string `option:"labels"`
Ports []int `option:"ports"`
}
func (t ServiceTag) Tag() string {
return "service"
}Tag is released under version 2.0 of the Apache License.