blob: e90c38f8ed113d03b5eb668b67dc85d030ce3c9f [file] [log] [blame]
// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package flagpb
import (
"fmt"
"reflect"
"strings"
)
// MarshalUntyped marshals a key-value map to flags.
func MarshalUntyped(msg map[string]interface{}) ([]string, error) {
return appendFlags(nil, nil, reflect.ValueOf(msg))
}
func appendFlags(flags []string, path []string, v reflect.Value) ([]string, error) {
name := "-" + strings.Join(path, ".")
v = indirect(v)
var err error
switch v.Kind() {
case reflect.Map:
if kind := v.Type().Key().Kind(); kind != reflect.String {
return nil, fmt.Errorf("map key type must be string, got %s", kind)
}
for _, k := range v.MapKeys() {
flags, err = appendFlags(flags, append(path, k.String()), v.MapIndex(k))
if err != nil {
return nil, err
}
}
case reflect.Slice, reflect.Array:
sep := false
for i, l := 0, v.Len(); i < l; i++ {
if sep {
flags = append(flags, name)
}
e := indirect(v.Index(i))
if flags, err = appendFlags(flags, path, e); err != nil {
return nil, err
}
sep = e.Kind() == reflect.Map
}
case reflect.Bool:
if v.Bool() {
flags = append(flags, name)
} else {
flags = append(flags, name+"=false")
}
// numbers and strings
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64,
reflect.String:
flags = append(flags, name, fmt.Sprintf("%v", v.Interface()))
case reflect.Invalid:
return nil, fmt.Errorf("invalid value")
default:
return nil, fmt.Errorf("unsupported type: %s", v.Type())
}
return flags, nil
}
func indirect(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface {
v = v.Elem()
}
return v
}