// GenMapType unmarshals JSON-encoded data that is in the form of // map[string][]Type and returns both the type declaration and the struct // definition(s) for Type. func GenMapType(typeName, name string, tagKeys []string, data []byte) ([]byte, error) { if len(typeName) == 0 { return nil, fmt.Errorf("type name required") } typeName = strings.Title(typeName) if len(name) == 0 { name = "Struct" } else { name = strings.Title(name) } var def interface{} err := json.Unmarshal(data, &def) if err != nil { return nil, err } switch d := def.(type) { case []interface{}: def = d[0] } // if it isn't a map, return an error as this only supports maps if reflect.TypeOf(def).Kind() != reflect.Map { return nil, fmt.Errorf("GenMapType error: expected a map, got %s", reflect.TypeOf(def).Kind()) } // extract the element to use as the basis point for defining the struct // m := reflect.ValueOf(def) keys := m.MapKeys() val := m.MapIndex(keys[0]) var buff bytes.Buffer // it it contains a slice, get an element from the slice if val.Elem().Kind() == reflect.Slice { buff.WriteString(fmt.Sprintf("type %s map[string][]%s\n\n", typeName, name)) val = val.Elem().Index(0) } else { buff.WriteString(fmt.Sprintf("type %s map[string]%s\n\n", typeName, name)) } var wg sync.WaitGroup q := queue.NewQ(2) result := make(chan []byte) // create first work item and add to the queue s := newStructDef(name, val.Elem()) q.Enqueue(s) // start the worker & send initial work item go func() { defineStruct(q, tagKeys, result, &wg) }() // collect the results until the resCh is closed var i int for { i++ val, ok := <-result if !ok { break } // TODO handle error/short read buff.Write(val) } return buff.Bytes(), nil }
// Gen generates the struct definitions and outputs it to W. func (t *Transmogrifier) Gen() error { var buff bytes.Buffer _, err := buff.ReadFrom(t.r) if err != nil { return err } if t.WriteJSON { var n int n, err = t.jw.Write(buff.Bytes()) if err != nil { return err } if n != buff.Len() { return ShortWriteError{n: buff.Len(), written: n, operation: "JSON to file"} } } var def interface{} err = json.Unmarshal(buff.Bytes(), &def) if err != nil { return err } switch d := def.(type) { case []interface{}: def = d[0] } buff.Reset() var wg sync.WaitGroup // Write the package and import stuff to the buffer n, err := buff.WriteString(fmt.Sprintf("package %s\n\n", t.pkg)) if err != nil { return err } if n != (10 + len(t.pkg)) { return ShortWriteError{n: len(t.pkg), written: n, operation: "package name to buffer"} } if t.ImportJSON { n, err = buff.WriteString("import (\n\t\"encoding/json\"\n)\n\n") if err != nil { return err } if n != 29 { return ShortWriteError{n: 29, written: n, operation: "import to buffer"} } } // create the work queue and the result chan q := queue.NewQ(2) result := make(chan []byte) // if MapType, process as a map type // and enqueue the first item if t.MapType { // if it isn't a map, return an error as this only supports maps if reflect.TypeOf(def).Kind() != reflect.Map { return fmt.Errorf("GenMapType error: expected a map, got %s", reflect.TypeOf(def).Kind()) } // extract the element to use as the basis point for defining the struct // m := reflect.ValueOf(def) keys := m.MapKeys() val := m.MapIndex(keys[0]) // it it contains a slice, get an element from the slice if val.Elem().Kind() == reflect.Slice { buff.WriteString(fmt.Sprintf("type %s map[string][]%s\n\n", t.name, t.structName)) val = val.Elem().Index(0) } else { buff.WriteString(fmt.Sprintf("type %s map[string]%s\n\n", t.name, t.structName)) } q.Enqueue(newStructDef(t.structName, val.Elem())) goto DEFINE } // start the worker // send initial work item q.Enqueue(newStructDef(t.name, reflect.ValueOf(def))) DEFINE: go func() { defineStruct(q, t.tagKeys, result, &wg) }() // collect the results until the resCh is closed for { val, ok := <-result if !ok { break } // TODO: returned values are being ignored; should n be checked here? err will always be nil buff.Write(val) } fmtd, err := format.Source(buff.Bytes()) if err != nil { return err } n, err = t.w.Write(fmtd) if err != nil { return err } if n != len(fmtd) { return ShortWriteError{n: len(fmtd), written: n, operation: "formatted Go code"} } return nil }