Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
jolheiser | 8af49ac71f | |
John Olheiser | 0b1a1e4bd0 | |
John Olheiser | 262effd971 | |
John Olheiser | c628fd1987 | |
John Olheiser | 5e1722bd43 | |
John Olheiser | f3053ba01b |
|
@ -1,23 +0,0 @@
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- deadcode
|
|
||||||
- dogsled
|
|
||||||
- dupl
|
|
||||||
- errcheck
|
|
||||||
- funlen
|
|
||||||
- gocognit
|
|
||||||
- goconst
|
|
||||||
- gocritic
|
|
||||||
- gocyclo
|
|
||||||
- gofmt
|
|
||||||
- golint
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- misspell
|
|
||||||
- prealloc
|
|
||||||
- staticcheck
|
|
||||||
- structcheck
|
|
||||||
- typecheck
|
|
||||||
- unparam
|
|
||||||
- unused
|
|
||||||
- varcheck
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
Copyright 2020 John Olheiser
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
12
Makefile
12
Makefile
|
@ -1,13 +1,9 @@
|
||||||
GO ?= go
|
GO ?= go
|
||||||
|
|
||||||
.PHONY: lint
|
|
||||||
lint:
|
|
||||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
export BINARY="golangci-lint"; \
|
|
||||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.1; \
|
|
||||||
fi
|
|
||||||
golangci-lint run --timeout 5m
|
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
$(GO) fmt ./...
|
$(GO) fmt ./...
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
$(GO) test -race ./...
|
|
@ -49,3 +49,9 @@ Beaver allows you to customize the colors of various parts of the message
|
||||||
// Set Trace logging to Magenta instead of Bold-Cyan
|
// Set Trace logging to Magenta instead of Bold-Cyan
|
||||||
color.Trace = color.New(color.FgMagenta)
|
color.Trace = color.New(color.FgMagenta)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[More info for the `color` package](color/README.md)
|
||||||
|
|
||||||
|
## LICENSE
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitea.com/jolheiser/beaver"
|
"go.jolheiser.com/beaver"
|
||||||
"gitea.com/jolheiser/beaver/color"
|
"go.jolheiser.com/beaver/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -104,4 +104,12 @@ func main() {
|
||||||
color.Time = color.New(color.FgCyan)
|
color.Time = color.New(color.FgCyan)
|
||||||
color.Stack = color.New(color.FgGreen)
|
color.Stack = color.New(color.FgGreen)
|
||||||
b.Info("Full options emulating Gitea")
|
b.Info("Full options emulating Gitea")
|
||||||
|
|
||||||
|
// Extended example
|
||||||
|
ex := color.NewExtended(color.Gold1, color.DarkGreen)
|
||||||
|
fmt.Println(ex.Format("Gold on Dark Green"))
|
||||||
|
|
||||||
|
// True color example
|
||||||
|
birb := color.NewTrue(color.MustParseHex("#007D96"), &color.RGB{})
|
||||||
|
fmt.Println(birb.Format("Birb blue on black"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Color
|
||||||
|
|
||||||
|
Beaver comes with the `color` sub-package that can be used even without Beaver calls.
|
||||||
|
|
||||||
|
`Color` is an interface that simply needs `Format(text string) string` to fulfill it.
|
||||||
|
Any logger in beaver can be set to a `Color`.
|
||||||
|
|
||||||
|
## Formatting a string with a single attribute
|
||||||
|
```go
|
||||||
|
text := color.FgRed.Format("red")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatting a string with a full color
|
||||||
|
```go
|
||||||
|
text := color.New(color.BgGreen, color.FgRed, color.Bold).Format("green background, red text, and bold")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extended colors
|
||||||
|
|
||||||
|
Extended colors are 256-color extensions. They can be referred to by name, available in [extended_colors.go](extended_colors.go).
|
||||||
|
|
||||||
|
## True colors
|
||||||
|
|
||||||
|
True colors are full RBG colors, they can be either created with RGB values or parsed from hex.
|
|
@ -0,0 +1,96 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface guard
|
||||||
|
var _ Color = &Basic{}
|
||||||
|
|
||||||
|
// Basic defines a custom color object which is defined by SGR parameters.
|
||||||
|
type Basic struct {
|
||||||
|
Attrs []BasicAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicAttribute defines a single SGR Code
|
||||||
|
type BasicAttribute int
|
||||||
|
|
||||||
|
// Logger attributes
|
||||||
|
const (
|
||||||
|
Reset BasicAttribute = iota
|
||||||
|
Bold
|
||||||
|
Faint
|
||||||
|
Italic
|
||||||
|
Underline
|
||||||
|
BlinkSlow
|
||||||
|
BlinkRapid
|
||||||
|
ReverseVideo
|
||||||
|
Concealed
|
||||||
|
CrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new Color with attrs Attributes
|
||||||
|
func New(attrs ...BasicAttribute) *Basic {
|
||||||
|
return &Basic{Attrs: attrs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds new Attributes to a Color
|
||||||
|
func (b *Basic) Add(attrs ...BasicAttribute) {
|
||||||
|
for _, attr := range attrs {
|
||||||
|
has := false
|
||||||
|
for _, att := range b.Attrs {
|
||||||
|
if attr == att {
|
||||||
|
has = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
b.Attrs = append(b.Attrs, attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the sum of a Color's Attributes
|
||||||
|
func (b *Basic) String() string {
|
||||||
|
attrs := 0
|
||||||
|
for _, attr := range b.Attrs {
|
||||||
|
attrs += int(attr)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d", attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Basic) sequence() string {
|
||||||
|
format := make([]string, len(b.Attrs))
|
||||||
|
for i, v := range b.Attrs {
|
||||||
|
format[i] = strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(format, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Basic) wrap(s string) string {
|
||||||
|
return b.format() + s + b.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Basic) format() string {
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, b.sequence())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Basic) unformat() string {
|
||||||
|
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format returns a string wrapped in the basic color
|
||||||
|
func (b *Basic) Format(text string) string {
|
||||||
|
if len(b.Attrs) > 0 {
|
||||||
|
return b.wrap(text)
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatf returns a formatted string wrapped in the basic color
|
||||||
|
func (b *Basic) Formatf(format string, v ...interface{}) string {
|
||||||
|
return b.Format(fmt.Sprintf(format, v...))
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack BasicAttribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
FgHiBlack BasicAttribute = iota + 90
|
||||||
|
FgHiRed
|
||||||
|
FgHiGreen
|
||||||
|
FgHiYellow
|
||||||
|
FgHiBlue
|
||||||
|
FgHiMagenta
|
||||||
|
FgHiCyan
|
||||||
|
FgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background text colors
|
||||||
|
const (
|
||||||
|
BgBlack BasicAttribute = iota + 40
|
||||||
|
BgRed
|
||||||
|
BgGreen
|
||||||
|
BgYellow
|
||||||
|
BgBlue
|
||||||
|
BgMagenta
|
||||||
|
BgCyan
|
||||||
|
BgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
BgHiBlack BasicAttribute = iota + 100
|
||||||
|
BgHiRed
|
||||||
|
BgHiGreen
|
||||||
|
BgHiYellow
|
||||||
|
BgHiBlue
|
||||||
|
BgHiMagenta
|
||||||
|
BgHiCyan
|
||||||
|
BgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
var attrCache = make(map[BasicAttribute]*Basic)
|
||||||
|
|
||||||
|
func attr(a BasicAttribute) *Basic {
|
||||||
|
if c, ok := attrCache[a]; ok {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
c := New(a)
|
||||||
|
attrCache[a] = c
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format is a quick way to format a string using a single attribute
|
||||||
|
func (a BasicAttribute) Format(text string) string {
|
||||||
|
return attr(a).Format(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format is a quick way to format a formatted string using a single attribute
|
||||||
|
func (a BasicAttribute) Formatf(text string, v ...interface{}) string {
|
||||||
|
return attr(a).Formatf(text, v...)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var color = New()
|
||||||
|
|
||||||
|
func TestColor(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
Name string
|
||||||
|
SetAttrs []BasicAttribute
|
||||||
|
AddAttrs []BasicAttribute
|
||||||
|
Size int
|
||||||
|
}{
|
||||||
|
{"Init", nil, nil, 0},
|
||||||
|
{"Set 2", []BasicAttribute{Bold, FgBlack}, nil, 2},
|
||||||
|
{"Add 2", nil, []BasicAttribute{Italic, BgWhite}, 4},
|
||||||
|
{"Set 3", []BasicAttribute{Bold, FgBlack, BgWhite}, nil, 3},
|
||||||
|
{"Add 3", nil, []BasicAttribute{Italic, FgWhite, BgBlack}, 6},
|
||||||
|
{"Add Same", nil, []BasicAttribute{Italic, FgWhite, BgBlack}, 6},
|
||||||
|
{"Add 2 Same", nil, []BasicAttribute{Italic, FgWhite, BgGreen}, 7},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
if tc.SetAttrs != nil {
|
||||||
|
color.Attrs = tc.SetAttrs
|
||||||
|
}
|
||||||
|
if tc.AddAttrs != nil {
|
||||||
|
color.Add(tc.AddAttrs...)
|
||||||
|
}
|
||||||
|
if len(color.Attrs) != tc.Size {
|
||||||
|
t.Logf("color has `%d` attributes, but should have `%d`", len(color.Attrs), tc.Size)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
133
color/color.go
133
color/color.go
|
@ -1,143 +1,18 @@
|
||||||
package color
|
package color
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Color defines a custom color object which is defined by SGR parameters.
|
|
||||||
type Color struct {
|
|
||||||
Attrs []Attribute
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute defines a single SGR Code
|
|
||||||
type Attribute int
|
|
||||||
|
|
||||||
const escape = "\x1b"
|
const escape = "\x1b"
|
||||||
|
|
||||||
// Logger attributes
|
type Color interface {
|
||||||
const (
|
Format(text string) string
|
||||||
Reset Attribute = iota
|
Formatf(text string, v ...interface{}) string
|
||||||
Bold
|
|
||||||
Faint
|
|
||||||
Italic
|
|
||||||
Underline
|
|
||||||
BlinkSlow
|
|
||||||
BlinkRapid
|
|
||||||
ReverseVideo
|
|
||||||
Concealed
|
|
||||||
CrossedOut
|
|
||||||
)
|
|
||||||
|
|
||||||
// Foreground text colors
|
|
||||||
const (
|
|
||||||
FgBlack Attribute = iota + 30
|
|
||||||
FgRed
|
|
||||||
FgGreen
|
|
||||||
FgYellow
|
|
||||||
FgBlue
|
|
||||||
FgMagenta
|
|
||||||
FgCyan
|
|
||||||
FgWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Foreground Hi-Intensity text colors
|
|
||||||
const (
|
|
||||||
FgHiBlack Attribute = iota + 90
|
|
||||||
FgHiRed
|
|
||||||
FgHiGreen
|
|
||||||
FgHiYellow
|
|
||||||
FgHiBlue
|
|
||||||
FgHiMagenta
|
|
||||||
FgHiCyan
|
|
||||||
FgHiWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Background text colors
|
|
||||||
const (
|
|
||||||
BgBlack Attribute = iota + 40
|
|
||||||
BgRed
|
|
||||||
BgGreen
|
|
||||||
BgYellow
|
|
||||||
BgBlue
|
|
||||||
BgMagenta
|
|
||||||
BgCyan
|
|
||||||
BgWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Background Hi-Intensity text colors
|
|
||||||
const (
|
|
||||||
BgHiBlack Attribute = iota + 100
|
|
||||||
BgHiRed
|
|
||||||
BgHiGreen
|
|
||||||
BgHiYellow
|
|
||||||
BgHiBlue
|
|
||||||
BgHiMagenta
|
|
||||||
BgHiCyan
|
|
||||||
BgHiWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// New returns a new Color with attrs Attributes
|
|
||||||
func New(attrs ...Attribute) *Color {
|
|
||||||
return &Color{Attrs: attrs}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds new Attributes to a Color
|
|
||||||
func (c *Color) Add(attrs ...Attribute) {
|
|
||||||
for _, attr := range attrs {
|
|
||||||
has := false
|
|
||||||
for _, att := range c.Attrs {
|
|
||||||
if attr == att {
|
|
||||||
has = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !has {
|
|
||||||
c.Attrs = append(c.Attrs, attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the sum of a Color's Attributes
|
|
||||||
func (c *Color) String() string {
|
|
||||||
attrs := 0
|
|
||||||
for _, attr := range c.Attrs {
|
|
||||||
attrs += int(attr)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%d", attrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) sequence() string {
|
|
||||||
format := make([]string, len(c.Attrs))
|
|
||||||
for i, v := range c.Attrs {
|
|
||||||
format[i] = strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(format, ";")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) wrap(s string) string {
|
|
||||||
return c.format() + s + c.unformat()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) format() string {
|
|
||||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) unformat() string {
|
|
||||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) Format(text string) string {
|
|
||||||
if len(c.Attrs) > 0 {
|
|
||||||
return c.wrap(text)
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
|
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
|
||||||
func ParseLevel(level string) *Color {
|
func ParseLevel(level string) Color {
|
||||||
switch strings.ToUpper(level) {
|
switch strings.ToUpper(level) {
|
||||||
case "T", "TRACE":
|
case "T", "TRACE":
|
||||||
return Trace
|
return Trace
|
||||||
|
|
|
@ -5,47 +5,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var color = New()
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestColor(t *testing.T) {
|
|
||||||
tt := []struct {
|
|
||||||
Name string
|
|
||||||
SetAttrs []Attribute
|
|
||||||
AddAttrs []Attribute
|
|
||||||
Size int
|
|
||||||
}{
|
|
||||||
{"Init", nil, nil, 0},
|
|
||||||
{"Set 2", []Attribute{Bold, FgBlack}, nil, 2},
|
|
||||||
{"Add 2", nil, []Attribute{Italic, BgWhite}, 4},
|
|
||||||
{"Set 3", []Attribute{Bold, FgBlack, BgWhite}, nil, 3},
|
|
||||||
{"Add 3", nil, []Attribute{Italic, FgWhite, BgBlack}, 6},
|
|
||||||
{"Add Same", nil, []Attribute{Italic, FgWhite, BgBlack}, 6},
|
|
||||||
{"Add 2 Same", nil, []Attribute{Italic, FgWhite, BgGreen}, 7},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tt {
|
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
|
||||||
if tc.SetAttrs != nil {
|
|
||||||
color.Attrs = tc.SetAttrs
|
|
||||||
}
|
|
||||||
if tc.AddAttrs != nil {
|
|
||||||
color.Add(tc.AddAttrs...)
|
|
||||||
}
|
|
||||||
if len(color.Attrs) != tc.Size {
|
|
||||||
t.Logf("color has `%d` attributes, but should have `%d`", len(color.Attrs), tc.Size)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseLevel(t *testing.T) {
|
func TestParseLevel(t *testing.T) {
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
Parse string
|
Parse string
|
||||||
Expected *Color
|
Expected Color
|
||||||
}{
|
}{
|
||||||
{"T", Trace},
|
{"T", Trace},
|
||||||
{"Trace", Trace},
|
{"Trace", Trace},
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface guard
|
||||||
|
var _ Color = &Extended{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
extendedFG = "38;5;"
|
||||||
|
extendedBG = "48;5;"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extended defines a custom color object which is defined by SGR parameters.
|
||||||
|
type Extended struct {
|
||||||
|
FG ExtendedAttribute
|
||||||
|
BG ExtendedAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExtended returns a new Color with an ExtendedAttribute FG and BG
|
||||||
|
func NewExtended(fg, bg ExtendedAttribute) *Extended {
|
||||||
|
return &Extended{
|
||||||
|
FG: fg,
|
||||||
|
BG: bg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the sum of a Color's Attributes
|
||||||
|
func (e *Extended) String() string {
|
||||||
|
return fmt.Sprintf("%d", e.FG+e.BG)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Extended) wrap(s string) string {
|
||||||
|
return e.format() + s + e.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Extended) format() string {
|
||||||
|
return fmt.Sprintf("%s[%s%dm%s[%s%dm", escape, extendedFG, e.FG, escape, extendedBG, e.BG)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Extended) unformat() string {
|
||||||
|
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format returns a string wrapped in the extended color
|
||||||
|
func (e *Extended) Format(text string) string {
|
||||||
|
return e.wrap(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatf returns a formatted string wrapped in the extended color
|
||||||
|
func (e *Extended) Formatf(text string, v ...interface{}) string {
|
||||||
|
return e.wrap(fmt.Sprintf(text, v...))
|
||||||
|
}
|
|
@ -0,0 +1,519 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
// ExtendedAttribute defines an extended color
|
||||||
|
type ExtendedAttribute int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// #000000
|
||||||
|
Black ExtendedAttribute = iota
|
||||||
|
// #800000
|
||||||
|
Maroon
|
||||||
|
// #008000
|
||||||
|
Green
|
||||||
|
// #808000
|
||||||
|
Olive
|
||||||
|
// #000080
|
||||||
|
Navy
|
||||||
|
// #800080
|
||||||
|
Purple
|
||||||
|
// #008080
|
||||||
|
Teal
|
||||||
|
// #c0c0c0
|
||||||
|
Silver
|
||||||
|
// #808080
|
||||||
|
Grey
|
||||||
|
// #ff0000
|
||||||
|
Red
|
||||||
|
// #00ff00
|
||||||
|
Lime
|
||||||
|
// #ffff00
|
||||||
|
Yellow
|
||||||
|
// #0000ff
|
||||||
|
Blue
|
||||||
|
// #ff00ff
|
||||||
|
Fuchsia
|
||||||
|
// #00ffff
|
||||||
|
Aqua
|
||||||
|
// #ffffff
|
||||||
|
White
|
||||||
|
// #000000
|
||||||
|
Grey0
|
||||||
|
// #00005f
|
||||||
|
NavyBlue
|
||||||
|
// #000087
|
||||||
|
DarkBlue
|
||||||
|
// #0000af
|
||||||
|
Blue3
|
||||||
|
// #0000d7
|
||||||
|
Blue3_1
|
||||||
|
// #0000ff
|
||||||
|
Blue1
|
||||||
|
// #005f00
|
||||||
|
DarkGreen
|
||||||
|
// #005f5f
|
||||||
|
DeepSkyBlue4
|
||||||
|
// #005f87
|
||||||
|
DeepSkyBlue4_1
|
||||||
|
// #005faf
|
||||||
|
DeepSkyBlue4_2
|
||||||
|
// #005fd7
|
||||||
|
DodgerBlue3
|
||||||
|
// #005fff
|
||||||
|
DodgerBlue2
|
||||||
|
// #008700
|
||||||
|
Green4
|
||||||
|
// #00875f
|
||||||
|
SpringGreen4
|
||||||
|
// #008787
|
||||||
|
Turquoise4
|
||||||
|
// #0087af
|
||||||
|
DeepSkyBlue3
|
||||||
|
// #0087d7
|
||||||
|
DeepSkyBlue3_1
|
||||||
|
// #0087ff
|
||||||
|
DodgerBlue1
|
||||||
|
// #00af00
|
||||||
|
Green3
|
||||||
|
// #00af5f
|
||||||
|
SpringGreen3
|
||||||
|
// #00af87
|
||||||
|
DarkCyan
|
||||||
|
// #00afaf
|
||||||
|
LightSeaGreen
|
||||||
|
// #00afd7
|
||||||
|
DeepSkyBlue2
|
||||||
|
// #00afff
|
||||||
|
DeepSkyBlue1
|
||||||
|
// #00d700
|
||||||
|
Green3_1
|
||||||
|
// #00d75f
|
||||||
|
SpringGreen3_1
|
||||||
|
// #00d787
|
||||||
|
SpringGreen2
|
||||||
|
// #00d7af
|
||||||
|
Cyan3
|
||||||
|
// #00d7d7
|
||||||
|
DarkTurquoise
|
||||||
|
// #00d7ff
|
||||||
|
Turquoise2
|
||||||
|
// #00ff00
|
||||||
|
Green1
|
||||||
|
// #00ff5f
|
||||||
|
SpringGreen2_1
|
||||||
|
// #00ff87
|
||||||
|
SpringGreen1
|
||||||
|
// #00ffaf
|
||||||
|
MediumSpringGreen
|
||||||
|
// #00ffd7
|
||||||
|
Cyan2
|
||||||
|
// #00ffff
|
||||||
|
Cyan1
|
||||||
|
// #5f0000
|
||||||
|
DarkRed
|
||||||
|
// #5f005f
|
||||||
|
DeepPink4
|
||||||
|
// #5f0087
|
||||||
|
Purple4
|
||||||
|
// #5f00af
|
||||||
|
Purple4_1
|
||||||
|
// #5f00d7
|
||||||
|
Purple3
|
||||||
|
// #5f00ff
|
||||||
|
BlueViolet
|
||||||
|
// #5f5f00
|
||||||
|
Orange4
|
||||||
|
// #5f5f5f
|
||||||
|
Grey37
|
||||||
|
// #5f5f87
|
||||||
|
MediumPurple4
|
||||||
|
// #5f5faf
|
||||||
|
SlateBlue3
|
||||||
|
// #5f5fd7
|
||||||
|
SlateBlue3_1
|
||||||
|
// #5f5fff
|
||||||
|
RoyalBlue1
|
||||||
|
// #5f8700
|
||||||
|
Chartreuse4
|
||||||
|
// #5f875f
|
||||||
|
DarkSeaGreen4
|
||||||
|
// #5f8787
|
||||||
|
PaleTurquoise4
|
||||||
|
// #5f87af
|
||||||
|
SteelBlue
|
||||||
|
// #5f87d7
|
||||||
|
SteelBlue3
|
||||||
|
// #5f87ff
|
||||||
|
CornflowerBlue
|
||||||
|
// #5faf00
|
||||||
|
Chartreuse3
|
||||||
|
// #5faf5f
|
||||||
|
DarkSeaGreen4_1
|
||||||
|
// #5faf87
|
||||||
|
CadetBlue
|
||||||
|
// #5fafaf
|
||||||
|
CadetBlue_1
|
||||||
|
// #5fafd7
|
||||||
|
SkyBlue3
|
||||||
|
// #5fafff
|
||||||
|
SteelBlue1
|
||||||
|
// #5fd700
|
||||||
|
Chartreuse3_1
|
||||||
|
// #5fd75f
|
||||||
|
PaleGreen3
|
||||||
|
// #5fd787
|
||||||
|
SeaGreen3
|
||||||
|
// #5fd7af
|
||||||
|
Aquamarine3
|
||||||
|
// #5fd7d7
|
||||||
|
MediumTurquoise
|
||||||
|
// #5fd7ff
|
||||||
|
SteelBlue1_1
|
||||||
|
// #5fff00
|
||||||
|
Chartreuse2
|
||||||
|
// #5fff5f
|
||||||
|
SeaGreen2
|
||||||
|
// #5fff87
|
||||||
|
SeaGreen1
|
||||||
|
// #5fffaf
|
||||||
|
SeaGreen1_1
|
||||||
|
// #5fffd7
|
||||||
|
Aquamarine1
|
||||||
|
// #5fffff
|
||||||
|
DarkSlateGray2
|
||||||
|
// #870000
|
||||||
|
DarkRed_1
|
||||||
|
// #87005f
|
||||||
|
DeepPink4_1
|
||||||
|
// #870087
|
||||||
|
DarkMagenta
|
||||||
|
// #8700af
|
||||||
|
DarkMagenta_1
|
||||||
|
// #8700d7
|
||||||
|
DarkViolet
|
||||||
|
// #8700ff
|
||||||
|
Purple_1
|
||||||
|
// #875f00
|
||||||
|
Orange4_1
|
||||||
|
// #875f5f
|
||||||
|
LightPink4
|
||||||
|
// #875f87
|
||||||
|
Plum4
|
||||||
|
// #875faf
|
||||||
|
MediumPurple3
|
||||||
|
// #875fd7
|
||||||
|
MediumPurple3_1
|
||||||
|
// #875fff
|
||||||
|
SlateBlue1
|
||||||
|
// #878700
|
||||||
|
Yellow4
|
||||||
|
// #87875f
|
||||||
|
Wheat4
|
||||||
|
// #878787
|
||||||
|
Grey53
|
||||||
|
// #8787af
|
||||||
|
LightSlateGrey
|
||||||
|
// #8787d7
|
||||||
|
MediumPurple
|
||||||
|
// #8787ff
|
||||||
|
LightSlateBlue
|
||||||
|
// #87af00
|
||||||
|
Yellow4_1
|
||||||
|
// #87af5f
|
||||||
|
DarkOliveGreen3
|
||||||
|
// #87af87
|
||||||
|
DarkSeaGreen
|
||||||
|
// #87afaf
|
||||||
|
LightSkyBlue3
|
||||||
|
// #87afd7
|
||||||
|
LightSkyBlue3_1
|
||||||
|
// #87afff
|
||||||
|
SkyBlue2
|
||||||
|
// #87d700
|
||||||
|
Chartreuse2_1
|
||||||
|
// #87d75f
|
||||||
|
DarkOliveGreen3_1
|
||||||
|
// #87d787
|
||||||
|
PaleGreen3_1
|
||||||
|
// #87d7af
|
||||||
|
DarkSeaGreen3
|
||||||
|
// #87d7d7
|
||||||
|
DarkSlateGray3
|
||||||
|
// #87d7ff
|
||||||
|
SkyBlue1
|
||||||
|
// #87ff00
|
||||||
|
Chartreuse1
|
||||||
|
// #87ff5f
|
||||||
|
LightGreen
|
||||||
|
// #87ff87
|
||||||
|
LightGreen_1
|
||||||
|
// #87ffaf
|
||||||
|
PaleGreen1
|
||||||
|
// #87ffd7
|
||||||
|
Aquamarine1_1
|
||||||
|
// #87ffff
|
||||||
|
DarkSlateGray1
|
||||||
|
// #af0000
|
||||||
|
Red3
|
||||||
|
// #af005f
|
||||||
|
DeepPink4_2
|
||||||
|
// #af0087
|
||||||
|
MediumVioletRed
|
||||||
|
// #af00af
|
||||||
|
Magenta3
|
||||||
|
// #af00d7
|
||||||
|
DarkViolet_1
|
||||||
|
// #af00ff
|
||||||
|
Purple_2
|
||||||
|
// #af5f00
|
||||||
|
DarkOrange3
|
||||||
|
// #af5f5f
|
||||||
|
IndianRed
|
||||||
|
// #af5f87
|
||||||
|
HotPink3
|
||||||
|
// #af5faf
|
||||||
|
MediumOrchid3
|
||||||
|
// #af5fd7
|
||||||
|
MediumOrchid
|
||||||
|
// #af5fff
|
||||||
|
MediumPurple2
|
||||||
|
// #af8700
|
||||||
|
DarkGoldenrod
|
||||||
|
// #af875f
|
||||||
|
LightSalmon3
|
||||||
|
// #af8787
|
||||||
|
RosyBrown
|
||||||
|
// #af87af
|
||||||
|
Grey63
|
||||||
|
// #af87d7
|
||||||
|
MediumPurple2_1
|
||||||
|
// #af87ff
|
||||||
|
MediumPurple1
|
||||||
|
// #afaf00
|
||||||
|
Gold3
|
||||||
|
// #afaf5f
|
||||||
|
DarkKhaki
|
||||||
|
// #afaf87
|
||||||
|
NavajoWhite3
|
||||||
|
// #afafaf
|
||||||
|
Grey69
|
||||||
|
// #afafd7
|
||||||
|
LightSteelBlue3
|
||||||
|
// #afafff
|
||||||
|
LightSteelBlue
|
||||||
|
// #afd700
|
||||||
|
Yellow3
|
||||||
|
// #afd75f
|
||||||
|
DarkOliveGreen3_2
|
||||||
|
// #afd787
|
||||||
|
DarkSeaGreen3_1
|
||||||
|
// #afd7af
|
||||||
|
DarkSeaGreen2
|
||||||
|
// #afd7d7
|
||||||
|
LightCyan3
|
||||||
|
// #afd7ff
|
||||||
|
LightSkyBlue1
|
||||||
|
// #afff00
|
||||||
|
GreenYellow
|
||||||
|
// #afff5f
|
||||||
|
DarkOliveGreen2
|
||||||
|
// #afff87
|
||||||
|
PaleGreen1_1
|
||||||
|
// #afffaf
|
||||||
|
DarkSeaGreen2_1
|
||||||
|
// #afffd7
|
||||||
|
DarkSeaGreen1
|
||||||
|
// #afffff
|
||||||
|
PaleTurquoise1
|
||||||
|
// #d70000
|
||||||
|
Red3_1
|
||||||
|
// #d7005f
|
||||||
|
DeepPink3
|
||||||
|
// #d70087
|
||||||
|
DeepPink3_1
|
||||||
|
// #d700af
|
||||||
|
Magenta3_1
|
||||||
|
// #d700d7
|
||||||
|
Magenta3_2
|
||||||
|
// #d700ff
|
||||||
|
Magenta2
|
||||||
|
// #d75f00
|
||||||
|
DarkOrange3_1
|
||||||
|
// #d75f5f
|
||||||
|
IndianRed_1
|
||||||
|
// #d75f87
|
||||||
|
HotPink3_1
|
||||||
|
// #d75faf
|
||||||
|
HotPink2
|
||||||
|
// #d75fd7
|
||||||
|
Orchid
|
||||||
|
// #d75fff
|
||||||
|
MediumOrchid1
|
||||||
|
// #d78700
|
||||||
|
Orange3
|
||||||
|
// #d7875f
|
||||||
|
LightSalmon3_1
|
||||||
|
// #d78787
|
||||||
|
LightPink3
|
||||||
|
// #d787af
|
||||||
|
Pink3
|
||||||
|
// #d787d7
|
||||||
|
Plum3
|
||||||
|
// #d787ff
|
||||||
|
Violet
|
||||||
|
// #d7af00
|
||||||
|
Gold3_1
|
||||||
|
// #d7af5f
|
||||||
|
LightGoldenrod3
|
||||||
|
// #d7af87
|
||||||
|
Tan
|
||||||
|
// #d7afaf
|
||||||
|
MistyRose3
|
||||||
|
// #d7afd7
|
||||||
|
Thistle3
|
||||||
|
// #d7afff
|
||||||
|
Plum2
|
||||||
|
// #d7d700
|
||||||
|
Yellow3_1
|
||||||
|
// #d7d75f
|
||||||
|
Khaki3
|
||||||
|
// #d7d787
|
||||||
|
LightGoldenrod2
|
||||||
|
// #d7d7af
|
||||||
|
LightYellow3
|
||||||
|
// #d7d7d7
|
||||||
|
Grey84
|
||||||
|
// #d7d7ff
|
||||||
|
LightSteelBlue1
|
||||||
|
// #d7ff00
|
||||||
|
Yellow2
|
||||||
|
// #d7ff5f
|
||||||
|
DarkOliveGreen1
|
||||||
|
// #d7ff87
|
||||||
|
DarkOliveGreen1_1
|
||||||
|
// #d7ffaf
|
||||||
|
DarkSeaGreen1_1
|
||||||
|
// #d7ffd7
|
||||||
|
Honeydew2
|
||||||
|
// #d7ffff
|
||||||
|
LightCyan1
|
||||||
|
// #ff0000
|
||||||
|
Red1
|
||||||
|
// #ff005f
|
||||||
|
DeepPink2
|
||||||
|
// #ff0087
|
||||||
|
DeepPink1
|
||||||
|
// #ff00af
|
||||||
|
DeepPink1_1
|
||||||
|
// #ff00d7
|
||||||
|
Magenta2_1
|
||||||
|
// #ff00ff
|
||||||
|
Magenta1
|
||||||
|
// #ff5f00
|
||||||
|
OrangeRed1
|
||||||
|
// #ff5f5f
|
||||||
|
IndianRed1
|
||||||
|
// #ff5f87
|
||||||
|
IndianRed1_1
|
||||||
|
// #ff5faf
|
||||||
|
HotPink
|
||||||
|
// #ff5fd7
|
||||||
|
HotPink_1
|
||||||
|
// #ff5fff
|
||||||
|
MediumOrchid1_1
|
||||||
|
// #ff8700
|
||||||
|
DarkOrange
|
||||||
|
// #ff875f
|
||||||
|
Salmon1
|
||||||
|
// #ff8787
|
||||||
|
LightCoral
|
||||||
|
// #ff87af
|
||||||
|
PaleVioletRed1
|
||||||
|
// #ff87d7
|
||||||
|
Orchid2
|
||||||
|
// #ff87ff
|
||||||
|
Orchid1
|
||||||
|
// #ffaf00
|
||||||
|
Orange1
|
||||||
|
// #ffaf5f
|
||||||
|
SandyBrown
|
||||||
|
// #ffaf87
|
||||||
|
LightSalmon1
|
||||||
|
// #ffafaf
|
||||||
|
LightPink1
|
||||||
|
// #ffafd7
|
||||||
|
Pink1
|
||||||
|
// #ffafff
|
||||||
|
Plum1
|
||||||
|
// #ffd700
|
||||||
|
Gold1
|
||||||
|
// #ffd75f
|
||||||
|
LightGoldenrod2_1
|
||||||
|
// #ffd787
|
||||||
|
LightGoldenrod2_2
|
||||||
|
// #ffd7af
|
||||||
|
NavajoWhite1
|
||||||
|
// #ffd7d7
|
||||||
|
MistyRose1
|
||||||
|
// #ffd7ff
|
||||||
|
Thistle1
|
||||||
|
// #ffff00
|
||||||
|
Yellow1
|
||||||
|
// #ffff5f
|
||||||
|
LightGoldenrod1
|
||||||
|
// #ffff87
|
||||||
|
Khaki1
|
||||||
|
// #ffffaf
|
||||||
|
Wheat1
|
||||||
|
// #ffffd7
|
||||||
|
Cornsilk1
|
||||||
|
// #ffffff
|
||||||
|
Grey100
|
||||||
|
// #080808
|
||||||
|
Grey3
|
||||||
|
// #121212
|
||||||
|
Grey7
|
||||||
|
// #1c1c1c
|
||||||
|
Grey11
|
||||||
|
// #262626
|
||||||
|
Grey15
|
||||||
|
// #303030
|
||||||
|
Grey19
|
||||||
|
// #3a3a3a
|
||||||
|
Grey23
|
||||||
|
// #444444
|
||||||
|
Grey27
|
||||||
|
// #4e4e4e
|
||||||
|
Grey30
|
||||||
|
// #585858
|
||||||
|
Grey35
|
||||||
|
// #626262
|
||||||
|
Grey39
|
||||||
|
// #6c6c6c
|
||||||
|
Grey42
|
||||||
|
// #767676
|
||||||
|
Grey46
|
||||||
|
// #808080
|
||||||
|
Grey50
|
||||||
|
// #8a8a8a
|
||||||
|
Grey54
|
||||||
|
// #949494
|
||||||
|
Grey58
|
||||||
|
// #9e9e9e
|
||||||
|
Grey62
|
||||||
|
// #a8a8a8
|
||||||
|
Grey66
|
||||||
|
// #b2b2b2
|
||||||
|
Grey70
|
||||||
|
// #bcbcbc
|
||||||
|
Grey74
|
||||||
|
// #c6c6c6
|
||||||
|
Grey78
|
||||||
|
// #d0d0d0
|
||||||
|
Grey82
|
||||||
|
// #dadada
|
||||||
|
Grey85
|
||||||
|
// #e4e4e4
|
||||||
|
Grey89
|
||||||
|
// #eeeeee
|
||||||
|
Grey93
|
||||||
|
)
|
|
@ -0,0 +1,66 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RGB struct {
|
||||||
|
Red int
|
||||||
|
Green int
|
||||||
|
Blue int
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustParseHex(hexColor string) *RGB {
|
||||||
|
c, err := ParseHex(hexColor)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseHex(hexColor string) (*RGB, error) {
|
||||||
|
hexColor = strings.TrimPrefix(hexColor, "#")
|
||||||
|
|
||||||
|
// Convert to individual parts
|
||||||
|
var rh, gh, bh string
|
||||||
|
switch len(hexColor) {
|
||||||
|
case 3:
|
||||||
|
rh, gh, bh = hexColor[:1]+hexColor[:1], hexColor[1:2]+hexColor[1:2], hexColor[2:3]+hexColor[2:3]
|
||||||
|
case 6:
|
||||||
|
rh, gh, bh = hexColor[0:2], hexColor[2:4], hexColor[4:6]
|
||||||
|
default:
|
||||||
|
return nil, errors.New("invalid hex string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to bytes
|
||||||
|
rb, err := hex.DecodeString(rh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gb, err := hex.DecodeString(gh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bb, err := hex.DecodeString(bh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RGB{
|
||||||
|
Red: int(rb[0]),
|
||||||
|
Green: int(gb[0]),
|
||||||
|
Blue: int(bb[0]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns an r;g;b representation of an RGB
|
||||||
|
func (r *RGB) String() string {
|
||||||
|
return r.format()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RGB) format() string {
|
||||||
|
return fmt.Sprintf("%d;%d;%d", r.Red, r.Green, r.Blue)
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParseHex(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
Name string
|
||||||
|
Hex string
|
||||||
|
RGB *RGB
|
||||||
|
Error bool
|
||||||
|
}{
|
||||||
|
{Name: "WhiteShort", Hex: "FFF", RGB: &RGB{255, 255, 255}, Error: false},
|
||||||
|
{Name: "WhiteLong", Hex: "FFFFFF", RGB: &RGB{255, 255, 255}, Error: false},
|
||||||
|
{Name: "WhiteInvalid", Hex: "F", RGB: nil, Error: true},
|
||||||
|
{Name: "InvalidHex", Hex: "ZZZ", RGB: nil, Error: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
rgb, err := ParseHex(tc.Hex)
|
||||||
|
if err != nil {
|
||||||
|
if tc.Error {
|
||||||
|
return // Pass
|
||||||
|
}
|
||||||
|
t.Log(err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if rgb.Red != tc.RGB.Red || rgb.Green != tc.RGB.Green || rgb.Blue != tc.RGB.Blue {
|
||||||
|
t.Logf("hex was parsed incorrectly: parsed %#v vs expected %#v", rgb, tc.RGB)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Interface guard
|
||||||
|
var _ Color = &True{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
trueFG = "38;2;"
|
||||||
|
trueBG = "48;2;"
|
||||||
|
)
|
||||||
|
|
||||||
|
type True struct {
|
||||||
|
FG *RGB
|
||||||
|
BG *RGB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTrue returns a new Color with RGB FG and BG
|
||||||
|
func NewTrue(fg, bg *RGB) *True {
|
||||||
|
return &True{
|
||||||
|
FG: fg,
|
||||||
|
BG: bg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the sum of a Color's Attributes
|
||||||
|
func (t *True) String() string {
|
||||||
|
return fmt.Sprintf("%d", t.FG.Red+t.FG.Green+t.FG.Blue+t.BG.Red+t.BG.Green+t.BG.Blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *True) wrap(s string) string {
|
||||||
|
return t.format() + s + t.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *True) format() string {
|
||||||
|
return fmt.Sprintf("%s[%s%sm%s[%s%sm", escape, trueFG, t.FG.format(), escape, trueBG, t.BG.format())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *True) unformat() string {
|
||||||
|
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format returns a string wrapped in the true color
|
||||||
|
func (t *True) Format(text string) string {
|
||||||
|
return t.wrap(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatf returns a formatted string wrapped in the true color
|
||||||
|
func (t *True) Formatf(text string, v ...interface{}) string {
|
||||||
|
return t.wrap(fmt.Sprintf(text, v...))
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Console = &Logger{
|
Console = &Logger{
|
||||||
Writer: os.Stdout,
|
Writer: os.Stderr,
|
||||||
Level: INFO,
|
Level: INFO,
|
||||||
Format: FormatOptions{
|
Format: FormatOptions{
|
||||||
MessageColor: true,
|
MessageColor: true,
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module gitea.com/jolheiser/beaver
|
module go.jolheiser.com/beaver
|
||||||
|
|
||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
|
|
40
level.go
40
level.go
|
@ -3,9 +3,8 @@ package beaver
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.com/jolheiser/beaver/color"
|
"go.jolheiser.com/beaver/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bold = color.New(color.Bold)
|
var bold = color.New(color.Bold)
|
||||||
|
@ -63,7 +62,7 @@ func (l Level) Prefix() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color returns a Level's color, defaulting to bold
|
// Color returns a Level's color, defaulting to bold
|
||||||
func (l Level) Color() *color.Color {
|
func (l Level) Color() color.Color {
|
||||||
switch l {
|
switch l {
|
||||||
case TRACE:
|
case TRACE:
|
||||||
return color.Trace
|
return color.Trace
|
||||||
|
@ -105,38 +104,3 @@ func ParseLevel(level string) Level {
|
||||||
}
|
}
|
||||||
return INFO
|
return INFO
|
||||||
}
|
}
|
||||||
|
|
||||||
func timePrefix(t time.Time) string {
|
|
||||||
var buf = &[]byte{}
|
|
||||||
year, month, day := t.Date()
|
|
||||||
itoa(buf, int(month), 2)
|
|
||||||
*buf = append(*buf, '/')
|
|
||||||
itoa(buf, day, 2)
|
|
||||||
*buf = append(*buf, '/')
|
|
||||||
itoa(buf, year, 4)
|
|
||||||
*buf = append(*buf, ' ')
|
|
||||||
|
|
||||||
hour, min, sec := t.Clock()
|
|
||||||
itoa(buf, hour, 2)
|
|
||||||
*buf = append(*buf, ':')
|
|
||||||
itoa(buf, min, 2)
|
|
||||||
*buf = append(*buf, ':')
|
|
||||||
itoa(buf, sec, 2)
|
|
||||||
|
|
||||||
return string(*buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func itoa(buf *[]byte, i int, wid int) {
|
|
||||||
var b [20]byte
|
|
||||||
bp := len(b) - 1
|
|
||||||
for i >= 10 || wid > 1 {
|
|
||||||
wid--
|
|
||||||
q := i / 10
|
|
||||||
b[bp] = byte('0' + i - q*10)
|
|
||||||
bp--
|
|
||||||
i = q
|
|
||||||
}
|
|
||||||
// i < 10
|
|
||||||
b[bp] = byte('0' + i)
|
|
||||||
*buf = append(*buf, b[bp:]...)
|
|
||||||
}
|
|
||||||
|
|
13
logger.go
13
logger.go
|
@ -5,10 +5,9 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.com/jolheiser/beaver/color"
|
"go.jolheiser.com/beaver/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is a base Beaver logger
|
// Logger is a base Beaver logger
|
||||||
|
@ -41,12 +40,12 @@ func New(writer io.Writer, level Level, opts FormatOptions) *Logger {
|
||||||
|
|
||||||
func (l *Logger) write(level Level, text string) {
|
func (l *Logger) write(level Level, text string) {
|
||||||
if l.Level <= level {
|
if l.Level <= level {
|
||||||
_, file, line, _ := runtime.Caller(l.skip)
|
|
||||||
var message string
|
var message string
|
||||||
if l.Format.TimePrefix {
|
if l.Format.TimePrefix {
|
||||||
message += color.Time.Format(timePrefix(time.Now())) + " "
|
message += color.Time.Format(time.Now().Format("01/02/2006 15:04:05")) + " "
|
||||||
}
|
}
|
||||||
if l.Format.StackPrefix {
|
if l.Format.StackPrefix {
|
||||||
|
_, file, line, _ := runtime.Caller(l.skip)
|
||||||
idx := len(file) - l.Format.StackLimit
|
idx := len(file) - l.Format.StackLimit
|
||||||
if l.Format.StackLimit <= 0 {
|
if l.Format.StackLimit <= 0 {
|
||||||
idx = 0
|
idx = 0
|
||||||
|
@ -75,11 +74,7 @@ func (l *Logger) write(level Level, text string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) log(level Level, v ...interface{}) {
|
func (l *Logger) log(level Level, v ...interface{}) {
|
||||||
var args = make([]string, len(v))
|
text := fmt.Sprint(v...)
|
||||||
for i := 0; i < len(v); i++ {
|
|
||||||
args[i] = fmt.Sprintf("%v", v[i])
|
|
||||||
}
|
|
||||||
text := strings.Join(args, " ")
|
|
||||||
l.write(level, text)
|
l.write(level, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue