Example #1
0
// 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
}
Example #2
0
// 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
}