micro/metadata/metadata.go
Vasiliy Tolstov 88c7439c01 tmp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-11 01:04:43 +03:00

179 lines
3.8 KiB
Go

// Package metadata is a way of defining message headers
package metadata // import "go.unistack.org/micro/v3/metadata"
import (
"net/textproto"
"sort"
)
var (
// HeaderTopic is the header name that contains topic name
HeaderTopic = "Micro-Topic"
// HeaderContentType specifies content type of message
HeaderContentType = "Content-Type"
// HeaderEndpoint specifies endpoint in service
HeaderEndpoint = "Micro-Endpoint"
// HeaderService specifies service
HeaderService = "Micro-Service"
// HeaderTimeout specifies timeout of operation
HeaderTimeout = "Micro-Timeout"
// HeaderAuthorization specifies Authorization header
HeaderAuthorization = "Authorization"
)
// Metadata is our way of representing request headers internally.
// They're used at the RPC level and translate back and forth
// from Transport headers.
type Metadata map[string][]string
type rawMetadata struct {
md Metadata
}
// defaultMetadataSize used when need to init new Metadata
var defaultMetadataSize = 2
// Iterator used to iterate over metadata with order
type Iterator struct {
md Metadata
keys []string
cur int
cnt int
}
// Next advance iterator to next element
func (iter *Iterator) Next(k *string, v *[]string) bool {
if iter.cur+1 > iter.cnt {
return false
}
*k = iter.keys[iter.cur]
*v = iter.md[*k]
iter.cur++
return true
}
// Iterator returns the itarator for metadata in sorted order
func (md Metadata) Iterator() *Iterator {
iter := &Iterator{md: md, cnt: len(md)}
iter.keys = make([]string, 0, iter.cnt)
for k := range md {
iter.keys = append(iter.keys, k)
}
sort.Strings(iter.keys)
return iter
}
// Values returns values from metadata by key
func (md Metadata) Values(key string) ([]string, bool) {
if md == nil {
return nil, false
}
// fast path
val, ok := md[key]
if !ok {
// slow path
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
}
return val, ok
}
// Get returns value from metadata by key
func (md Metadata) Get(key string) (string, bool) {
if md == nil {
return "", false
}
// fast path
val, ok := md[key]
if !ok {
// slow path
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
}
if len(val) == 0 {
return "", false
}
return val[0], ok
}
// Set is used to store value in metadata
func (md Metadata) Set(kv ...string) {
if md == nil {
return
}
if len(kv)%2 == 1 {
kv = kv[:len(kv)-1]
}
for idx := 0; idx < len(kv); idx += 2 {
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = []string{kv[idx+1]}
}
}
// Add is used to append value in metadata
func (md Metadata) Add(k string, v string) {
kn := textproto.CanonicalMIMEHeaderKey(k)
md[kn] = append(md[kn], v)
}
// Del is used to remove value from metadata
func (md Metadata) Del(keys ...string) {
for _, key := range keys {
// fast path
delete(md, key)
// slow path
delete(md, textproto.CanonicalMIMEHeaderKey(key))
}
}
// Copy makes a copy of the metadata
func Copy(md Metadata) Metadata {
nmd := New(len(md))
for key, val := range md {
nmd[key] = val
}
return nmd
}
// New return new sized metadata
func New(size int) Metadata {
if size == 0 {
size = defaultMetadataSize
}
return make(Metadata, size)
}
// Merge merges metadata to existing metadata, overwriting if specified
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
nmd := Copy(omd)
for key, nval := range mmd {
oval, ok := nmd[key]
switch {
case ok && !overwrite:
continue
case len(nval) == 1 && nval[0] != "":
nmd[key] = nval
case ok && len(nval) > 1:
sort.Strings(nval)
sort.Strings(oval)
for idx, v := range nval {
if oval[idx] != v {
oval[idx] = v
}
}
nmd[key] = oval
case ok && nval[0] == "":
nmd.Del(key)
}
}
return nmd
}
// Pairs from which metadata created
func Pairs(kv ...string) (Metadata, bool) {
if len(kv)%2 == 1 {
return nil, false
}
md := New(len(kv) / 2)
md.Set(kv...)
return md, true
}