gorgonia.org/gorgonia@v0.9.17/formatter.go (about)

     1  package gorgonia
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  )
     8  
     9  type mapFmt struct {
    10  	m reflect.Value // map
    11  }
    12  
    13  // FmtNodeMap is a convenience function to print map[*Node]<T>
    14  //
    15  // The fmt flag that makes it all nicely formatted is "-". Because a map consists of two types (key's type
    16  // and val's type), and the Go fmt verb doesn't quite allow us to do something like "%ds", a hack is introduced
    17  // to enable nicer printing of map[*Node]<T>
    18  //
    19  // Here's the hack:
    20  // The "#" flag is used to indicate if the map will use the Node's ID or Name when formatting the map.
    21  //		%-v 	nodeName:%v
    22  //		%-#v	nodeID:%v
    23  //		%-d 	nodeName:%x
    24  //		%-#d 	nodeID: %x
    25  //		%-p 	nodeName:%p
    26  // 		%-#p	nodeID:%p
    27  //
    28  // If the "-" flag is not found, then the formatter returns the default Go format for map[<T>]<T2>
    29  func FmtNodeMap(m interface{}) mapFmt {
    30  	refVal := reflect.ValueOf(m)
    31  	if refVal.Kind() != reflect.Map {
    32  		panic("Only expect maps in FmtNodeMap")
    33  	}
    34  
    35  	t := refVal.Type()
    36  	keyType := t.Key()
    37  
    38  	var n *Node
    39  	if keyType != reflect.TypeOf(n) {
    40  		panic("Only expected map[*Node]<T>")
    41  	}
    42  
    43  	return mapFmt{
    44  		m: refVal,
    45  	}
    46  }
    47  
    48  func (mf mapFmt) defaultFmt(s fmt.State, c rune) {
    49  	var buf bytes.Buffer
    50  	buf.WriteRune('%')
    51  	for i := 0; i < 128; i++ {
    52  		if s.Flag(i) {
    53  			buf.WriteByte(byte(i))
    54  		}
    55  	}
    56  	if w, ok := s.Width(); ok {
    57  		fmt.Fprintf(&buf, "%d", w)
    58  	}
    59  	if p, ok := s.Precision(); ok {
    60  		fmt.Fprintf(&buf, ".%d", p)
    61  	}
    62  	buf.WriteRune(c)
    63  	fmt.Fprintf(s, buf.String(), mf.m)
    64  }
    65  
    66  func (mf mapFmt) format(s fmt.State, c rune) string {
    67  	var tmpl string
    68  	switch {
    69  	case c == 'v':
    70  		if s.Flag('#') {
    71  			tmpl = "\t%x: %v\n"
    72  		} else {
    73  			tmpl = "\t%s: %v\n"
    74  		}
    75  	case c == 'd':
    76  		if s.Flag('#') {
    77  			tmpl = "\t%x: %x\n"
    78  		} else {
    79  			tmpl = "\t%s: %x\n"
    80  		}
    81  	case c == 'p':
    82  		if s.Flag('#') {
    83  			tmpl = "\t%x: %p\n"
    84  		} else {
    85  			tmpl = "\t%s: %p\n"
    86  		}
    87  	default:
    88  		tmpl = "\t%s: %s\n"
    89  	}
    90  	return tmpl
    91  }
    92  
    93  func (mf mapFmt) Format(s fmt.State, c rune) {
    94  	refVal := mf.m
    95  	var n *Node
    96  	t := refVal.Type()
    97  	keyType := t.Key()
    98  	if keyType != reflect.TypeOf(n) {
    99  		panic("Only map[*Node]<T> is expected")
   100  	}
   101  
   102  	tmpl := mf.format(s, c)
   103  
   104  	keys := refVal.MapKeys()
   105  	if s.Flag('-') {
   106  		if s.Flag('#') {
   107  			// then key, will try its best to be a number
   108  
   109  			fmt.Fprintf(s, "map[Node.ID]%s {\n", t.Elem())
   110  			for i := 0; i < refVal.Len(); i++ {
   111  				key := keys[i]
   112  				val := refVal.MapIndex(key)
   113  
   114  				meth := key.MethodByName("ID")
   115  				id := meth.Call(nil)[0]
   116  
   117  				valType := val.Type()
   118  				if valType == reflect.TypeOf(n) {
   119  					switch c {
   120  					case 'd':
   121  						valMeth := val.MethodByName("ID")
   122  						valID := valMeth.Call(nil)[0]
   123  						fmt.Fprintf(s, tmpl, id, valID)
   124  					default:
   125  						strMeth := val.MethodByName("String")
   126  						str := strMeth.Call(nil)[0]
   127  						fmt.Fprintf(s, tmpl, id, str)
   128  
   129  					}
   130  				} else {
   131  					if _, ok := valType.MethodByName("Format"); ok {
   132  						fmt.Fprintf(s, "\t%x: ", id)
   133  						fmtMeth := val.MethodByName("Format")
   134  						fmtMeth.Call([]reflect.Value{reflect.ValueOf(s), reflect.ValueOf(c)})
   135  						fmt.Fprintf(s, "\n")
   136  					} else if _, ok := valType.MethodByName("String"); ok {
   137  						strMeth := val.MethodByName("String")
   138  						str := strMeth.Call(nil)[0]
   139  						fmt.Fprintf(s, tmpl, id, str)
   140  					} else {
   141  						fmt.Fprintf(s, tmpl, id, val)
   142  					}
   143  				}
   144  			}
   145  			s.Write([]byte("}"))
   146  
   147  		} else {
   148  			fmt.Fprintf(s, "map[Node.Name]%s {\n", t.Elem())
   149  			for i := 0; i < refVal.Len(); i++ {
   150  				key := keys[i]
   151  				val := refVal.MapIndex(key)
   152  
   153  				meth := key.MethodByName("String")
   154  				id := meth.Call(nil)[0]
   155  
   156  				valType := val.Type()
   157  				if valType == reflect.TypeOf(n) {
   158  					switch c {
   159  					case 'd':
   160  						valMeth := val.MethodByName("ID")
   161  						valID := valMeth.Call(nil)[0]
   162  						fmt.Fprintf(s, tmpl, id, valID)
   163  					default:
   164  						strMeth := val.MethodByName("String")
   165  						str := strMeth.Call(nil)[0]
   166  						fmt.Fprintf(s, tmpl, id, str)
   167  
   168  					}
   169  				} else {
   170  					if _, ok := valType.MethodByName("Format"); ok {
   171  						fmt.Fprintf(s, "\t%s: ", id)
   172  						fmtMeth := val.MethodByName("Format")
   173  						fmtMeth.Call([]reflect.Value{reflect.ValueOf(s), reflect.ValueOf(c)})
   174  						fmt.Fprintf(s, "\n")
   175  					} else if _, ok := valType.MethodByName("String"); ok {
   176  						strMeth := val.MethodByName("String")
   177  						str := strMeth.Call(nil)[0]
   178  						fmt.Fprintf(s, tmpl, id, str)
   179  					} else {
   180  						fmt.Fprintf(s, tmpl, id, val)
   181  					}
   182  				}
   183  			}
   184  			s.Write([]byte("}"))
   185  		}
   186  		return
   187  	}
   188  	mf.defaultFmt(s, c)
   189  }