diff --git a/formatter/formatter.go b/formatter/formatter.go index 8e71051..d88622a 100644 --- a/formatter/formatter.go +++ b/formatter/formatter.go @@ -5,6 +5,11 @@ package formatter import ( + "fmt" + + "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/strutil" + "github.com/duke-git/lancet/v2/validator" "golang.org/x/exp/constraints" ) @@ -13,57 +18,29 @@ import ( // Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345" // Play: https://go.dev/play/p/eRD5k2vzUVX func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string { - // s, err := numberToString(value) - // if err != nil { - // return "" - // } + if validator.IsInt(value) { + v, err := convertor.ToInt(value) + if err != nil { + return "" + } + return symbol + commaInt(v) + } - // dotIndex := strings.Index(s, ".") - // if dotIndex != -1 { - // return symbol + commaString(s[:dotIndex]) + s[dotIndex:] - // } + if validator.IsFloat(value) { + v, err := convertor.ToFloat(value) + if err != nil { + return "" + } + return symbol + commaFloat(v) + } - // return symbol + commaString(s) + if strutil.IsString(value) { + v := fmt.Sprintf("%v", value) + if validator.IsNumberStr(v) { + return symbol + commaStr(v) + } + return "" + } return "" - } - -// func commaString(s string) string { -// if len(s) <= 3 { -// return s -// } -// return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:]) -// } - -// func numberToString(value any) (string, error) { -// switch reflect.TypeOf(value).Kind() { -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, -// reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: -// return fmt.Sprintf("%v", value), nil - -// // todo: need to handle 12345678.9 => 1.23456789e+07 -// case reflect.Float32, reflect.Float64: -// return fmt.Sprintf("%v", value), nil - -// case reflect.String: -// { -// sv := fmt.Sprintf("%v", value) -// if strings.Contains(sv, ".") { -// _, err := strconv.ParseFloat(sv, 64) -// if err != nil { -// return "", err -// } -// return sv, nil -// } else { -// _, err := strconv.ParseInt(sv, 10, 64) -// if err != nil { -// return "", nil -// } -// return sv, nil -// } -// } -// default: -// return "", nil -// } -// } diff --git a/formatter/formatter_internal.go b/formatter/formatter_internal.go new file mode 100644 index 0000000..a602ac6 --- /dev/null +++ b/formatter/formatter_internal.go @@ -0,0 +1,85 @@ +package formatter + +import ( + "bytes" + "math" + "strconv" + "strings" +) + +// see https://github.com/dustin/go-humanize/blob/master/comma.go +func commaInt(v int64) string { + sign := "" + + // Min int64 can't be negated to a usable value, so it has to be special cased. + if v == math.MinInt64 { + return "-9,223,372,036,854,775,808" + } + + if v < 0 { + sign = "-" + v = 0 - v + } + + parts := []string{"", "", "", "", "", "", ""} + j := len(parts) - 1 + + for v > 999 { + parts[j] = strconv.FormatInt(v%1000, 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + v = v / 1000 + j-- + } + parts[j] = strconv.Itoa(int(v)) + return sign + strings.Join(parts[j:], ",") +} + +func commaFloat(v float64) string { + buf := &bytes.Buffer{} + if v < 0 { + buf.Write([]byte{'-'}) + v = 0 - v + } + + comma := []byte{','} + + parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} + +func commaStr(s string) string { + dotIndex := strings.Index(s, ".") + if dotIndex != -1 { + return commaStrRecursive(s[:dotIndex]) + s[dotIndex:] + } + + return commaStrRecursive(s) +} + +func commaStrRecursive(s string) string { + if len(s) <= 3 { + return s + } + return commaStrRecursive(s[:len(s)-3]) + "," + commaStrRecursive(s[len(s)-3:]) +} diff --git a/formatter/formatter_test.go b/formatter/formatter_test.go index 5f82148..0ec05d9 100644 --- a/formatter/formatter_test.go +++ b/formatter/formatter_test.go @@ -23,6 +23,6 @@ func TestComma(t *testing.T) { assert.Equal("¥12,345", Comma(12345, "¥")) assert.Equal("12,345.6789", Comma(12345.6789, "")) assert.Equal("12,345.6789", Comma(+12345.6789, "")) - // assert.Equal("12,345,678.9", Comma(12345678.9, "")) + assert.Equal("12,345,678.9", Comma(12345678.9, "")) assert.Equal("123,456,789,000", Comma(123456789000, "")) }