package message
import (
"bytes"
"fmt"
"math"
"reflect"
"sync"
"unicode/utf8"
"golang.org/x/text/internal/format"
"golang.org/x/text/internal/number"
"golang.org/x/text/language"
"golang.org/x/text/message/catalog"
)
const (
commaSpaceString = ", "
nilAngleString = "<nil>"
nilParenString = "(nil)"
nilString = "nil"
mapString = "map["
percentBangString = "%!"
missingString = "(MISSING)"
badIndexString = "(BADINDEX)"
panicString = "(PANIC="
extraString = "%!(EXTRA "
badWidthString = "%!(BADWIDTH)"
badPrecString = "%!(BADPREC)"
noVerbString = "%!(NOVERB)"
invReflectString = "<invalid reflect.Value>"
)
var printerPool = sync .Pool {
New : func () interface {} { return new (printer ) },
}
func newPrinter(pp *Printer ) *printer {
p := printerPool .Get ().(*printer )
p .Printer = *pp
p .catContext = pp .cat .Context (pp .tag , p )
p .panicking = false
p .erroring = false
p .fmt .init (&p .Buffer )
return p
}
func (p *printer ) free () {
p .Buffer .Reset ()
p .arg = nil
p .value = reflect .Value {}
printerPool .Put (p )
}
type printer struct {
Printer
catContext *catalog .Context
bytes .Buffer
arg interface {}
value reflect .Value
fmt formatInfo
panicking bool
erroring bool
}
func (p *printer ) Language () language .Tag { return p .tag }
func (p *printer ) Width () (wid int , ok bool ) { return p .fmt .Width , p .fmt .WidthPresent }
func (p *printer ) Precision () (prec int , ok bool ) { return p .fmt .Prec , p .fmt .PrecPresent }
func (p *printer ) Flag (b int ) bool {
switch b {
case '-' :
return p .fmt .Minus
case '+' :
return p .fmt .Plus || p .fmt .PlusV
case '#' :
return p .fmt .Sharp || p .fmt .SharpV
case ' ' :
return p .fmt .Space
case '0' :
return p .fmt .Zero
}
return false
}
func getField(v reflect .Value , i int ) reflect .Value {
val := v .Field (i )
if val .Kind () == reflect .Interface && !val .IsNil () {
val = val .Elem ()
}
return val
}
func (p *printer ) unknownType (v reflect .Value ) {
if !v .IsValid () {
p .WriteString (nilAngleString )
return
}
p .WriteByte ('?' )
p .WriteString (v .Type ().String ())
p .WriteByte ('?' )
}
func (p *printer ) badVerb (verb rune ) {
p .erroring = true
p .WriteString (percentBangString )
p .WriteRune (verb )
p .WriteByte ('(' )
switch {
case p .arg != nil :
p .WriteString (reflect .TypeOf (p .arg ).String ())
p .WriteByte ('=' )
p .printArg (p .arg , 'v' )
case p .value .IsValid ():
p .WriteString (p .value .Type ().String ())
p .WriteByte ('=' )
p .printValue (p .value , 'v' , 0 )
default :
p .WriteString (nilAngleString )
}
p .WriteByte (')' )
p .erroring = false
}
func (p *printer ) fmtBool (v bool , verb rune ) {
switch verb {
case 't' , 'v' :
p .fmt .fmt_boolean (v )
default :
p .badVerb (verb )
}
}
func (p *printer ) fmt0x64 (v uint64 , leading0x bool ) {
sharp := p .fmt .Sharp
p .fmt .Sharp = leading0x
p .fmt .fmt_integer (v , 16 , unsigned , ldigits )
p .fmt .Sharp = sharp
}
func (p *printer ) fmtInteger (v uint64 , isSigned bool , verb rune ) {
switch verb {
case 'v' :
if p .fmt .SharpV && !isSigned {
p .fmt0x64 (v , true )
return
}
fallthrough
case 'd' :
if p .fmt .Sharp || p .fmt .SharpV {
p .fmt .fmt_integer (v , 10 , isSigned , ldigits )
} else {
p .fmtDecimalInt (v , isSigned )
}
case 'b' :
p .fmt .fmt_integer (v , 2 , isSigned , ldigits )
case 'o' :
p .fmt .fmt_integer (v , 8 , isSigned , ldigits )
case 'x' :
p .fmt .fmt_integer (v , 16 , isSigned , ldigits )
case 'X' :
p .fmt .fmt_integer (v , 16 , isSigned , udigits )
case 'c' :
p .fmt .fmt_c (v )
case 'q' :
if v <= utf8 .MaxRune {
p .fmt .fmt_qc (v )
} else {
p .badVerb (verb )
}
case 'U' :
p .fmt .fmt_unicode (v )
default :
p .badVerb (verb )
}
}
func (p *printer ) fmtFloat (v float64 , size int , verb rune ) {
switch verb {
case 'b' :
p .fmt .fmt_float (v , size , verb , -1 )
case 'v' :
verb = 'g'
fallthrough
case 'g' , 'G' :
if p .fmt .Sharp || p .fmt .SharpV {
p .fmt .fmt_float (v , size , verb , -1 )
} else {
p .fmtVariableFloat (v , size )
}
case 'e' , 'E' :
if p .fmt .Sharp || p .fmt .SharpV {
p .fmt .fmt_float (v , size , verb , 6 )
} else {
p .fmtScientific (v , size , 6 )
}
case 'f' , 'F' :
if p .fmt .Sharp || p .fmt .SharpV {
p .fmt .fmt_float (v , size , verb , 6 )
} else {
p .fmtDecimalFloat (v , size , 6 )
}
default :
p .badVerb (verb )
}
}
func (p *printer ) setFlags (f *number .Formatter ) {
f .Flags &^= number .ElideSign
if p .fmt .Plus || p .fmt .Space {
f .Flags |= number .AlwaysSign
if !p .fmt .Plus {
f .Flags |= number .ElideSign
}
} else {
f .Flags &^= number .AlwaysSign
}
}
func (p *printer ) updatePadding (f *number .Formatter ) {
f .Flags &^= number .PadMask
if p .fmt .Minus {
f .Flags |= number .PadAfterSuffix
} else {
f .Flags |= number .PadBeforePrefix
}
f .PadRune = ' '
f .FormatWidth = uint16 (p .fmt .Width )
}
func (p *printer ) initDecimal (minFrac , maxFrac int ) {
f := &p .toDecimal
f .MinIntegerDigits = 1
f .MaxIntegerDigits = 0
f .MinFractionDigits = uint8 (minFrac )
f .MaxFractionDigits = int16 (maxFrac )
p .setFlags (f )
f .PadRune = 0
if p .fmt .WidthPresent {
if p .fmt .Zero {
wid := p .fmt .Width
if f .MinFractionDigits > 0 {
wid -= 1 + int (f .MinFractionDigits )
}
if p .fmt .Plus || p .fmt .Space {
wid --
}
if wid > 0 && wid > int (f .MinIntegerDigits ) {
f .MinIntegerDigits = uint8 (wid )
}
}
p .updatePadding (f )
}
}
func (p *printer ) initScientific (minFrac , maxFrac int ) {
f := &p .toScientific
if maxFrac < 0 {
f .SetPrecision (maxFrac )
} else {
f .SetPrecision (maxFrac + 1 )
f .MinFractionDigits = uint8 (minFrac )
f .MaxFractionDigits = int16 (maxFrac )
}
f .MinExponentDigits = 2
p .setFlags (f )
f .PadRune = 0
if p .fmt .WidthPresent {
f .Flags &^= number .PadMask
if p .fmt .Zero {
f .PadRune = f .Digit (0 )
f .Flags |= number .PadAfterPrefix
} else {
f .PadRune = ' '
f .Flags |= number .PadBeforePrefix
}
p .updatePadding (f )
}
}
func (p *printer ) fmtDecimalInt (v uint64 , isSigned bool ) {
var d number .Decimal
f := &p .toDecimal
if p .fmt .PrecPresent {
p .setFlags (f )
f .MinIntegerDigits = uint8 (p .fmt .Prec )
f .MaxIntegerDigits = 0
f .MinFractionDigits = 0
f .MaxFractionDigits = 0
if p .fmt .WidthPresent {
p .updatePadding (f )
}
} else {
p .initDecimal (0 , 0 )
}
d .ConvertInt (p .toDecimal .RoundingContext , isSigned , v )
out := p .toDecimal .Format ([]byte (nil ), &d )
p .Buffer .Write (out )
}
func (p *printer ) fmtDecimalFloat (v float64 , size , prec int ) {
var d number .Decimal
if p .fmt .PrecPresent {
prec = p .fmt .Prec
}
p .initDecimal (prec , prec )
d .ConvertFloat (p .toDecimal .RoundingContext , v , size )
out := p .toDecimal .Format ([]byte (nil ), &d )
p .Buffer .Write (out )
}
func (p *printer ) fmtVariableFloat (v float64 , size int ) {
prec := -1
if p .fmt .PrecPresent {
prec = p .fmt .Prec
}
var d number .Decimal
p .initScientific (0 , prec )
d .ConvertFloat (p .toScientific .RoundingContext , v , size )
shortest := prec < 0
ePrec := prec
if shortest {
prec = len (d .Digits )
ePrec = 6
} else if prec == 0 {
prec = 1
ePrec = 1
}
exp := int (d .Exp ) - 1
if exp < -4 || exp >= ePrec {
p .initScientific (0 , prec )
out := p .toScientific .Format ([]byte (nil ), &d )
p .Buffer .Write (out )
} else {
if prec > int (d .Exp ) {
prec = len (d .Digits )
}
if prec -= int (d .Exp ); prec < 0 {
prec = 0
}
p .initDecimal (0 , prec )
out := p .toDecimal .Format ([]byte (nil ), &d )
p .Buffer .Write (out )
}
}
func (p *printer ) fmtScientific (v float64 , size , prec int ) {
var d number .Decimal
if p .fmt .PrecPresent {
prec = p .fmt .Prec
}
p .initScientific (prec , prec )
rc := p .toScientific .RoundingContext
d .ConvertFloat (rc , v , size )
out := p .toScientific .Format ([]byte (nil ), &d )
p .Buffer .Write (out )
}
func (p *printer ) fmtComplex (v complex128 , size int , verb rune ) {
switch verb {
case 'v' , 'b' , 'g' , 'G' , 'f' , 'F' , 'e' , 'E' :
p .WriteByte ('(' )
p .fmtFloat (real (v ), size /2 , verb )
if math .IsNaN (imag (v )) {
f := &p .toScientific
p .setFlags (f )
p .updatePadding (f )
p .setFlags (f )
nan := f .Symbol (number .SymNan )
extra := 0
if w , ok := p .Width (); ok {
extra = w - utf8 .RuneCountInString (nan ) - 1
}
if f .Flags &number .PadAfterNumber == 0 {
for ; extra > 0 ; extra -- {
p .WriteRune (f .PadRune )
}
}
p .WriteString (f .Symbol (number .SymPlusSign ))
p .WriteString (nan )
for ; extra > 0 ; extra -- {
p .WriteRune (f .PadRune )
}
p .WriteString ("i)" )
return
}
oldPlus := p .fmt .Plus
p .fmt .Plus = true
p .fmtFloat (imag (v ), size /2 , verb )
p .WriteString ("i)" )
p .fmt .Plus = oldPlus
default :
p .badVerb (verb )
}
}
func (p *printer ) fmtString (v string , verb rune ) {
switch verb {
case 'v' :
if p .fmt .SharpV {
p .fmt .fmt_q (v )
} else {
p .fmt .fmt_s (v )
}
case 's' :
p .fmt .fmt_s (v )
case 'x' :
p .fmt .fmt_sx (v , ldigits )
case 'X' :
p .fmt .fmt_sx (v , udigits )
case 'q' :
p .fmt .fmt_q (v )
case 'm' :
ctx := p .cat .Context (p .tag , rawPrinter {p })
if ctx .Execute (v ) == catalog .ErrNotFound {
p .WriteString (v )
}
default :
p .badVerb (verb )
}
}
func (p *printer ) fmtBytes (v []byte , verb rune , typeString string ) {
switch verb {
case 'v' , 'd' :
if p .fmt .SharpV {
p .WriteString (typeString )
if v == nil {
p .WriteString (nilParenString )
return
}
p .WriteByte ('{' )
for i , c := range v {
if i > 0 {
p .WriteString (commaSpaceString )
}
p .fmt0x64 (uint64 (c ), true )
}
p .WriteByte ('}' )
} else {
p .WriteByte ('[' )
for i , c := range v {
if i > 0 {
p .WriteByte (' ' )
}
p .fmt .fmt_integer (uint64 (c ), 10 , unsigned , ldigits )
}
p .WriteByte (']' )
}
case 's' :
p .fmt .fmt_s (string (v ))
case 'x' :
p .fmt .fmt_bx (v , ldigits )
case 'X' :
p .fmt .fmt_bx (v , udigits )
case 'q' :
p .fmt .fmt_q (string (v ))
default :
p .printValue (reflect .ValueOf (v ), verb , 0 )
}
}
func (p *printer ) fmtPointer (value reflect .Value , verb rune ) {
var u uintptr
switch value .Kind () {
case reflect .Chan , reflect .Func , reflect .Map , reflect .Ptr , reflect .Slice , reflect .UnsafePointer :
u = value .Pointer ()
default :
p .badVerb (verb )
return
}
switch verb {
case 'v' :
if p .fmt .SharpV {
p .WriteByte ('(' )
p .WriteString (value .Type ().String ())
p .WriteString (")(" )
if u == 0 {
p .WriteString (nilString )
} else {
p .fmt0x64 (uint64 (u ), true )
}
p .WriteByte (')' )
} else {
if u == 0 {
p .fmt .padString (nilAngleString )
} else {
p .fmt0x64 (uint64 (u ), !p .fmt .Sharp )
}
}
case 'p' :
p .fmt0x64 (uint64 (u ), !p .fmt .Sharp )
case 'b' , 'o' , 'd' , 'x' , 'X' :
if verb == 'd' {
p .fmt .Sharp = true
}
p .fmtInteger (uint64 (u ), unsigned , verb )
default :
p .badVerb (verb )
}
}
func (p *printer ) catchPanic (arg interface {}, verb rune ) {
if err := recover (); err != nil {
if v := reflect .ValueOf (arg ); v .Kind () == reflect .Ptr && v .IsNil () {
p .WriteString (nilAngleString )
return
}
if p .panicking {
panic (err )
}
oldFlags := p .fmt .Parser
p .fmt .ClearFlags ()
p .WriteString (percentBangString )
p .WriteRune (verb )
p .WriteString (panicString )
p .panicking = true
p .printArg (err , 'v' )
p .panicking = false
p .WriteByte (')' )
p .fmt .Parser = oldFlags
}
}
func (p *printer ) handleMethods (verb rune ) (handled bool ) {
if p .erroring {
return
}
if formatter , ok := p .arg .(format .Formatter ); ok {
handled = true
defer p .catchPanic (p .arg , verb )
formatter .Format (p , verb )
return
}
if formatter , ok := p .arg .(fmt .Formatter ); ok {
handled = true
defer p .catchPanic (p .arg , verb )
formatter .Format (p , verb )
return
}
if p .fmt .SharpV {
if stringer , ok := p .arg .(fmt .GoStringer ); ok {
handled = true
defer p .catchPanic (p .arg , verb )
p .fmt .fmt_s (stringer .GoString ())
return
}
} else {
switch verb {
case 'v' , 's' , 'x' , 'X' , 'q' :
switch v := p .arg .(type ) {
case error :
handled = true
defer p .catchPanic (p .arg , verb )
p .fmtString (v .Error(), verb )
return
case fmt .Stringer :
handled = true
defer p .catchPanic (p .arg , verb )
p .fmtString (v .String (), verb )
return
}
}
}
return false
}
func (p *printer ) printArg (arg interface {}, verb rune ) {
p .arg = arg
p .value = reflect .Value {}
if arg == nil {
switch verb {
case 'T' , 'v' :
p .fmt .padString (nilAngleString )
default :
p .badVerb (verb )
}
return
}
switch verb {
case 'T' :
p .fmt .fmt_s (reflect .TypeOf (arg ).String ())
return
case 'p' :
p .fmtPointer (reflect .ValueOf (arg ), 'p' )
return
}
switch f := arg .(type ) {
case bool :
p .fmtBool (f , verb )
case float32 :
p .fmtFloat (float64 (f ), 32 , verb )
case float64 :
p .fmtFloat (f , 64 , verb )
case complex64 :
p .fmtComplex (complex128 (f ), 64 , verb )
case complex128 :
p .fmtComplex (f , 128 , verb )
case int :
p .fmtInteger (uint64 (f ), signed , verb )
case int8 :
p .fmtInteger (uint64 (f ), signed , verb )
case int16 :
p .fmtInteger (uint64 (f ), signed , verb )
case int32 :
p .fmtInteger (uint64 (f ), signed , verb )
case int64 :
p .fmtInteger (uint64 (f ), signed , verb )
case uint :
p .fmtInteger (uint64 (f ), unsigned , verb )
case uint8 :
p .fmtInteger (uint64 (f ), unsigned , verb )
case uint16 :
p .fmtInteger (uint64 (f ), unsigned , verb )
case uint32 :
p .fmtInteger (uint64 (f ), unsigned , verb )
case uint64 :
p .fmtInteger (f , unsigned , verb )
case uintptr :
p .fmtInteger (uint64 (f ), unsigned , verb )
case string :
p .fmtString (f , verb )
case []byte :
p .fmtBytes (f , verb , "[]byte" )
case reflect .Value :
if f .IsValid () && f .CanInterface () {
p .arg = f .Interface ()
if p .handleMethods (verb ) {
return
}
}
p .printValue (f , verb , 0 )
default :
if !p .handleMethods (verb ) {
p .printValue (reflect .ValueOf (f ), verb , 0 )
}
}
}
func (p *printer ) printValue (value reflect .Value , verb rune , depth int ) {
if depth > 0 && value .IsValid () && value .CanInterface () {
p .arg = value .Interface ()
if p .handleMethods (verb ) {
return
}
}
p .arg = nil
p .value = value
switch f := value ; value .Kind () {
case reflect .Invalid :
if depth == 0 {
p .WriteString (invReflectString )
} else {
switch verb {
case 'v' :
p .WriteString (nilAngleString )
default :
p .badVerb (verb )
}
}
case reflect .Bool :
p .fmtBool (f .Bool (), verb )
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
p .fmtInteger (uint64 (f .Int ()), signed , verb )
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
p .fmtInteger (f .Uint (), unsigned , verb )
case reflect .Float32 :
p .fmtFloat (f .Float (), 32 , verb )
case reflect .Float64 :
p .fmtFloat (f .Float (), 64 , verb )
case reflect .Complex64 :
p .fmtComplex (f .Complex (), 64 , verb )
case reflect .Complex128 :
p .fmtComplex (f .Complex (), 128 , verb )
case reflect .String :
p .fmtString (f .String (), verb )
case reflect .Map :
if p .fmt .SharpV {
p .WriteString (f .Type ().String ())
if f .IsNil () {
p .WriteString (nilParenString )
return
}
p .WriteByte ('{' )
} else {
p .WriteString (mapString )
}
keys := f .MapKeys ()
for i , key := range keys {
if i > 0 {
if p .fmt .SharpV {
p .WriteString (commaSpaceString )
} else {
p .WriteByte (' ' )
}
}
p .printValue (key , verb , depth +1 )
p .WriteByte (':' )
p .printValue (f .MapIndex (key ), verb , depth +1 )
}
if p .fmt .SharpV {
p .WriteByte ('}' )
} else {
p .WriteByte (']' )
}
case reflect .Struct :
if p .fmt .SharpV {
p .WriteString (f .Type ().String ())
}
p .WriteByte ('{' )
for i := 0 ; i < f .NumField (); i ++ {
if i > 0 {
if p .fmt .SharpV {
p .WriteString (commaSpaceString )
} else {
p .WriteByte (' ' )
}
}
if p .fmt .PlusV || p .fmt .SharpV {
if name := f .Type ().Field (i ).Name ; name != "" {
p .WriteString (name )
p .WriteByte (':' )
}
}
p .printValue (getField (f , i ), verb , depth +1 )
}
p .WriteByte ('}' )
case reflect .Interface :
value := f .Elem ()
if !value .IsValid () {
if p .fmt .SharpV {
p .WriteString (f .Type ().String ())
p .WriteString (nilParenString )
} else {
p .WriteString (nilAngleString )
}
} else {
p .printValue (value , verb , depth +1 )
}
case reflect .Array , reflect .Slice :
switch verb {
case 's' , 'q' , 'x' , 'X' :
t := f .Type ()
if t .Elem ().Kind () == reflect .Uint8 {
var bytes []byte
if f .Kind () == reflect .Slice {
bytes = f .Bytes ()
} else if f .CanAddr () {
bytes = f .Slice (0 , f .Len ()).Bytes ()
} else {
bytes = make ([]byte , f .Len ())
for i := range bytes {
bytes [i ] = byte (f .Index (i ).Uint ())
}
}
p .fmtBytes (bytes , verb , t .String ())
return
}
}
if p .fmt .SharpV {
p .WriteString (f .Type ().String ())
if f .Kind () == reflect .Slice && f .IsNil () {
p .WriteString (nilParenString )
return
}
p .WriteByte ('{' )
for i := 0 ; i < f .Len (); i ++ {
if i > 0 {
p .WriteString (commaSpaceString )
}
p .printValue (f .Index (i ), verb , depth +1 )
}
p .WriteByte ('}' )
} else {
p .WriteByte ('[' )
for i := 0 ; i < f .Len (); i ++ {
if i > 0 {
p .WriteByte (' ' )
}
p .printValue (f .Index (i ), verb , depth +1 )
}
p .WriteByte (']' )
}
case reflect .Ptr :
if depth == 0 && f .Pointer () != 0 {
switch a := f .Elem (); a .Kind () {
case reflect .Array , reflect .Slice , reflect .Struct , reflect .Map :
p .WriteByte ('&' )
p .printValue (a , verb , depth +1 )
return
}
}
fallthrough
case reflect .Chan , reflect .Func , reflect .UnsafePointer :
p .fmtPointer (f , verb )
default :
p .unknownType (f )
}
}
func (p *printer ) badArgNum (verb rune ) {
p .WriteString (percentBangString )
p .WriteRune (verb )
p .WriteString (badIndexString )
}
func (p *printer ) missingArg (verb rune ) {
p .WriteString (percentBangString )
p .WriteRune (verb )
p .WriteString (missingString )
}
func (p *printer ) doPrintf (fmt string ) {
for p .fmt .Parser .SetFormat (fmt ); p .fmt .Scan (); {
switch p .fmt .Status {
case format .StatusText :
p .WriteString (p .fmt .Text ())
case format .StatusSubstitution :
p .printArg (p .Arg (p .fmt .ArgNum ), p .fmt .Verb )
case format .StatusBadWidthSubstitution :
p .WriteString (badWidthString )
p .printArg (p .Arg (p .fmt .ArgNum ), p .fmt .Verb )
case format .StatusBadPrecSubstitution :
p .WriteString (badPrecString )
p .printArg (p .Arg (p .fmt .ArgNum ), p .fmt .Verb )
case format .StatusNoVerb :
p .WriteString (noVerbString )
case format .StatusBadArgNum :
p .badArgNum (p .fmt .Verb )
case format .StatusMissingArg :
p .missingArg (p .fmt .Verb )
default :
panic ("unreachable" )
}
}
if !p .fmt .Reordered && p .fmt .ArgNum < len (p .fmt .Args ) && p .fmt .ArgNum != 0 {
p .fmt .ClearFlags ()
p .WriteString (extraString )
for i , arg := range p .fmt .Args [p .fmt .ArgNum :] {
if i > 0 {
p .WriteString (commaSpaceString )
}
if arg == nil {
p .WriteString (nilAngleString )
} else {
p .WriteString (reflect .TypeOf (arg ).String ())
p .WriteString ("=" )
p .printArg (arg , 'v' )
}
}
p .WriteByte (')' )
}
}
func (p *printer ) doPrint (a []interface {}) {
prevString := false
for argNum , arg := range a {
isString := arg != nil && reflect .TypeOf (arg ).Kind () == reflect .String
if argNum > 0 && !isString && !prevString {
p .WriteByte (' ' )
}
p .printArg (arg , 'v' )
prevString = isString
}
}
func (p *printer ) doPrintln (a []interface {}) {
for argNum , arg := range a {
if argNum > 0 {
p .WriteByte (' ' )
}
p .printArg (arg , 'v' )
}
p .WriteByte ('\n' )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .