func TestAtof(t *testing.T) { var ( iv interface{} ) iv = clike.Atof("123.456") // pick up result as an interface so we can test type as well as value switch iv.(type) { case float64: break default: t.Errorf("atof() did not return float64, no other atof() tests executed") return } if iv.(float64) != 123.456 { t.Errorf("atoll( '123.456' ) returned %.3f; did not return 123.456", iv.(float64)) } }
/* Given a thing (field value in a struct), set the thing with the element in the map (key) the map value to the proper type. */ func set_value(thing reflect.Value, kind reflect.Kind, key string, tag_id string, pfx string, annon bool, m map[string]string) { if !thing.CanAddr() { // prevent stack dump return } switch kind { default: fmt.Fprintf(os.Stderr, "transform.mts: tagged sturct member cannot be converted from map: tag=%s kind=%v\n", key, thing.Kind()) case reflect.String: thing.SetString(m[key]) case reflect.Ptr: p := thing.Elem() // get the pointer value; allows us to suss the type if !p.IsValid() { // ptr is nill in the struct so we must allocate a pointer to 0 so it can be changed below thing.Set(reflect.New(thing.Type().Elem())) p = thing.Elem() } switch p.Kind() { case reflect.String: s := m[key] // copy it and then point to the copy thing.Set(reflect.ValueOf(&s)) case reflect.Int: i := clike.Atoi(m[key]) // convert to integer and then point at the value thing.Set(reflect.ValueOf(&i)) case reflect.Int64: i := clike.Atoi64(m[key]) thing.Set(reflect.ValueOf(&i)) case reflect.Int32: i := clike.Atoi32(m[key]) thing.Set(reflect.ValueOf(&i)) case reflect.Int16: i := clike.Atoi16(m[key]) thing.Set(reflect.ValueOf(&i)) case reflect.Int8: i := int8(clike.Atoi16(m[key])) thing.Set(reflect.ValueOf(&i)) case reflect.Uint: ui := clike.Atou(m[key]) thing.Set(reflect.ValueOf(&ui)) case reflect.Uint64: ui := clike.Atou64(m[key]) thing.Set(reflect.ValueOf(&ui)) case reflect.Uint32: ui := clike.Atou32(m[key]) thing.Set(reflect.ValueOf(&ui)) case reflect.Uint16: ui := clike.Atou16(m[key]) thing.Set(reflect.ValueOf(&ui)) case reflect.Uint8: ui := uint8(clike.Atou16(m[key])) thing.Set(reflect.ValueOf(&ui)) case reflect.Float64: fv := clike.Atof(m[key]) thing.Set(reflect.ValueOf(&fv)) case reflect.Float32: fv := float32(clike.Atof(m[key])) thing.Set(reflect.ValueOf(&fv)) case reflect.Bool: b := m[key] == "true" || m[key] == "True" || m[key] == "TRUE" thing.Set(reflect.ValueOf(&b)) case reflect.Struct: map_to_struct(m, p, p.Type(), tag_id, pfx) // recurse to process } case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: thing.SetInt(clike.Atoi64(m[key])) case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: thing.SetUint(uint64(clike.Atoi64(m[key]))) case reflect.Float64, reflect.Float32: thing.SetFloat(clike.Atof(m[key])) case reflect.Bool: thing.SetBool(m[key] == "true") case reflect.Map: new_map := reflect.MakeMap(thing.Type()) // create the map thing.Set(new_map) // put it in the struct idx := key + "/" // now populate the map ilen := len(idx) for k, _ := range m { // we could keep a separate list of keys, but for now this should do if strings.HasPrefix(k, key) { tokens := strings.Split(k[ilen:], "/") map_key := reflect.ValueOf(tokens[0]) // map key is everything past the tag to the next slant map_ele_type := new_map.Type().Elem() // the type of the element that the map references mthing := reflect.New(map_ele_type).Elem() // new returns pointer, so dereference with Elem() (value not type!) set_value(mthing, mthing.Kind(), idx+tokens[0], tag_id, idx+tokens[0]+"/", false, m) // put the value into the map thing new_map.SetMapIndex(map_key, mthing) // put it into the map (order IS important; add to map after recursion) //fmt.Fprintf( os.Stderr, "saving: %s thing-type=%s mthing=%s\n", map_key, thing.Type(), mthing ) } } case reflect.Slice: c := clike.Atoi(m[key+".cap"]) l := clike.Atoi(m[key+".len"]) thing.Set(reflect.MakeSlice(thing.Type(), l, c)) // create a new slice with the same len/cap that it had for j := 0; j < l; j++ { idx := fmt.Sprintf("%s/%d", key, j) set_value(thing.Index(j), thing.Type().Elem().Kind(), idx, tag_id, idx+"/", false, m) // populate each value of the slice up to len } case reflect.Struct: if annon { map_to_struct(m, thing, thing.Type(), tag_id, key) // anon structs share namespace, so prefix is the same } else { map_to_struct(m, thing, thing.Type(), tag_id, pfx) // dive to get the substruct adding a level to the prefix } } }
/* Internal funciton to convert an interface into a supported desired value. */ func cvt2desired(v interface{}, desired int) interface{} { switch v.(type) { case string: switch desired { case ET_STRING: return v case ET_STRINGP: return &v case ET_INT: return v.(int) case ET_UINT: return v.(uint) case ET_INT64: return v.(int64) case ET_FLOAT: return v.(float64) case ET_BOOL: return v == "true" } case *string: switch desired { case ET_STRING: return *(v.(*string)) case ET_STRINGP: return v case ET_INT: return clike.Atoi(*(v.(*string))) case ET_UINT: return clike.Atou(*(v.(*string))) case ET_INT64: return clike.Atoll(*(v.(*string))) case ET_FLOAT: return clike.Atof(*(v.(*string))) case ET_BOOL: return *(v.(*string)) == "true" } case float64: switch desired { case ET_STRING: return fmt.Sprintf("%.2f", v) case ET_STRINGP: s := fmt.Sprintf("%.2f", v) return &s case ET_INT: return int(v.(float64)) case ET_UINT: return uint(v.(float64)) case ET_INT64: return int64(v.(float64)) case ET_FLOAT: return v case ET_BOOL: return v.(float64) != 0.0 } case int: switch desired { case ET_STRING: return fmt.Sprintf("%d", v) case ET_STRINGP: s := fmt.Sprintf("%d", v) return &s case ET_INT: return v case ET_UINT: return uint(v.(int)) case ET_INT64: return int64(v.(int)) case ET_FLOAT: return float64(v.(int)) case ET_BOOL: return v.(int) != 0 } case int64: switch desired { case ET_STRING: return fmt.Sprintf("%d", v) case ET_STRINGP: s := fmt.Sprintf("%d", v) return &s case ET_INT: return int(v.(int64)) case ET_UINT: return uint(v.(int64)) case ET_INT64: return v case ET_FLOAT: return float64(v.(int64)) case ET_BOOL: return v.(int64) != 0 } case bool: switch desired { case ET_STRING: return fmt.Sprintf("%v", v) case ET_STRINGP: s := fmt.Sprintf("%v", v) return &s case ET_INT: if v.(bool) { return int(1) } else { return int(0) } case ET_UINT: if v.(bool) { return uint(1) } else { return uint(0) } case ET_INT64: if v.(bool) { return int64(1) } else { return int64(0) } case ET_FLOAT: if v.(bool) { return 1.0 } else { return 0.0 } case ET_BOOL: return v } } return nil }
/* Parses a configuration file containing sections and key/value pairs within the sections. Returns a map of sections (by name) with each entry in the map being a map[string]interface{}. Key/values are converted and stored by the key name as either string pointers or float64s. If the value is quoted, the quotes are removed. Section names may be duplicated which causes values appearing later in subsequent sections to be added to the previously encountered values. Keys within each section must be uniqueue. If a duplicate key is encountered, the last one read will be the one that ends up in the map. If all_str is true, then all values are returned as strings; no attempt is made to convert values that seem to be numeric into actual values as it might make logic in the user programme a bit easier (no interface dreferences). */ func Parse(sectmap map[string]map[string]interface{}, fname string, all_str bool) (m map[string]map[string]interface{}, err error) { var ( rec string // record read from input file sect map[string]interface{} // current section sname string // current section name rerr error = nil // read error ) if sectmap != nil { m = sectmap if m["default"] == nil { // don't count on user creating a default section m["default"] = make(map[string]interface{}) } } else { m = make(map[string]map[string]interface{}) m["default"] = make(map[string]interface{}) } sname = "default" sect = m[sname] // always start in default section f, err := os.Open(fname) if err != nil { return } defer f.Close() br := bufio.NewReader(f) for rerr == nil { rec, rerr = br.ReadString('\n') if rerr == nil { rec = strings.Trim(rec, " \t\n") // ditch lead/trail whitespace if len(rec) == 0 { // blank line continue } switch rec[0] { case ':': // section //sname = rec[1:]; _, tokens := token.Tokenise_qpopulated(rec, " \t") // easy way to ditch everything after the first token sname = tokens[0][1:] if m[sname] == nil { sect = make(map[string]interface{}) m[sname] = sect } else { sect = m[sname] } case '#': // comment // nop case '<': m, err = Parse(m, rec[1:], all_str) if err != nil { return } default: // assume key value pair append := false first_tok := 1 // first token of var is 1, but could be later force_str := strings.Index(rec, "\"") != -1 // if a quote in the buffer, we force it to be a string ntokens, tokens := token.Tokenise_qpopulated(rec, " \t=") if ntokens >= 2 { // if key = "value" # [comment], n will be 3 or more tl := len(tokens[0]) if tokens[0][tl-1:] == "+" { //foo+= bar rather than foo = bar or foo += bar tokens[0] = tokens[0][:tl-1] append = true } else { if tokens[1] == "+" { // += results in lone plus, not to be confused with +9999 append = true first_tok++ } } key := tokens[0] if tokens[first_tok] == "" { // key = (missing value) given tokens[first_tok] = " " } fc := tokens[first_tok][0:1] if !force_str && !all_str && ((fc >= "0" && fc <= "9") || fc == "+" || fc == "-") { // allowed to convert numbers to float sect[key] = clike.Atof(tokens[first_tok]) } else { dup := "" sep := "" for i := first_tok; i < ntokens && tokens[i][0:1] != "#"; i++ { dup += sep + tokens[i] // snarf tokens up to comment reducing white space to 1 blank sep = " " } if append && sect[key] != nil { if old_str, ok := sect[key].(*string); ok { dup = *old_str + " " + dup } } sect[key] = &dup } } // silently discard token that is just a key, allowing the default to override } } } if rerr != io.EOF { err = rerr } return }
/* Parses a configuration file containing sections and key/value pairs within the sections. Returns a map of sections (by name) with each entry in the map being a map[string]interface{}. Key/values are converted and stored by the key name as either string pointers or float64s. If the value is quoted, the quotes are removed. Section names may be duplicated which causes values appearing later in subsequent sections to be added to the previously encountered values. Keys within each section must be uniqueue. If a duplicate key is encountered, the last one read will be the one that ends up in the map. If all_str is true, then all values are returned as strings; no attempt is made to convert values that seem to be numeric into actual values as it might make logic in the user programme a bit easier (no interface dreferences). */ func Parse(sectmap map[string]map[string]interface{}, fname string, all_str bool) (m map[string]map[string]interface{}, err error) { var ( rec string // record read from input file sect map[string]interface{} // current section sname string // current section name rerr error = nil // read error ) if sectmap != nil { m = sectmap if m["default"] == nil { // don't count on user creating a default section m["default"] = make(map[string]interface{}) } } else { m = make(map[string]map[string]interface{}) m["default"] = make(map[string]interface{}) } sname = "default" sect = m[sname] // always start in default section f, err := os.Open(fname) if err != nil { return } defer f.Close() br := bufio.NewReader(f) for rerr == nil { rec, rerr = br.ReadString('\n') if rerr == nil { rec = strings.Trim(rec, " \t\n") // ditch lead/trail whitespace if len(rec) == 0 { // blank line continue } switch rec[0] { case ':': // section //sname = rec[1:]; _, tokens := token.Tokenise_qpopulated(rec, " \t") // easy way to ditch everything after the first token sname = tokens[0][1:] if m[sname] == nil { sect = make(map[string]interface{}) m[sname] = sect } else { sect = m[sname] } case '#': // comment // nop case '<': m, err = Parse(m, rec[1:], all_str) if err != nil { return } default: // assume key value pair ntokens, tokens := token.Tokenise_qpopulated(rec, " \t=") if ntokens >= 2 { // if key = "value" # [comment], n will be 3 or more key := tokens[0] if tokens[1] == "" { // key = (missing value) given tokens[1] = " " } fc := tokens[1][0:1] if !all_str && ((fc >= "0" && fc <= "9") || fc == "+" || fc == "-") { // allowed to convert numbers to float sect[key] = clike.Atof(tokens[1]) } else { dup := "" sep := "" for i := 1; i < ntokens && tokens[i][0:1] != "#"; i++ { dup += sep + tokens[i] // snarf tokens up to comment reducing white space to 1 blank sep = " " } sect[key] = &dup } } // silently discard token that is just a key, allowing the default to override } } } if rerr != io.EOF { err = rerr } return }