go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/client/flagpb/marshal.go (about)

     1  // Copyright 2016 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package flagpb
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"strings"
    21  )
    22  
    23  // MarshalUntyped marshals a key-value map to flags.
    24  func MarshalUntyped(msg map[string]any) ([]string, error) {
    25  	return appendFlags(nil, nil, reflect.ValueOf(msg))
    26  }
    27  
    28  func appendFlags(flags []string, path []string, v reflect.Value) ([]string, error) {
    29  	name := "-" + strings.Join(path, ".")
    30  
    31  	v = indirect(v)
    32  
    33  	var err error
    34  	switch v.Kind() {
    35  
    36  	case reflect.Map:
    37  		if kind := v.Type().Key().Kind(); kind != reflect.String {
    38  			return nil, fmt.Errorf("map key type must be string, got %s", kind)
    39  		}
    40  		for _, k := range v.MapKeys() {
    41  			flags, err = appendFlags(flags, append(path, k.String()), v.MapIndex(k))
    42  			if err != nil {
    43  				return nil, err
    44  			}
    45  		}
    46  
    47  	case reflect.Slice, reflect.Array:
    48  		sep := false
    49  		for i, l := 0, v.Len(); i < l; i++ {
    50  			if sep {
    51  				flags = append(flags, name)
    52  			}
    53  			e := indirect(v.Index(i))
    54  			if flags, err = appendFlags(flags, path, e); err != nil {
    55  				return nil, err
    56  			}
    57  			sep = e.Kind() == reflect.Map
    58  		}
    59  
    60  	case reflect.Bool:
    61  		if v.Bool() {
    62  			flags = append(flags, name)
    63  		} else {
    64  			flags = append(flags, name+"=false")
    65  		}
    66  
    67  	// numbers and strings
    68  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    69  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
    70  		reflect.Float32, reflect.Float64,
    71  		reflect.String:
    72  		flags = append(flags, name, fmt.Sprintf("%v", v.Interface()))
    73  
    74  	case reflect.Invalid:
    75  		return nil, fmt.Errorf("invalid value")
    76  
    77  	default:
    78  		return nil, fmt.Errorf("unsupported type: %s", v.Type())
    79  	}
    80  	return flags, nil
    81  }
    82  
    83  func indirect(v reflect.Value) reflect.Value {
    84  	if v.Kind() == reflect.Interface {
    85  		v = v.Elem()
    86  	}
    87  	return v
    88  }