Awesome
is
is
provides numerous detectors for checking the states of environment (build, executive, ...).
Features
is.State(which) bool
: the universal detector entry - viaRegisterStateGetter(state string, getter func() bool)
to add your own ones. Since v0.5.11is.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
, andis.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()
- easier
- exec: Run, RunWithOutput, Sudo, ...
- 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 bydlv
.DebugBuild
:-tags=delve
is set at building.DebugMode
:--debug
is specified at command line.
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()
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))
ctx, cancel := context.WithCancel(context.Background())
catcher := is.Signals().Catch()
catcher.
WithPrompt("Press CTRL-C to quit...").
WithOnLoop(dbStarter, cacheStarter, mqStarter).
WithOnSignalCaught(func(sig os.Signal, wg *sync.WaitGroup) {
println()
slog.Info("signal caught", "sig", sig)
cancel() // cancel user's loop, see Wait(...)
}).
Wait(func(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
slog.Debug("entering looper's loop...")
go func() {
// to terminate this app after a while automatically:
time.Sleep(10 * time.Second)
stopChan <- os.Interrupt
}()
<-ctx.Done() // waiting until any os signal caught
wgDone.Done() // and complete myself
})
}
func dbStarter(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
// initializing database connections...
// ...
wgDone.Done()
}
func cacheStarter(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
// initializing redis cache connections...
// ...
wgDone.Done()
}
func mqStarter(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
// initializing message queue connections...
// ...
wgDone.Done()
}
Result is similar with:
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
- closers
Build Tags
Some functions want special buildtags presented. These are including:
verbose
: See VerboseBuild, ...delve
: See DebugBuild, ...k8s
: See K8sBuildistio
: See IstioBuilddocker
: 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:
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.
`))
}
Basics (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{})
}
Basics (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(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()
}).
Wait(func(stopChan chan<- os.Signal, wgShutdown *sync.WaitGroup) {
logz.Debug("entering looper's loop...")
server.WithOnShutdown(func(err error, ss net.Server) { wgShutdown.Done() })
err := server.ListenAndServe(ctx, nil)
if err != nil {
logz.Fatal("server serve failed", "err", err)
}
})
}
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