New Upstream Release - golang-github-k0kubun-pp
Ready changes
Summary
Merged new upstream version: 3.2.0 (was: 3.0.7).
Resulting package
Built on 2023-04-10T08:29 (took 15m36s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases golang-github-k0kubun-pp-dev
Lintian Result
Diff
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..36c8bcb
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: k0kubun
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..202ae23
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "gomod"
+ directory: "/"
+ schedule:
+ interval: "monthly"
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 112436b..9023e85 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -18,6 +18,8 @@ jobs:
- 1.13
- 1.14
- 1.15
+ - 1.16
+ - 1.17
fail-fast: false
name: Build
runs-on: ubuntu-latest
diff --git a/.github_changelog_generator b/.github_changelog_generator
new file mode 100644
index 0000000..caec759
--- /dev/null
+++ b/.github_changelog_generator
@@ -0,0 +1,2 @@
+user=k0kubun
+project=pp
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9586a5e..bb53179 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,87 @@
# Changelog
-## [Unreleased](https://github.com/k0kubun/pp/tree/HEAD)
+## [v3.2.0](https://github.com/k0kubun/pp/tree/v3.2.0)
-[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.6...HEAD)
+[Full Changelog](https://github.com/k0kubun/pp/compare/v3.1.0...v3.2.0)
+
+**Closed issues:**
+
+- ignore private fields in a struct in pretty print golang [\#74](https://github.com/k0kubun/pp/issues/74)
+- Bug?: Colorize map field names/keys using "FieldName" color scheme setting [\#72](https://github.com/k0kubun/pp/issues/72)
+- Printf not work correctly [\#70](https://github.com/k0kubun/pp/issues/70)
+- How to use the latest version? [\#69](https://github.com/k0kubun/pp/issues/69)
+- Please provide a simple way to disable color [\#67](https://github.com/k0kubun/pp/issues/67)
+- disable printing of struct metadata [\#66](https://github.com/k0kubun/pp/issues/66)
+
+**Merged pull requests:**
+
+- Expose defaultPrettyPrinter as pp.Default [\#75](https://github.com/k0kubun/pp/pull/75) ([k0kubun](https://github.com/k0kubun))
+- Bump github.com/mattn/go-colorable from 0.1.12 to 0.1.13 [\#73](https://github.com/k0kubun/pp/pull/73) ([dependabot[bot]](https://github.com/apps/dependabot))
+- doc: fix pp.go arguments word typo [\#68](https://github.com/k0kubun/pp/pull/68) ([rasecoiac03](https://github.com/rasecoiac03))
+
+## [v3.1.0](https://github.com/k0kubun/pp/tree/v3.1.0) (2022-01-06)
+
+[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.10...v3.1.0)
+
+**Merged pull requests:**
+
+- Use SetDecimalUnit\(true\) by default [\#65](https://github.com/k0kubun/pp/pull/65) ([k0kubun](https://github.com/k0kubun))
+
+## [v3.0.10](https://github.com/k0kubun/pp/tree/v3.0.10) (2022-01-06)
+
+[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.9...v3.0.10)
+
+**Merged pull requests:**
+
+- Remove k0kubun/colorstring from dependencies [\#64](https://github.com/k0kubun/pp/pull/64) ([k0kubun](https://github.com/k0kubun))
+
+## [v3.0.9](https://github.com/k0kubun/pp/tree/v3.0.9) (2022-01-06)
+
+[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.8...v3.0.9)
+
+**Merged pull requests:**
+
+- Bump github.com/mattn/go-colorable from 0.1.7 to 0.1.12 [\#63](https://github.com/k0kubun/pp/pull/63) ([dependabot[bot]](https://github.com/apps/dependabot))
+
+## [v3.0.8](https://github.com/k0kubun/pp/tree/v3.0.8) (2021-11-30)
+
+[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.7...v3.0.8)
+
+**Closed issues:**
+
+- Add option to skip unexported fields [\#59](https://github.com/k0kubun/pp/issues/59)
+- Verb '%T' is working incorrectly in formatted print [\#58](https://github.com/k0kubun/pp/issues/58)
+- Option to print byte as decimal [\#54](https://github.com/k0kubun/pp/issues/54)
+- Option for thousands separator [\#53](https://github.com/k0kubun/pp/issues/53)
+- The color part of vim is messy [\#52](https://github.com/k0kubun/pp/issues/52)
+- Bug: `printTime` assumes the standard library time [\#47](https://github.com/k0kubun/pp/issues/47)
+- Feature request: Add `pp:"noprint"` tag [\#42](https://github.com/k0kubun/pp/issues/42)
+- Feature request: omitempty [\#22](https://github.com/k0kubun/pp/issues/22)
+- Unable to print invalid address [\#21](https://github.com/k0kubun/pp/issues/21)
+
+**Merged pull requests:**
+
+- build: upgrade `go` directive in `go.mod` to 1.17 [\#62](https://github.com/k0kubun/pp/pull/62) ([Juneezee](https://github.com/Juneezee))
+- SetThousandsSeparator [\#61](https://github.com/k0kubun/pp/pull/61) ([mariusgrigoriu](https://github.com/mariusgrigoriu))
+- Add ExportedOnly option [\#60](https://github.com/k0kubun/pp/pull/60) ([k0kubun](https://github.com/k0kubun))
+- Add SetDecimalUint option [\#55](https://github.com/k0kubun/pp/pull/55) ([k0kubun](https://github.com/k0kubun))
+- Add Badge for pkg.go.dev [\#51](https://github.com/k0kubun/pp/pull/51) ([zakuro9715](https://github.com/zakuro9715))
+- Fix typo in README.md [\#50](https://github.com/k0kubun/pp/pull/50) ([bl-ue](https://github.com/bl-ue))
+- Add Struct Tag Support for Printing [\#49](https://github.com/k0kubun/pp/pull/49) ([rickbau5](https://github.com/rickbau5))
+- Ensure time.Time is from standard library [\#48](https://github.com/k0kubun/pp/pull/48) ([bquenin](https://github.com/bquenin))
+
+## [v3.0.7](https://github.com/k0kubun/pp/tree/v3.0.7) (2020-11-18)
+
+[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.6...v3.0.7)
**Closed issues:**
- Sort map by key before printing [\#23](https://github.com/k0kubun/pp/issues/23)
+**Merged pull requests:**
+
+- Resurrect Go 1.11 support [\#46](https://github.com/k0kubun/pp/pull/46) ([k0kubun](https://github.com/k0kubun))
+
## [v3.0.6](https://github.com/k0kubun/pp/tree/v3.0.6) (2020-11-14)
[Full Changelog](https://github.com/k0kubun/pp/compare/v3.0.5...v3.0.6)
@@ -96,7 +170,7 @@
**Merged pull requests:**
-- check whether reflect.Value can call `Interface\(\)` [\#19](https://github.com/k0kubun/pp/pull/19) ([skatsuta](https://github.com/skatsuta))
+- check whether reflect.Value can call `Interface()` [\#19](https://github.com/k0kubun/pp/pull/19) ([skatsuta](https://github.com/skatsuta))
- Fix indent for slices [\#18](https://github.com/k0kubun/pp/pull/18) ([sdidyk](https://github.com/sdidyk))
## [v2.1.0](https://github.com/k0kubun/pp/tree/v2.1.0) (2015-04-25)
diff --git a/README.md b/README.md
index 6e346b4..c9b2a99 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# pp [![Go](https://github.com/k0kubun/pp/workflows/Go/badge.svg)](https://github.com/k0kubun/pp/actions)
+# pp [![Go](https://github.com/k0kubun/pp/workflows/Go/badge.svg)](https://github.com/k0kubun/pp/actions) [![Go Reference](https://pkg.go.dev/badge/github.com/k0kubun/pp/v3.svg)](https://pkg.go.dev/github.com/k0kubun/pp/v3)
Colored pretty printer for Go language
@@ -9,7 +9,7 @@ Colored pretty printer for Go language
Just call `pp.Print()`.
```go
-import "github.com/k0kubun/pp"
+import "github.com/k0kubun/pp/v3"
m := map[string]string{"foo": "bar", "hello": "world"}
pp.Print(m)
@@ -29,7 +29,8 @@ pp.Fprintf()
// ...
```
-You can also create own instances that do not interfer with the default printer
+You can also create own instances that do not interfere with the default printer:
+
```go
mypp := pp.New()
mypp.SetOutput(os.Stderr)
@@ -37,8 +38,6 @@ mypp.Println()
// ...
```
-API doc is available at: http://godoc.org/github.com/k0kubun/pp
-
### Custom colors
If you require, you may change the colors (all or some) for syntax highlighting:
diff --git a/color.go b/color.go
index 0fe6a58..59a6724 100644
--- a/color.go
+++ b/color.go
@@ -1,3 +1,4 @@
+// color.go: Color API and implementation
package pp
import (
diff --git a/debian/changelog b/debian/changelog
index 16c057e..b0b6895 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-k0kubun-pp (3.2.0-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Mon, 10 Apr 2023 08:14:14 -0000
+
golang-github-k0kubun-pp (3.0.7-1) unstable; urgency=medium
* New upstream release.
diff --git a/go.mod b/go.mod
index dc08ac6..7d1bb24 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,13 @@
module github.com/k0kubun/pp/v3
-go 1.14
+go 1.17
require (
- github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88
- github.com/mattn/go-colorable v0.1.7
+ github.com/mattn/go-colorable v0.1.13
+ golang.org/x/text v0.3.7
+)
+
+require (
+ github.com/mattn/go-isatty v0.0.16 // indirect
+ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
)
diff --git a/go.sum b/go.sum
index 4104c7d..2c08622 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,9 @@
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
-github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
-github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/pp.go b/pp.go
index e2c2406..bac9958 100644
--- a/pp.go
+++ b/pp.go
@@ -1,3 +1,4 @@
+// pp.go: API definitions. The core implementation is delegated to printer.go.
package pp
import (
@@ -11,22 +12,43 @@ import (
"github.com/mattn/go-colorable"
)
+// Global variable API
+// see also: color.go
var (
- defaultOut = colorable.NewColorableStdout()
- defaultWithLineInfo = false
- defaultPrettyPrinter = newPrettyPrinter(3) // pp.* => PrettyPrinter.* => formatAll
+ // Default pretty printer. It's public so that you can modify config globally.
+ Default = newPrettyPrinter(3) // pp.* => PrettyPrinter.* => formatAll
+ // If the length of array or slice is larger than this,
+ // the buffer will be shorten as {...}.
+ BufferFoldThreshold = 1024
+ // PrintMapTypes when set to true will have map types will always appended to maps.
+ PrintMapTypes = true
+ // WithLineInfo add file name and line information to output
+ // call this function with care, because getting stack has performance penalty
+ WithLineInfo bool
+)
+
+// Internals
+var (
+ defaultOut = colorable.NewColorableStdout()
+ defaultWithLineInfo = false
)
type PrettyPrinter struct {
- out io.Writer
- currentScheme ColorScheme
- // WithLineInfo add file name and line information to output
- // call this function with care, because getting stack has performance penalty
- WithLineInfo bool
- outLock sync.Mutex
- maxDepth int
- coloringEnabled bool
- callerLevel int
+ // WithLineInfo adds file name and line information to output.
+ // Call this function with care, because getting stack has performance penalty.
+ WithLineInfo bool
+ // To support WithLineInfo, we need to know which frame we should look at.
+ // Thus callerLevel sets the number of frames it needs to skip.
+ callerLevel int
+ out io.Writer
+ currentScheme ColorScheme
+ outLock sync.Mutex
+ maxDepth int
+ coloringEnabled bool
+ decimalUint bool
+ thousandsSeparator bool
+ // This skips unexported fields of structs.
+ exportedOnly bool
}
// New creates a new PrettyPrinter that can be used to pretty print values
@@ -36,12 +58,14 @@ func New() *PrettyPrinter {
func newPrettyPrinter(callerLevel int) *PrettyPrinter {
return &PrettyPrinter{
+ WithLineInfo: defaultWithLineInfo,
+ callerLevel: callerLevel,
out: defaultOut,
currentScheme: defaultScheme,
- WithLineInfo: defaultWithLineInfo,
maxDepth: -1,
coloringEnabled: true,
- callerLevel: callerLevel,
+ decimalUint: true,
+ exportedOnly: false,
}
}
@@ -60,7 +84,7 @@ func (pp *PrettyPrinter) Println(a ...interface{}) (n int, err error) {
return fmt.Fprintln(pp.out, pp.formatAll(a)...)
}
-// Sprint formats given arguemnts and returns the result as string.
+// Sprint formats given arguments and returns the result as string.
func (pp *PrettyPrinter) Sprint(a ...interface{}) string {
return fmt.Sprint(pp.formatAll(a)...)
}
@@ -70,7 +94,7 @@ func (pp *PrettyPrinter) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, pp.formatAll(a)...)
}
-// Sprintln formats given arguemnts with newline and returns the result as string.
+// Sprintln formats given arguments with newline and returns the result as string.
func (pp *PrettyPrinter) Sprintln(a ...interface{}) string {
return fmt.Sprintln(pp.formatAll(a)...)
}
@@ -117,6 +141,18 @@ func (pp *PrettyPrinter) SetColoringEnabled(enabled bool) {
pp.coloringEnabled = enabled
}
+func (pp *PrettyPrinter) SetDecimalUint(enabled bool) {
+ pp.decimalUint = enabled
+}
+
+func (pp *PrettyPrinter) SetExportedOnly(enabled bool) {
+ pp.exportedOnly = enabled
+}
+
+func (pp *PrettyPrinter) SetThousandsSeparator(enabled bool) {
+ pp.thousandsSeparator = enabled
+}
+
// SetOutput sets pp's output
func (pp *PrettyPrinter) SetOutput(o io.Writer) {
pp.outLock.Lock()
@@ -154,7 +190,7 @@ func (pp *PrettyPrinter) formatAll(objects []interface{}) []interface{} {
// fix for backwards capability
withLineInfo := pp.WithLineInfo
- if pp == defaultPrettyPrinter {
+ if pp == Default {
withLineInfo = WithLineInfo
}
@@ -171,67 +207,67 @@ func (pp *PrettyPrinter) formatAll(objects []interface{}) []interface{} {
// Print prints given arguments.
func Print(a ...interface{}) (n int, err error) {
- return defaultPrettyPrinter.Print(a...)
+ return Default.Print(a...)
}
// Printf prints a given format.
func Printf(format string, a ...interface{}) (n int, err error) {
- return defaultPrettyPrinter.Printf(format, a...)
+ return Default.Printf(format, a...)
}
// Println prints given arguments with newline.
func Println(a ...interface{}) (n int, err error) {
- return defaultPrettyPrinter.Println(a...)
+ return Default.Println(a...)
}
-// Sprint formats given arguemnts and returns the result as string.
+// Sprint formats given arguments and returns the result as string.
func Sprint(a ...interface{}) string {
- return defaultPrettyPrinter.Sprint(a...)
+ return Default.Sprint(a...)
}
// Sprintf formats with pretty print and returns the result as string.
func Sprintf(format string, a ...interface{}) string {
- return defaultPrettyPrinter.Sprintf(format, a...)
+ return Default.Sprintf(format, a...)
}
-// Sprintln formats given arguemnts with newline and returns the result as string.
+// Sprintln formats given arguments with newline and returns the result as string.
func Sprintln(a ...interface{}) string {
- return defaultPrettyPrinter.Sprintln(a...)
+ return Default.Sprintln(a...)
}
// Fprint prints given arguments to a given writer.
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
- return defaultPrettyPrinter.Fprint(w, a...)
+ return Default.Fprint(w, a...)
}
// Fprintf prints format to a given writer.
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
- return defaultPrettyPrinter.Fprintf(w, format, a...)
+ return Default.Fprintf(w, format, a...)
}
// Fprintln prints given arguments to a given writer with newline.
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
- return defaultPrettyPrinter.Fprintln(w, a...)
+ return Default.Fprintln(w, a...)
}
// Errorf formats given arguments and returns it as error type.
func Errorf(format string, a ...interface{}) error {
- return defaultPrettyPrinter.Errorf(format, a...)
+ return Default.Errorf(format, a...)
}
// Fatal prints given arguments and finishes execution with exit status 1.
func Fatal(a ...interface{}) {
- defaultPrettyPrinter.Fatal(a...)
+ Default.Fatal(a...)
}
// Fatalf prints a given format and finishes execution with exit status 1.
func Fatalf(format string, a ...interface{}) {
- defaultPrettyPrinter.Fatalf(format, a...)
+ Default.Fatalf(format, a...)
}
// Fatalln prints given arguments with newline and finishes execution with exit status 1.
func Fatalln(a ...interface{}) {
- defaultPrettyPrinter.Fatalln(a...)
+ Default.Fatalln(a...)
}
// Change Print* functions' output to a given writer.
@@ -243,34 +279,30 @@ func Fatalln(a ...interface{}) {
// }
// }
func SetDefaultOutput(o io.Writer) {
- defaultPrettyPrinter.SetOutput(o)
+ Default.SetOutput(o)
}
// GetOutput returns pp's default output.
func GetDefaultOutput() io.Writer {
- return defaultPrettyPrinter.GetOutput()
+ return Default.GetOutput()
}
// Change Print* functions' output to default one.
func ResetDefaultOutput() {
- defaultPrettyPrinter.ResetOutput()
+ Default.ResetOutput()
}
// SetColorScheme takes a colorscheme used by all future Print calls.
func SetColorScheme(scheme ColorScheme) {
- defaultPrettyPrinter.SetColorScheme(scheme)
+ Default.SetColorScheme(scheme)
}
// ResetColorScheme resets colorscheme to default.
func ResetColorScheme() {
- defaultPrettyPrinter.ResetColorScheme()
+ Default.ResetColorScheme()
}
// SetMaxDepth sets the printer's Depth, -1 prints all
func SetDefaultMaxDepth(v int) {
- defaultPrettyPrinter.maxDepth = v
+ Default.maxDepth = v
}
-
-// WithLineInfo add file name and line information to output
-// call this function with care, because getting stack has performance penalty
-var WithLineInfo bool
diff --git a/pp_test.go b/pp_test.go
index c13da02..aa9e962 100644
--- a/pp_test.go
+++ b/pp_test.go
@@ -2,6 +2,7 @@ package pp
import (
"bytes"
+ "strings"
"testing"
)
@@ -31,7 +32,7 @@ func TestDefaultOutput(t *testing.T) {
func TestColorScheme(t *testing.T) {
SetColorScheme(ColorScheme{})
- if defaultPrettyPrinter.currentScheme.FieldName == 0 {
+ if Default.currentScheme.FieldName == 0 {
t.FailNow()
}
}
@@ -72,3 +73,90 @@ func TestWithLineInfoBackwardsCompatible(t *testing.T) {
ResetDefaultOutput()
}
+
+func TestStructPrintingWithTags(t *testing.T) {
+ type Foo struct {
+ IgnoreMe interface{} `pp:"-"`
+ ChangeMyName string `pp:"NewName"`
+ OmitIfEmpty string `pp:",omitempty"`
+ Full string `pp:"full,omitempty"`
+ }
+
+ testCases := []struct {
+ name string
+ foo Foo
+ omitIfEmptyOmitted bool
+ fullOmitted bool
+ }{
+ {
+ name: "all set",
+ foo: Foo{
+ IgnoreMe: "i'm a secret",
+ ChangeMyName: "i'm an alias",
+ OmitIfEmpty: "i'm not empty",
+ Full: "hello",
+ },
+ omitIfEmptyOmitted: false,
+ fullOmitted: false,
+ },
+ {
+ name: "omit if empty not set",
+ foo: Foo{
+ IgnoreMe: "i'm a secret",
+ ChangeMyName: "i'm an alias",
+ OmitIfEmpty: "",
+ Full: "hello",
+ },
+ omitIfEmptyOmitted: true,
+ fullOmitted: false,
+ },
+ {
+ name: "both omitted",
+ foo: Foo{
+ IgnoreMe: "i'm a secret",
+ ChangeMyName: "i'm an alias",
+ OmitIfEmpty: "",
+ Full: "",
+ },
+ omitIfEmptyOmitted: true,
+ fullOmitted: true,
+ },
+ {
+ name: "zero",
+ foo: Foo{},
+ omitIfEmptyOmitted: true,
+ fullOmitted: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ output := new(bytes.Buffer)
+ pp := New()
+ pp.SetOutput(output)
+
+ pp.Print(tc.foo)
+
+ result := output.String()
+
+ if strings.Contains(result, "IgnoreMe") {
+ t.Error("result should not contain IgnoreMe")
+ }
+
+ if strings.Contains(result, "OmitIfEmpty") && tc.omitIfEmptyOmitted {
+ t.Error("result should not contain OmitIfEmpty")
+ } else if !strings.Contains(result, "OmitIfEmpty") && !tc.omitIfEmptyOmitted {
+ t.Error("result should contain OmitIfEmpty")
+ }
+
+ // field Full is renamed to full by the tag
+ if strings.Contains(result, "full") && tc.fullOmitted {
+ t.Error("result should not contain full")
+ } else if !strings.Contains(result, "full") && !tc.fullOmitted {
+ t.Error("result should contain full")
+ }
+ })
+ }
+
+}
diff --git a/printer.go b/printer.go
index 782ea0a..13e0a0a 100644
--- a/printer.go
+++ b/printer.go
@@ -1,8 +1,10 @@
+// printer.go: The actual pretty print implementation. Everything in this file should be private.
package pp
import (
"bytes"
"fmt"
+ "math"
"math/big"
"reflect"
"regexp"
@@ -10,50 +12,58 @@ import (
"strings"
"text/tabwriter"
"time"
+
+ "golang.org/x/text/language"
+ "golang.org/x/text/message"
)
const (
indentWidth = 2
)
-var (
- // If the length of array or slice is larger than this,
- // the buffer will be shorten as {...}.
- BufferFoldThreshold = 1024
- // PrintMapTypes when set to true will have map types will always appended to maps.
- PrintMapTypes = true
-)
-
func (pp *PrettyPrinter) format(object interface{}) string {
- return newPrinter(object, &pp.currentScheme, pp.maxDepth, pp.coloringEnabled).String()
+ return newPrinter(object, &pp.currentScheme, pp.maxDepth, pp.coloringEnabled, pp.decimalUint, pp.exportedOnly, pp.thousandsSeparator).String()
}
-func newPrinter(object interface{}, currentScheme *ColorScheme, maxDepth int, coloringEnabled bool) *printer {
+func newPrinter(object interface{}, currentScheme *ColorScheme, maxDepth int, coloringEnabled bool, decimalUint bool, exportedOnly bool, thousandsSeparator bool) *printer {
buffer := bytes.NewBufferString("")
tw := new(tabwriter.Writer)
tw.Init(buffer, indentWidth, 0, 1, ' ', 0)
- return &printer{
- Buffer: buffer,
- tw: tw,
- depth: 0,
- maxDepth: maxDepth,
- value: reflect.ValueOf(object),
- visited: map[uintptr]bool{},
- currentScheme: currentScheme,
- coloringEnabled: coloringEnabled,
+ printer := &printer{
+ Buffer: buffer,
+ tw: tw,
+ depth: 0,
+ maxDepth: maxDepth,
+ value: reflect.ValueOf(object),
+ visited: map[uintptr]bool{},
+ currentScheme: currentScheme,
+ coloringEnabled: coloringEnabled,
+ decimalUint: decimalUint,
+ exportedOnly: exportedOnly,
+ thousandsSeparator: thousandsSeparator,
}
+
+ if thousandsSeparator {
+ printer.localizedPrinter = message.NewPrinter(language.English)
+ }
+
+ return printer
}
type printer struct {
*bytes.Buffer
- tw *tabwriter.Writer
- depth int
- maxDepth int
- value reflect.Value
- visited map[uintptr]bool
- currentScheme *ColorScheme
- coloringEnabled bool
+ tw *tabwriter.Writer
+ depth int
+ maxDepth int
+ value reflect.Value
+ visited map[uintptr]bool
+ currentScheme *ColorScheme
+ coloringEnabled bool
+ decimalUint bool
+ exportedOnly bool
+ thousandsSeparator bool
+ localizedPrinter *message.Printer
}
func (p *printer) String() string {
@@ -180,7 +190,7 @@ func (p *printer) printMap() {
func (p *printer) printStruct() {
if p.value.CanInterface() {
- if p.value.Type().String() == "time.Time" {
+ if p.value.Type().String() == "time.Time" && p.value.Type().PkgPath() == "time" {
p.printTime()
return
} else if p.value.Type().String() == "big.Int" {
@@ -194,17 +204,48 @@ func (p *printer) printStruct() {
}
}
- if p.value.NumField() == 0 {
+ var fields []int
+ for i := 0; i < p.value.NumField(); i++ {
+ field := p.value.Type().Field(i)
+ value := p.value.Field(i)
+ // ignore unexported if needed
+ if p.exportedOnly && field.PkgPath != "" {
+ continue
+ }
+ // ignore fields if zero value, or explicitly set
+ if tag := field.Tag.Get("pp"); tag != "" {
+ parts := strings.Split(tag, ",")
+ if len(parts) == 2 && parts[1] == "omitempty" && valueIsZero(value) {
+ continue
+ }
+ if parts[0] == "-" {
+ continue
+ }
+ }
+ fields = append(fields, i)
+ }
+
+ if len(fields) == 0 {
p.print(p.typeString() + "{}")
return
}
p.println(p.typeString() + "{")
p.indented(func() {
- for i := 0; i < p.value.NumField(); i++ {
- field := p.colorize(p.value.Type().Field(i).Name, p.currentScheme.FieldName)
+ for _, i := range fields {
+ field := p.value.Type().Field(i)
value := p.value.Field(i)
- p.indentPrintf("%s:\t%s,\n", field, p.format(value))
+
+ fieldName := field.Name
+ if tag := field.Tag.Get("pp"); tag != "" {
+ tagName := strings.Split(tag, ",")
+ if tagName[0] != "" {
+ fieldName = tagName[0]
+ }
+ }
+
+ colorizedFieldName := p.colorize(fieldName, p.currentScheme.FieldName)
+ p.indentPrintf("%s:\t%s,\n", colorizedFieldName, p.format(value))
}
})
p.indentPrint("}")
@@ -361,25 +402,53 @@ func (p *printer) indented(proc func()) {
p.depth--
}
+func (p *printer) fmtOrLocalizedSprintf(format string, a ...interface{}) string {
+ if p.localizedPrinter == nil {
+ return fmt.Sprintf(format, a...)
+ }
+
+ return p.localizedPrinter.Sprintf(format, a...)
+}
+
func (p *printer) raw() string {
// Some value causes panic when Interface() is called.
switch p.value.Kind() {
case reflect.Bool:
return fmt.Sprintf("%#v", p.value.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return fmt.Sprintf("%#v", p.value.Int())
+ return p.fmtOrLocalizedSprintf("%v", p.value.Int())
case reflect.Uint, reflect.Uintptr:
- return fmt.Sprintf("%#v", p.value.Uint())
+ if p.decimalUint {
+ return p.fmtOrLocalizedSprintf("%d", p.value.Uint())
+ } else {
+ return fmt.Sprintf("%#v", p.value.Uint())
+ }
case reflect.Uint8:
- return fmt.Sprintf("0x%02x", p.value.Uint())
+ if p.decimalUint {
+ return fmt.Sprintf("%d", p.value.Uint())
+ } else {
+ return fmt.Sprintf("0x%02x", p.value.Uint())
+ }
case reflect.Uint16:
- return fmt.Sprintf("0x%04x", p.value.Uint())
+ if p.decimalUint {
+ return p.fmtOrLocalizedSprintf("%d", p.value.Uint())
+ } else {
+ return fmt.Sprintf("0x%04x", p.value.Uint())
+ }
case reflect.Uint32:
- return fmt.Sprintf("0x%08x", p.value.Uint())
+ if p.decimalUint {
+ return p.fmtOrLocalizedSprintf("%d", p.value.Uint())
+ } else {
+ return fmt.Sprintf("0x%08x", p.value.Uint())
+ }
case reflect.Uint64:
- return fmt.Sprintf("0x%016x", p.value.Uint())
+ if p.decimalUint {
+ return p.fmtOrLocalizedSprintf("%d", p.value.Uint())
+ } else {
+ return fmt.Sprintf("0x%016x", p.value.Uint())
+ }
case reflect.Float32, reflect.Float64:
- return fmt.Sprintf("%f", p.value.Float())
+ return p.fmtOrLocalizedSprintf("%f", p.value.Float())
case reflect.Complex64, reflect.Complex128:
return fmt.Sprintf("%#v", p.value.Complex())
default:
@@ -400,7 +469,7 @@ func (p *printer) colorize(text string, color uint16) string {
}
func (p *printer) format(object interface{}) string {
- pp := newPrinter(object, p.currentScheme, p.maxDepth, p.coloringEnabled)
+ pp := newPrinter(object, p.currentScheme, p.maxDepth, p.coloringEnabled, p.decimalUint, p.exportedOnly, p.thousandsSeparator)
pp.depth = p.depth
pp.visited = p.visited
if value, ok := object.(reflect.Value); ok {
@@ -412,3 +481,46 @@ func (p *printer) format(object interface{}) string {
func (p *printer) indent() string {
return strings.Repeat("\t", p.depth)
}
+
+// valueIsZero reports whether v is the zero value for its type.
+// It returns false if the argument is invalid.
+// This is a copy paste of reflect#IsZero from go1.15. It is not present before go1.13 (source: https://golang.org/doc/go1.13#library)
+// source: https://golang.org/src/reflect/value.go?s=34297:34325#L1090
+// This will need to be updated for new types or the decision should be made to drop support for Go version pre go1.13
+func valueIsZero(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return math.Float64bits(v.Float()) == 0
+ case reflect.Complex64, reflect.Complex128:
+ c := v.Complex()
+ return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
+ case reflect.Array:
+ for i := 0; i < v.Len(); i++ {
+ if !valueIsZero(v.Index(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
+ return v.IsNil()
+ case reflect.String:
+ return v.Len() == 0
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ if !valueIsZero(v.Field(i)) {
+ return false
+ }
+ }
+ return true
+ default:
+ // this is the only difference between stdlib reflect#IsZero and this function. We're not going to
+ // panic on the default cause, even
+ return false
+ }
+}
diff --git a/printer_test.go b/printer_test.go
index 188ded6..a4beced 100644
--- a/printer_test.go
+++ b/printer_test.go
@@ -1,6 +1,7 @@
package pp
import (
+ "bytes"
"fmt"
"math/big"
"reflect"
@@ -9,10 +10,6 @@ import (
"testing"
"time"
"unsafe"
-
- // Use fork until following PR is merged
- // https://github.com/mitchellh/colorstring/pull/3
- "github.com/k0kubun/colorstring"
)
type testCase struct {
@@ -88,12 +85,12 @@ var (
{int16(16), "[blue][bold]16"},
{int32(32), "[blue][bold]32"},
{int64(64), "[blue][bold]64"},
- {uint(4), "[blue][bold]0x4"},
- {uint8(8), "[blue][bold]0x08"},
- {uint16(16), "[blue][bold]0x0010"},
- {uint32(32), "[blue][bold]0x00000020"},
- {uint64(64), "[blue][bold]0x0000000000000040"},
- {uintptr(128), "[blue][bold]0x80"},
+ {uint(4), "[blue][bold]4"},
+ {uint8(8), "[blue][bold]8"},
+ {uint16(16), "[blue][bold]16"},
+ {uint32(32), "[blue][bold]32"},
+ {uint64(64), "[blue][bold]64"},
+ {uintptr(128), "[blue][bold]128"},
{float32(2.23), "[magenta][bold]2.230000"},
{float64(3.14), "[magenta][bold]3.140000"},
{complex64(complex(3, -4)), "[blue][bold](3-4i)"},
@@ -131,35 +128,35 @@ var (
{
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, `
[][green]uint8[reset]{
- [blue][bold]0x00[reset], [blue][bold]0x01[reset], [blue][bold]0x02[reset], [blue][bold]0x03[reset], [blue][bold]0x04[reset], [blue][bold]0x05[reset], [blue][bold]0x06[reset], [blue][bold]0x07[reset], [blue][bold]0x08[reset], [blue][bold]0x09[reset], [blue][bold]0x00[reset], [blue][bold]0x01[reset], [blue][bold]0x02[reset], [blue][bold]0x03[reset], [blue][bold]0x04[reset], [blue][bold]0x05[reset],
- [blue][bold]0x06[reset], [blue][bold]0x07[reset], [blue][bold]0x08[reset], [blue][bold]0x09[reset],
+ [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset], [blue][bold]4[reset], [blue][bold]5[reset], [blue][bold]6[reset], [blue][bold]7[reset], [blue][bold]8[reset], [blue][bold]9[reset], [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset], [blue][bold]4[reset], [blue][bold]5[reset],
+ [blue][bold]6[reset], [blue][bold]7[reset], [blue][bold]8[reset], [blue][bold]9[reset],
}
`,
},
{
[]uint16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, `
[][green]uint16[reset]{
- [blue][bold]0x0000[reset], [blue][bold]0x0001[reset], [blue][bold]0x0002[reset], [blue][bold]0x0003[reset], [blue][bold]0x0004[reset], [blue][bold]0x0005[reset], [blue][bold]0x0006[reset], [blue][bold]0x0007[reset],
- [blue][bold]0x0008[reset], [blue][bold]0x0009[reset], [blue][bold]0x0000[reset], [blue][bold]0x0001[reset], [blue][bold]0x0002[reset], [blue][bold]0x0003[reset], [blue][bold]0x0004[reset], [blue][bold]0x0005[reset],
- [blue][bold]0x0006[reset], [blue][bold]0x0007[reset], [blue][bold]0x0008[reset], [blue][bold]0x0009[reset],
+ [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset], [blue][bold]4[reset], [blue][bold]5[reset], [blue][bold]6[reset], [blue][bold]7[reset],
+ [blue][bold]8[reset], [blue][bold]9[reset], [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset], [blue][bold]4[reset], [blue][bold]5[reset],
+ [blue][bold]6[reset], [blue][bold]7[reset], [blue][bold]8[reset], [blue][bold]9[reset],
}
`,
},
{
[]uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, `
[][green]uint32[reset]{
- [blue][bold]0x00000000[reset], [blue][bold]0x00000001[reset], [blue][bold]0x00000002[reset], [blue][bold]0x00000003[reset], [blue][bold]0x00000004[reset], [blue][bold]0x00000005[reset], [blue][bold]0x00000006[reset], [blue][bold]0x00000007[reset],
- [blue][bold]0x00000008[reset], [blue][bold]0x00000009[reset], [blue][bold]0x00000000[reset], [blue][bold]0x00000001[reset], [blue][bold]0x00000002[reset], [blue][bold]0x00000003[reset], [blue][bold]0x00000004[reset], [blue][bold]0x00000005[reset],
- [blue][bold]0x00000006[reset], [blue][bold]0x00000007[reset], [blue][bold]0x00000008[reset], [blue][bold]0x00000009[reset],
+ [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset], [blue][bold]4[reset], [blue][bold]5[reset], [blue][bold]6[reset], [blue][bold]7[reset],
+ [blue][bold]8[reset], [blue][bold]9[reset], [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset], [blue][bold]4[reset], [blue][bold]5[reset],
+ [blue][bold]6[reset], [blue][bold]7[reset], [blue][bold]8[reset], [blue][bold]9[reset],
}
`,
},
{
[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, `
[][green]uint64[reset]{
- [blue][bold]0x0000000000000000[reset], [blue][bold]0x0000000000000001[reset], [blue][bold]0x0000000000000002[reset], [blue][bold]0x0000000000000003[reset],
- [blue][bold]0x0000000000000004[reset], [blue][bold]0x0000000000000005[reset], [blue][bold]0x0000000000000006[reset], [blue][bold]0x0000000000000007[reset],
- [blue][bold]0x0000000000000008[reset], [blue][bold]0x0000000000000009[reset], [blue][bold]0x0000000000000000[reset],
+ [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset], [blue][bold]3[reset],
+ [blue][bold]4[reset], [blue][bold]5[reset], [blue][bold]6[reset], [blue][bold]7[reset],
+ [blue][bold]8[reset], [blue][bold]9[reset], [blue][bold]0[reset],
}
`,
},
@@ -167,13 +164,13 @@ var (
[][]byte{{0, 1, 2}, {3, 4}, {255}}, `
[][green][]uint8[reset]{
[][green]uint8[reset]{
- [blue][bold]0x00[reset], [blue][bold]0x01[reset], [blue][bold]0x02[reset],
+ [blue][bold]0[reset], [blue][bold]1[reset], [blue][bold]2[reset],
},
[][green]uint8[reset]{
- [blue][bold]0x03[reset], [blue][bold]0x04[reset],
+ [blue][bold]3[reset], [blue][bold]4[reset],
},
[][green]uint8[reset]{
- [blue][bold]0xff[reset],
+ [blue][bold]255[reset],
},
}
`,
@@ -222,13 +219,35 @@ var (
)
func TestFormat(t *testing.T) {
- for _, test := range testCases {
- actual := fmt.Sprintf("%s", defaultPrettyPrinter.format(test.object))
+ processTestCases(t, Default, testCases)
+}
+
+func TestThousands(t *testing.T) {
+ thousandsTestCases := []testCase{
+ {int(4), "[blue][bold]4"},
+ {int(4000), "[blue][bold]4,000"},
+ {uint(1000), "[blue][bold]1,000"},
+ {uint16(16000), "[blue][bold]16,000"},
+ {uint32(32000), "[blue][bold]32,000"},
+ {uint64(64000), "[blue][bold]64,000"},
+ {float64(3000.14), "[magenta][bold]3,000.140000"},
+ }
+
+ thousandsPrinter := newPrettyPrinter(3)
+ thousandsPrinter.SetThousandsSeparator(true)
+ thousandsPrinter.SetDecimalUint(true)
+
+ processTestCases(t, thousandsPrinter, thousandsTestCases)
+}
+
+func processTestCases(t *testing.T, printer *PrettyPrinter, cases []testCase) {
+ for _, test := range cases {
+ actual := fmt.Sprintf("%s", printer.format(test.object))
trimmed := strings.Replace(test.expect, "\t", "", -1)
trimmed = strings.TrimPrefix(trimmed, "\n")
trimmed = strings.TrimSuffix(trimmed, "\n")
- expect := colorstring.Color(trimmed)
+ expect := colorString(trimmed)
if expect != actual {
v := reflect.ValueOf(test.object)
t.Errorf("\nTestCase: %#v\nType: %s\nExpect: %# v\nActual: %# v\n", test.object, v.Kind(), expect, actual)
@@ -238,7 +257,7 @@ func TestFormat(t *testing.T) {
}
for _, object := range checkCases {
- actual := fmt.Sprintf("%s", defaultPrettyPrinter.format(object))
+ actual := fmt.Sprintf("%s", printer.format(object))
logResult(t, object, actual)
}
}
@@ -254,3 +273,44 @@ func logResult(t *testing.T, object interface{}, actual string) {
func isMultiLine(text string) bool {
return strings.Contains(text, "\n")
}
+
+func colorString(text string) string {
+ buf := new(bytes.Buffer)
+ colored := false
+
+ lastMatch := []int{0, 0}
+ for _, match := range colorRe.FindAllStringIndex(text, -1) {
+ buf.WriteString(text[lastMatch[1]:match[0]])
+ lastMatch = match
+
+ var colorText string
+ color := text[lastMatch[0]+1 : lastMatch[1]-1]
+ if code, ok := colors[color]; ok {
+ colored = (color != "reset")
+ colorText = fmt.Sprintf("\033[%sm", code)
+ } else {
+ colorText = text[lastMatch[0]:lastMatch[1]]
+ }
+ buf.WriteString(colorText)
+ }
+ buf.WriteString(text[lastMatch[1]:])
+
+ if colored {
+ buf.WriteString("\033[0m")
+ }
+ return buf.String()
+}
+
+var (
+ colorRe = regexp.MustCompile(`(?i)\[[a-z0-9_-]+\]`)
+ colors = map[string]string{
+ "red": "31",
+ "green": "32",
+ "yellow": "33",
+ "blue": "34",
+ "magenta": "35",
+ "cyan": "36",
+ "bold": "1",
+ "reset": "0",
+ }
+)
diff --git a/sort.go b/sort.go
index 8ed784f..591b595 100644
--- a/sort.go
+++ b/sort.go
@@ -1,3 +1,4 @@
+// sort.go: Implementation for sorting map keys
package pp
import (
Debdiff
File lists identical (after any substitutions)
No differences were encountered in the control files