// createMapResponse is a helper for generating a response value from // a value of type map. func createMapResponse(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) interface{} { response := reflect.MakeMap(value.Type()) for _, key := range value.MapKeys() { var elementOptions objx.Map keyStr := key.Interface().(string) if options != nil { var elementOptionsValue *objx.Value if options.Has(keyStr) { elementOptionsValue = options.Get(keyStr) } else if options.Has("*") { elementOptionsValue = options.Get("*") } if elementOptionsValue.IsMSI() { elementOptions = objx.Map(elementOptionsValue.MSI()) } else if elementOptionsValue.IsObjxMap() { elementOptions = elementOptionsValue.ObjxMap() } else { panic("Don't know what to do with option") } } itemResponse := createResponseValue(value.MapIndex(key), elementOptions, constructor, domain) response.SetMapIndex(key, reflect.ValueOf(itemResponse)) } return response.Interface() }
func element(k string, v interface{}, vString string, doIndent bool, indentLevel int, options objx.Map) string { var typeString string if v != nil && options.Has(OptionIncludeTypeAttributes) { typeString = getTypeString(v) } if doIndent { indent := strings.Repeat(Indentation, indentLevel) if options.Has(OptionIncludeTypeAttributes) { return fmt.Sprintf(XMLElementWithTypeAttributeFormatIndented, k, typeString, indent, vString, k) } else { return fmt.Sprintf(XMLElementFormatIndented, k, indent, vString, k) } } if options.Has(OptionIncludeTypeAttributes) { return fmt.Sprintf(XMLElementWithTypeAttributeFormat, k, typeString, vString, k) } else { return fmt.Sprintf(XMLElementFormat, k, vString, k) } }
// createStructResponse is a helper for generating a response value // from a value of type struct. func createStructResponse(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) interface{} { structType := value.Type() // Support "database/sql".Null* types, and any other types // matching that structure if v, err := createNullableDbResponse(value, structType); err == nil { return v } response := make(objx.Map) for i := 0; i < value.NumField(); i++ { fieldType := structType.Field(i) fieldValue := value.Field(i) if fieldType.Anonymous { embeddedResponse := CreateResponse(fieldValue.Interface(), options, constructor, domain).(objx.Map) for key, value := range embeddedResponse { // Don't overwrite values from the base struct if _, ok := response[key]; !ok { response[key] = value } } } else if unicode.IsUpper(rune(fieldType.Name[0])) { name := ResponseTag(fieldType) switch name { case "-": continue default: var subOptions objx.Map if options != nil && (options.Has(name) || options.Has("*")) { var subOptionsValue *objx.Value if options.Has(name) { subOptionsValue = options.Get(name) } else { subOptionsValue = options.Get("*") } if subOptionsValue.IsMSI() { subOptions = objx.Map(subOptionsValue.MSI()) } else if subOptionsValue.IsObjxMap() { subOptions = subOptionsValue.ObjxMap() } else { panic("Don't know what to do with option") } } response[name] = createResponseValue(fieldValue, subOptions, constructor, domain) } } } return response }
func (engine *RuleEngine) DefineVirtualDevice(name string, obj objx.Map) error { title := name if obj.Has("title") { title = obj.Get("title").Str(name) } // if the device was for some reason defined in another script, // we must remove it engine.model.RemoveLocalDevice(name) dev := engine.model.EnsureLocalDevice(name, title) engine.cleanup.AddCleanup(func() { // runs when the rule file is reloaded engine.model.RemoveLocalDevice(name) }) if !obj.Has("cells") { return nil } v := obj.Get("cells") var m objx.Map switch { case v.IsObjxMap(): m = v.ObjxMap() case v.IsMSI(): m = objx.Map(v.MSI()) default: return fmt.Errorf("device %s doesn't have proper 'cells' property", name) } // Sorting cells by their names is not important when defining device // while the engine is not active because all the cells will be published // all at once when the engine starts. // On the other hand, when defining the device for the active engine // the newly added cells are published immediately and if their order // changes (map key order is random) the tests may break. cellNames := make([]string, 0, len(m)) for cellName, _ := range m { cellNames = append(cellNames, cellName) } sort.Strings(cellNames) for _, cellName := range cellNames { maybeCellDef := m[cellName] cellDef, ok := maybeCellDef.(objx.Map) if !ok { cd, ok := maybeCellDef.(map[string]interface{}) if !ok { return fmt.Errorf("%s/%s: bad cell definition", name, cellName) } cellDef = objx.Map(cd) } cellType, ok := cellDef["type"] if !ok { return fmt.Errorf("%s/%s: no cell type", name, cellName) } // FIXME: too much spaghetti for my taste if cellType == "pushbutton" { dev.SetButtonCell(cellName) continue } cellValue, ok := cellDef["value"] if !ok { return fmt.Errorf("%s/%s: cell value required for cell type %s", name, cellName, cellType) } cellReadonly := false cellReadonlyRaw, hasReadonly := cellDef["readonly"] if hasReadonly { cellReadonly, ok = cellReadonlyRaw.(bool) if !ok { return fmt.Errorf("%s/%s: non-boolean value of readonly property", name, cellName) } } if cellType == "range" { fmax := DEFAULT_CELL_MAX max, ok := cellDef["max"] if ok { fmax, ok = max.(float64) if !ok { return fmt.Errorf("%s/%s: non-numeric value of max property", name, cellName) } } // FIXME: can be float dev.SetRangeCell(cellName, cellValue, fmax, cellReadonly) } else { dev.SetCell(cellName, cellType.(string), cellValue, cellReadonly) } } return nil }