hzDocs

hedzr/is (-go)

`is` detectors and its basic supports in golang

Intro to is

is provides numerous detectors for checking the states of environment (build, executive, ...).

Features

  • is.State(which) bool: the universal detector entry - via RegisterStateGetter(state string, getter func() bool) to add your own ones. Since v0.5.11
  • is.Env() holds a global struct for CLI app basic states, such as: verbose/quiet/debug/trace....
    • DebugMode/DebugLevel, TraceMode/TraceLevel, ColorMode, ...
  • is.InDebugging() bool, is.InTesting() bool, and is.InTracing() bool, ....
  • is.DebugBuild() bool.
  • is.K8sBuild() bool, is.DockerBuild() bool, ....
  • is.ColoredTty() bool, ....
  • is.Color() to get an indexer for the functions in our term/color subpackage, ...
  • Terminal Colorizer, Detector, unescape tools.
  • stringtool: RandomStringPure, case-converters ...
  • basics: closable, closer, signals.
    • easier Press any key to exit... prompt: is.Signals().Catch()
  • exec: Run, RunWithOutput, Sudo, ...
  • go1.23.7+ required since v0.7.0
  • go 1.22.7+ required

To using environment detecting utilities better and smoother, some terminal (and stringtool, basics) tools are bundled together.

Since v0.6.0, is.InDebugging() checks if the running process' parent is dlv. The old DebugMode and DebugBuild are still work:

  • InDebugging: checks this process is being debugged by dlv.
  • DebugBuild: -tags=delve is set at building.
  • DebugMode: --debug is specified at command line.

Since v0.8.27, basics.Signals().Catcher().WaitFor() wants ctx param passed in.

Usages

package main
 
import (
    "context"
    "fmt"
    "log/slog"
    "os"
    "sync"
    "time"
 
    "github.com/hedzr/is"
    "github.com/hedzr/is/basics"
    "github.com/hedzr/is/term/color"
)
 
func main() {
    // defer basics.Close() // uncomment if not using Catcher.WaitFor and/or cmdr.v2
 
    is.RegisterStateGetter("custom", func() bool { return is.InVscodeTerminal() })
 
    println(is.InTesting())
    println(is.State("in-testing"))
    println(is.State("custom")) // detects a state with custom detector
    println(is.Env().GetDebugLevel())
    if is.InDebugMode() {
        slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, Level: slog.LevelDebug})))
    }
 
    // or:
    //    is.Color().GetColorTranslator().Translate("<b>bold</b>")
    fmt.Printf("%v", color.GetCPT().Translate(`<code>code</code> | <kbd>CTRL</kbd>
        <b>bold / strong / em</b>
        <i>italic / cite</i>
        <u>underline</u>
        <mark>inverse mark</mark>
        <del>strike / del </del>
        <font color="green">green text</font>
`, color.FgDefault))
 
    var cancelled int32
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
 
    catcher := is.Signals().Catch()
    catcher.
        WithPrompt("Press CTRL-C to quit...").
        // WithOnLoopFunc(dbStarter, cacheStarter, mqStarter).
        WithPeripherals(&dbMgr{}).
        WithOnSignalCaught(func(ctx context.Context, sig os.Signal, wg *sync.WaitGroup) {
            println()
            slog.Info("signal caught", "sig", sig)
            cancel() // cancel user's loop, see Wait(...)
        }).
        WaitFor(ctx, func(ctx context.Context, closer func()) {
            slog.Debug("entering looper's loop...")
            defer close() // notify catcher we want to shutdown
            // to terminate this app after a while automatically:
            time.Sleep(10 * time.Second)
 
            if atomic.CompareAndSwapInt32(&cancelled, 0, 1) {
                is.PressAnyKeyToContinue(os.Stdin)
            }
        })
}
 
type dbMgr struct{}
 
func (*dbMgr) Close()                           {}         // before app terminatine
func (*dbMgr) Open(context.Context) (err error) { return } // ran before catcher.WaitFor()

Result is similar with:

image-20240113071930661

NOTE that is.Signals().Catch() will produce a prompt and enter a infinite loop to wait for user's keystroke pressed.

Lists

is.Terminal(os.Stdout)

The partials:

  • is.InDebugging / InDebugMode

  • is.DebuggerAttached (relyes on delve tag)

  • is.InTracing / InTestingT

  • is.InTesting / InTestingT

  • is.InDevelopingTime

  • is.InVscodeTerminal

  • is.InK8s

  • is.InIstio

  • is.InDocker / InDockerEnvSimple

  • Build

    • is.K8sBuild
    • is.IstioBuild
    • is.DockerBuild
    • is.VerboseBuild
    • is.DebugBuild
    • buildtags.IsBuildTagExists
  • States / Env

    • VerboseModeEnabled
    • GetVerboseLevel / SetVerboseMode / SetVerboseLevel
    • QuietModeEnabled
    • GetQuietLevel / SetQuietMode / SetQuietLevel
    • NoColorMode
    • GetNoColorLevel / SetNoColorMode / SetNoColorLevel
    • DebugMode
    • GetDebugLevel / SetDebugMode / SetDebugLevel
    • Tracing
    • TraceMode
    • GetTraceLevel / SetTraceMode / SetTraceLevel
  • Terminal / Tty

    • is.Terminal(file)
    • is.TerminalFd(fd)
    • is.Tty(wr)
    • is.ColoredTty(wr)
    • is.AnsiEscaped(s) (IsTtyEscaped(s))
    • StripEscapes(s)
    • ReadPassword
    • GetTtySize
    • is.GetTtySizeByName(filename) (cols,rows,err)
    • is.GetTtySizeByFile(file) (cols,rows,err)
    • is.GetTtySizeByFd(fd) (cols,rows,err)
    • StartupByDoubleClick() bool
  • [Special] Terminal / Color

    • escaping tools: GetCPT()/GetCPTC()/GetCPTNC()
    • Highlight, Dimf, Text, Dim, ToDim, ToHighlight, ToColor, ...
  • Basics

    • closers
      • Peripheral, Closable, Closer
      • RegisterClosable
      • RegisterClosers
      • RegisterCloseFns
    • is.Signals().Catcher()
    • is.FileExists(filepath)
    • is.ToBool, StringToBool

Build Tags

Some functions want special buildtags presented. These are including:

  • verbose: See VerboseBuild, ...
  • delve: See DebugBuild, ...
  • k8s: See K8sBuild
  • istio: See IstioBuild
  • docker: See DockerBuild
  • ...
  • buildtags.IsBuildTagExists(tag) bool

Colorizes

The test codes:

import "github.com/hedzr/is/term/color"
 
func TestGetCPT(t *testing.T) {
t.Logf("%v", color.GetCPT().Translate(`<code>code</code> | <kbd>CTRL</kbd>
    <b>bold / strong / em</b>
    <i>italic / cite</i>
    <u>underline</u>
    <mark>inverse mark</mark>
    <del>strike / del </del>
    <font color="green">green text</font>
    `, color.FgDefault))
}

Result:

image-20231107100150520

And more:

func TestStripLeftTabs(t *testing.T) {
t.Logf("%v", color.StripLeftTabs(`
 
        <code>code</code>
    NC Cool
     But it's tight.
      Hold On!
    Hurry Up.
    `))
}
 
func TestStripHTMLTags(t *testing.T) {
t.Logf("%v", color.StripHTMLTags(`
 
        <code>code</code>
    NC Cool
     But it's tight.
      Hold On!
    Hurry Up.
    `))
}

Cursor

Since v0.8+, A new color.Cursor object can be initialized by color.New(), which support format the colorful text with streaming calls, for console/tty.

See the online docs for more usages.

The examples are:

 
func ExampleNew() {
    // start a color text builder
    var c = color.New()
 
    // specially for running on remote ci server
    if states.Env().IsNoColorMode() {
        states.Env().SetNoColorMode(true)
    }
 
    // paint and get the result (with ansi-color-seq ready)
    var result = c.Println().
        Color16(color.FgRed).
        Printf("hello, %s.", "world").Println().
        SavePos(). // try using SavePosNow()
        Println("x").
        Color16(color.FgGreen).Printf("hello, %s.\n", "world").
        Color256(160).Printf("[160] hello, %s.\n", "world").
        Color256(161).Printf("[161] hello, %s.\n", "world").
        Color256(162).Printf("[162] hello, %s.\n", "world").
        Color256(163).Printf("[163] hello, %s.\n", "world").
        Color256(164).Printf("[164] hello, %s.\n", "world").
        Color256(165).Printf("[165] hello, %s.\n", "world").
        Up(3).Echo(" ERASED "). // try using UpNow()
        RGB(211, 211, 33).Printf("[16m] hello, %s.", "world").
        Println().
        RestorePos(). // try using RestorePosNow()
        Println("z").
        Down(8). // try using DownNow()
        Println("DONE").
        Build()
 
        // and render the result
    fmt.Println(result)
 
    // For most of ttys, the output looks like:
    //
    // \033[31mhello, world.\033[0m
    // \033[sx
    // \033[32mhello, world.
    // \033[38;5;160m[160] hello, world.
    // \033[38;5;161m[161] hello, world.
    // \033[38;5;162m[162] hello, world.
    // \033[38;5;163m[163] hello, world.
    // \033[38;5;164m[164] hello, world.
    // \033[38;5;165m[165] hello, world.
    // \033[0m\033[3A ERASED \033[38;2;211;211;33m[16m] hello, world.
    // \033[uz
    // \033[8BDONE
}
 
func ExampleCursor_Color16() {
    // another colorful builfer
    var c = color.New()
    fmt.Println(c.Color16(color.FgRed).
        Printf("hello, %s.", "world").Println().Build())
    // Output: \033[31mhello, world.\033[0m
}
 
func ExampleCursor_Color() {
    // another colorful builfer
    var c = color.New()
    fmt.Println(c.Color(color.FgRed, "hello, %s.", "world").Build())
    // Output: \033[31mhello, world.\033[0m
}
 
func ExampleCursor_Bg() {
    // another colorful builfer
    var c = color.New()
    fmt.Println(c.Bg(color.BgRed, "hello, %s.", "world").Build())
    // Output: \033[41mhello, world.\033[0m
}
 
func ExampleCursor_Effect() {
    // another colorful builfer
    var c = color.New()
    fmt.Println(c.Effect(color.BgDim, "hello, %s.", "world").Build())
    // Output: \033[2mhello, world.\033[0m
}
 
func ExampleCursor_Color256() {
    // another colorful builfer
    var c = color.New()
    fmt.Print(c.
     Color256(163).Printf("[163] hello, %s.\n", "world").
     Color256(164).Printf("[164] hello, %s.\n", "world").
     Color256(165).Printf("[165] hello, %s.\n", "world").
     Build())
    // Output:
    // \033[38;5;163m[163] hello, world.
    // \033[38;5;164m[164] hello, world.
    // \033[38;5;165m[165] hello, world.
}
 
func ExampleCursor_RGB() {
    // another colorful builfer
    var c = color.New()
    fmt.Print(c.
     RGB(211, 211, 33).Printf("[16m] hello, %s.\n", "world").
     BgRGB(211, 211, 33).Printf("[16m] hello, %s.\n", "world").
     Build())
    // Output:
    // \033[38;2;211;211;33m[16m] hello, world.
    // \033[48;2;211;211;33m[16m] hello, world.
}
 
func ExampleCursor_EDim() {
    // another colorful builfer
    var c = color.New()
    fmt.Print(c. // Color16(color.FgRed).
      EDim("[DIM] hello, %s.\n", "world").String())
    // Output:
    // \033[2m[DIM] hello, world.
    // \033[0m
}
 
func ExampleCursor_Black() {
    // another colorful builfer
    var c = color.New()
    fmt.Print(c. // Color16(color.FgRed).
      Black("[BLACK] hello, %s.\n", "world").String())
    // Output:
    // \033[30m[BLACK] hello, world.
    // \033[0m
}
 
func ExampleCursor_BgBlack() {
    // another colorful builfer
    var c = color.New()
    fmt.Print(c. // Color16(color.FgRed).
      BgBlack("[BGBLACK] hello, %s.\n", "world").String())
    // Output:
    // \033[40m[BGBLACK] hello, world.
    // \033[0m
}
 
func ExampleCursor_Translate() {
 // another colorful builfer
 var c = color.New()
 fmt.Print(c. // Color16(color.FgRed).
   Translate(`<code>code</code> | <kbd>CTRL</kbd>
  <b>bold / strong / em</b>
  <i>italic / cite</i>
  <u>underline</u>
  <mark>inverse mark</mark>
  <del>strike / del </del>
  <font color="green">green text</font>
  `).String())
 // Output:
 // \033[51;1mcode\033[0m\033[39m | \033[51;1mCTRL\033[0m\033[39m
 //  \033[1mbold / strong / em\033[0m\033[39m
 //  \033[3mitalic / cite\033[0m\033[39m
 //  \033[4munderline\033[0m\033[39m
 //  \033[7minverse mark\033[0m\033[39m
 //  \033[9mstrike / del \033[0m\033[39m
 //  \033[32mgreen text\033[0m\033[39m
}
 
func ExampleCursor_StripLeftTabsColorful() {
 // another colorful builfer
 var c = color.New()
 fmt.Print(c. // Color16(color.FgRed).
   StripLeftTabsColorful(`
  <code>code</code> | <kbd>CTRL</kbd>
  <b>bold / strong / em</b>
  <i>italic / cite</i>
  <u>underline</u>
  <mark>inverse mark</mark>
  <del>strike / del </del>
  <font color="green">green text</font>
  `).String())
 // Output:
 // \033[51;1mcode\033[0m\033[0m | \033[51;1mCTRL\033[0m\033[0m
 // \033[1mbold / strong / em\033[0m\033[0m
 // \033[3mitalic / cite\033[0m\033[0m
 // \033[4munderline\033[0m\033[0m
 // \033[7minverse mark\033[0m\033[0m
 // \033[9mstrike / del \033[0m\033[0m
 // \033[32mgreen text\033[0m\033[0m
}

color subpackage

Package color provides a wrapped standard output device like printf but with colored enhancements.

The main types are Cursor and Translator.

Cursor allows formatting colorful text and moving cursor to another coordinate.

New will return a Cursor object.

RowsBlock is another cursor controller, which can treat the current line and following lines as a block and updating these lines repeatedly. This feature will help the progressbar writers or the continuous lines updater.

Translator is a text and tiny HTML tags translator to convert these markup text into colorful console text sequences. GetCPT can return a smart translator which translate colorful text or strip the ansi escaped sequence from result text if states.Env().IsNoColorMode() is true.

Color is an interface type to represent a terminal color object, which can be serialized to ansi escaped sequence directly by [Color.Color].

To create a Color object, there are several ways:

Integrated with cmdr

Closers

The Closers() collects all closable objects and allow shutting down them at once.

package main
 
import (
    "os"
 
    "github.com/hedzr/is/basics"
)
 
type redisHub struct{}
 
func (s *redisHub) Close() {
    // close the connections to redis servers
    println("redis connections closed")
}
 
func main() {
    defer basics.Close()
 
    tmpFile, _ := os.CreateTemp(os.TempDir(), "1*.log")
    basics.RegisterClosers(tmpFile)
 
    basics.RegisterCloseFn(func() {
        // do some shutdown operations here
        println("close single functor")
    })
 
    basics.RegisterPeripheral(&redisHub{})
}

Signals

Signals() could catch OS signals and entering a infinite loop.

For example, a tcp server could be:

package main
 
import (
    "context"
    "os"
    "sync"
 
    "github.com/hedzr/go-socketlib/net"
    "github.com/hedzr/is"
    logz "github.com/hedzr/logg/slog"
)
 
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
 
    logger := logz.New("new-dns")
    server := net.NewServer(":7099",
        net.WithServerOnListening(func(ss net.Server, l stdnet.Listener) {
            go runClient(ctx, ss, l)
        }),
        net.WithServerLogger(logger.WithSkip(1)),
    )
    defer server.Close()
 
    // make a catcher so that it can catch ths signals,
    catcher := is.Signals().Catch()
    catcher.
        // WithVerboseFn(func(msg string, args ...any) {
        //     logz.WithSkip(2).Verbose(fmt.Sprintf("[verbose] %s", fmt.Sprintf(msg, args...)))
        // }).
        WithOnSignalCaught(func(ctx context.Context, sig os.Signal, wg *sync.WaitGroup) {
            println()
            logz.Debug("signal caught", "sig", sig)
            if err := server.Shutdown(); err != nil {
                logz.Error("server shutdown error", "err", err)
            }
            cancel()
        }).
        WaitFor(ctx, func(ctx context.Context, closer func()) {
            logz.Debug("entering looper's loop...")
 
            server.WithOnShutdown(func(err error, ss net.Server) { closer() })
            err := server.ListenAndServe(ctx, nil)
            if err != nil {
                logz.Fatal("server serve failed", "err", err)
            } else {
                closer()
            }
        })
}
 
func runClient(ctx context.Context, ss net.Server, l stdnet.Listener) {
    c := net.NewClient()
 
    if err := c.Dial("tcp", ":7099"); err != nil {
        logz.Fatal("connecting to server failed", "err", err, "server-endpoint", ":7099")
    }
    logz.Info("[client] connected", "server.addr", c.RemoteAddr())
    c.RunDemo(ctx)
}

some packages has stayed in progress so the above codes is just a skeleton (from go-socketlib/_examples/new-loop/main.go/v1).

Contributions

Kindly welcome, please issue me first for keeping this repo smaller.

License

under Apache 2.0

How is this guide?

Edit on GitHub

Last updated on

On this page