Skip to content

Commit

Permalink
[protip] Added new package for showing usage tips
Browse files Browse the repository at this point in the history
  • Loading branch information
andyone committed Oct 19, 2023
1 parent 10af1af commit aa1ad30
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 0 deletions.
1 change: 1 addition & 0 deletions .scripts/packages.list
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ L + netutil
* + pid
* + pluralize
* + progress
* + protip
* + rand
* + req
* + secstr
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### 12.83.0

* `[protip]` Added new package for showing usage tips
* `[fmtc]` Added method `IsTag` for color tag validation
* `[fmtutil/panel]` Added `TOP_LINE` option
* `[fmtutil/panel]` Added `DefaultOptions` variable to set default options for all panels
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ go get -u github.com/essentialkaos/ek/v12
* [`pid`](https://kaos.sh/g/ek.v12/pid) — Package for working with PID files
* [`pluralize`](https://kaos.sh/g/ek.v12/pluralize) — Package provides methods for pluralization
* [`progress`](https://kaos.sh/g/ek.v12/progress) — Package provides methods and structs for creating terminal progress bar
* [`protip`](https://kaos.sh/g/ek.v12/protip) — Package for displaying usage tips
* [`rand`](https://kaos.sh/g/ek.v12/rand) — Package for generating random data
* [`req`](https://kaos.sh/g/ek.v12/req) — Package simplify working with an HTTP requests
* [`secstr`](https://kaos.sh/g/ek.v12/secstr) — Package provides methods and structs for working with protected (secure) strings
Expand Down
51 changes: 51 additions & 0 deletions protip/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package protip

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

func ExampleAdd() {
// Add simple tip
Add(&Tip{
Title: "Tip #1",
Message: `Tip message`,
})

// Add tip with custom weight (≠ 0.5)
Add(&Tip{
Title: "Tip #2",
Message: `Tip message`,
Weight: 0.1,
})

// Add tip with custom weight (≠ 0.5) and color
Add(&Tip{
Title: "Tip #3",
Message: `Tip message`,
Weight: 0.8,
ColorTag: "{b}",
})
}

func ExampleShow() {
// Add simple tip
Add(&Tip{
Title: "Tip #1",
Message: `Tip message`,
})

// Increase default probability to 50%
Probability = 0.5

// Set default color to green
ColorTag = "{g}"

// Try to show tip
Show(false)

// Force show
Show(true)
}
120 changes: 120 additions & 0 deletions protip/protip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Package protip provides methods for displaying usage tips
package protip

import (
"math/rand"

"github.com/essentialkaos/ek/v12/fmtc"
"github.com/essentialkaos/ek/v12/fmtutil/panel"
"github.com/essentialkaos/ek/v12/strutil"
)

// ////////////////////////////////////////////////////////////////////////////////// //

// Tip contains basic tip content
type Tip struct {
Title string // Tip title (required)
Message string // Tip message (required)

ColorTag string // Custom tip color (optional)
Weight float64 // Custom tip weight (optional)
}

// Tips contains tips data
type Tips struct {
data []*Tip
selectors []int
maxWeight int
}

// ////////////////////////////////////////////////////////////////////////////////// //

// Probability is showing probability coefficient (0 ← less | more → 1)
var Probability = 0.25

// ColorTag is default panel color tag
var ColorTag = "{#75}"

// ////////////////////////////////////////////////////////////////////////////////// //

var collection *Tips

// ////////////////////////////////////////////////////////////////////////////////// //

// Add adds one or more tips to collection
func Add(tips ...*Tip) {
if collection == nil {
collection = &Tips{}
}

for _, tip := range tips {
if tip == nil || tip.Title == "" ||
tip.Message == "" || !fmtc.IsTag(tip.ColorTag) {
continue
}

if tip.Weight == 0 {
tip.Weight = 0.5
}

collection.data = append(collection.data, tip)
}
}

// Show shows random tip if required
func Show(force bool) bool {
if collection == nil || len(collection.data) == 0 {
return false
}

if rand.Float64() > Probability && !force {
return false
}

if len(collection.selectors) != len(collection.data) {
collection.selectors = make([]int, len(collection.data))
collection.maxWeight = 0

for i, tip := range collection.data {
collection.maxWeight += int(tip.Weight * 100)
collection.selectors[i] = collection.maxWeight
}
}

rnd := rand.Intn(collection.maxWeight) + 1
index := searchInts(collection.selectors, rnd)
tip := collection.data[index]

color := strutil.Q(tip.ColorTag, ColorTag)
color = strutil.B(fmtc.IsTag(color), color, "{#75}")

panel.Panel(
"❏ PROTIP", color, tip.Title, tip.Message,
panel.BOTTOM_LINE, panel.TOP_LINE, panel.INDENT_OUTER,
)

return true
}

// ////////////////////////////////////////////////////////////////////////////////// //

// searchInts improved searchInts version
//
// Original: https://github.com/mroth/weightedrand
func searchInts(a []int, x int) int {
var i int

j := len(a)

for i < j {
h := int(uint(i+j) >> 1)

if a[h] < x {
i = h + 1
} else {
j = h
}
}

return i
}
65 changes: 65 additions & 0 deletions protip/protip_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package protip

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

import (
"testing"

. "github.com/essentialkaos/check"
)

// ////////////////////////////////////////////////////////////////////////////////// //

func Test(t *testing.T) { TestingT(t) }

type TipSuite struct{}

// ////////////////////////////////////////////////////////////////////////////////// //

var _ = Suite(&TipSuite{})

// ////////////////////////////////////////////////////////////////////////////////// //

func (s *TipSuite) TestAdd(c *C) {
c.Assert(collection, IsNil)

var tip *Tip
Add(tip)
c.Assert(collection, NotNil)
c.Assert(collection.data, HasLen, 0)

tip = &Tip{}
Add(tip)
c.Assert(collection.data, HasLen, 0)

tip.Title = "TEST"
Add(tip)
c.Assert(collection.data, HasLen, 0)

tip.Message = "Test message"
Add(tip)
c.Assert(collection.data, HasLen, 1)
c.Assert(collection.data[0].Weight, Equals, 0.5)
}

func (s *TipSuite) TestShow(c *C) {
collection = nil

c.Assert(Show(true), Equals, false)

Add(&Tip{Title: "Test #1", Message: "Test", Weight: 0.99, ColorTag: "{r}"})
Add(&Tip{Title: "Test #2", Message: "Test", Weight: 0.01})

Probability = 0.0

c.Assert(Show(false), Equals, false)

Probability = 1.0

c.Assert(Show(true), Equals, true)
}

0 comments on commit aa1ad30

Please sign in to comment.