text: // Copyright 2013 The go-github AUTHORS. All rights reserved.
  //
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package github
  
  import (
  	"bytes"
  	"fmt"
  	"io"
  
  	"reflect"
  )
  
  var timestampType = reflect.TypeOf(Timestamp{})
  
  // Stringify attempts to create a reasonable string representation of types in
  // the GitHub library.  It does things like resolve pointers to their values
  // and omits struct fields with nil values.
  func Stringify(message interface{}) string {
  	var buf bytes.Buffer
  	v := reflect.ValueOf(message)
  	stringifyValue(&buf, v)
  	return buf.String()
  }
  
  // stringifyValue was heavily inspired by the goprotobuf library.
  
  func stringifyValue(w io.Writer, val reflect.Value) {
  	if val.Kind() == reflect.Ptr && val.IsNil() {
  		w.Write([]byte("<nil>"))
  		return
  	}
  
  	v := reflect.Indirect(val)
  
  	switch v.Kind() {
  	case reflect.String:
  		fmt.Fprintf(w, `"%s"`, v)
  	case reflect.Slice:
  		w.Write([]byte{'['})
  		for i := 0; i < v.Len(); i++ {
  			if i > 0 {
  				w.Write([]byte{' '})
  			}
  
  			stringifyValue(w, v.Index(i))
  		}
  
  		w.Write([]byte{']'})
  		return
  	case reflect.Struct:
  		if v.Type().Name() != "" {
  			w.Write([]byte(v.Type().String()))
  		}
  
  		// special handling of Timestamp values
  		if v.Type() == timestampType {
  			fmt.Fprintf(w, "{%s}", v.Interface())
  			return
  		}
  
  		w.Write([]byte{'{'})
  
  		var sep bool
  		for i := 0; i < v.NumField(); i++ {
  			fv := v.Field(i)
  			if fv.Kind() == reflect.Ptr && fv.IsNil() {
  				continue
  			}
  			if fv.Kind() == reflect.Slice && fv.IsNil() {
  				continue
  			}
  
  			if sep {
  				w.Write([]byte(", "))
  			} else {
  				sep = true
  			}
  
  			w.Write([]byte(v.Type().Field(i).Name))
  			w.Write([]byte{':'})
  			stringifyValue(w, fv)
  		}
  
  		w.Write([]byte{'}'})
  	default:
  		if v.CanInterface() {
  			fmt.Fprint(w, v.Interface())
  		}
  	}
  }
  