cmd/stdiscosrv: New discovery server (fixes #4618)
This is a new revision of the discovery server. Relevant changes and non-changes: - Protocol towards clients is unchanged. - Recommended large scale design is still to be deployed nehind nginx (I tested, and it's still a lot faster at terminating TLS). - Database backend is leveldb again, only. It scales enough, is easy to setup, and we don't need any backend to take care of. - Server supports replication. This is a simple TCP channel - protect it with a firewall when deploying over the internet. (We deploy this within the same datacenter, and with firewall.) Any incoming client announces are sent over the replication channel(s) to other peer discosrvs. Incoming replication changes are applied to the database as if they came from clients, but without the TLS/certificate overhead. - Metrics are exposed using the prometheus library, when enabled. - The database values and replication protocol is protobuf, because JSON was quite CPU intensive when I tried that and benchmarked it. - The "Retry-After" value for failed lookups gets slowly increased from a default of 120 seconds, by 5 seconds for each failed lookup, independently by each discosrv. This lowers the query load over time for clients that are never seen. The Retry-After maxes out at 3600 after a couple of weeks of this increase. The number of failed lookups is stored in the database, now and then (avoiding making each lookup a database put). All in all this means clients can be pointed towards a cluster using just multiple A / AAAA records to gain both load sharing and redundancy (if one is down, clients will talk to the remaining ones). GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4648
This commit is contained in:
21
vendor/github.com/gernest/wow/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gernest/wow/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Geofrey Ernest
|
||||
|
||||
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.
|
||||
21
vendor/github.com/gernest/wow/example/all/main.go
generated
vendored
Normal file
21
vendor/github.com/gernest/wow/example/all/main.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
//DO NOT EDIT : this file was automatically generated.
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gernest/wow"
|
||||
"github.com/gernest/wow/spin"
|
||||
)
|
||||
|
||||
var all = []spin.Name{spin.Toggle6, spin.BouncingBall, spin.Balloon, spin.Toggle, spin.Toggle12, spin.Dots5, spin.Dots6, spin.Dots7, spin.GrowVertical, spin.Noise, spin.Toggle8, spin.SimpleDots, spin.BoxBounce2, spin.Arc, spin.BouncingBar, spin.Christmas, spin.Squish, spin.Triangle, spin.Arrow3, spin.Hearts, spin.Earth, spin.Dqpb, spin.Line2, spin.SquareCorners, spin.Toggle3, spin.Toggle5, spin.Monkey, spin.Clock, spin.Shark, spin.Weather, spin.Dots2, spin.Dots3, spin.Dots12, spin.CircleHalves, spin.Arrow, spin.Moon, spin.Flip, spin.Hamburger, spin.Bounce, spin.Circle, spin.Toggle9, spin.Toggle13, spin.Toggle10, spin.Runner, spin.Dots9, spin.Line, spin.Star, spin.CircleQuarters, spin.Arrow2, spin.Smiley, spin.Dots, spin.Dots4, spin.Pipe, spin.Balloon2, spin.Dots10, spin.Dots11, spin.SimpleDotsScrolling, spin.BoxBounce, spin.Toggle4, spin.Dots8, spin.Toggle2, spin.Toggle7, spin.Toggle11, spin.Pong, spin.Star2, spin.GrowHorizontal}
|
||||
|
||||
func main() {
|
||||
for _, v := range all {
|
||||
w := wow.New(os.Stdout, spin.Get(v), " "+v.String(), wow.ForceOutput)
|
||||
w.Start()
|
||||
time.Sleep(2)
|
||||
w.Persist()
|
||||
}
|
||||
}
|
||||
18
vendor/github.com/gernest/wow/example/doge/main.go
generated
vendored
Normal file
18
vendor/github.com/gernest/wow/example/doge/main.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gernest/wow"
|
||||
"github.com/gernest/wow/spin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
w := wow.New(os.Stdout, spin.Get(spin.Dots), "Such Spins")
|
||||
w.Start()
|
||||
time.Sleep(2 * time.Second)
|
||||
w.Text("Very emojis").Spinner(spin.Get(spin.Hearts))
|
||||
time.Sleep(2 * time.Second)
|
||||
w.PersistWith(spin.Spinner{Frames: []string{"👍"}}, " Wow!")
|
||||
}
|
||||
228
vendor/github.com/gernest/wow/magefile.go
generated
vendored
Normal file
228
vendor/github.com/gernest/wow/magefile.go
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
// +build mage
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/magefile/mage/sh"
|
||||
|
||||
"github.com/magefile/mage/mg"
|
||||
)
|
||||
|
||||
// Spinners generates easy/accessible Go types for spinners from
|
||||
// cli-spinners/spinners.json.
|
||||
func Spinners() error {
|
||||
pkg := "spin"
|
||||
mg.Deps(func() error {
|
||||
_, err := os.Stat(pkg)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return os.Mkdir(pkg, 0777)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
b, err := ioutil.ReadFile("cli-spinners/spinners.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tpl, err := template.New("spinner").Funcs(helpers()).Parse(spinnersTpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err = tpl.Execute(&buf, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bo, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filepath.Join(pkg, "spinners.go"), bo, 0600)
|
||||
}
|
||||
|
||||
// ExampleAll generates example executable file to demo all spinners
|
||||
func ExampleAll() error {
|
||||
b, err := ioutil.ReadFile("cli-spinners/spinners.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate example/all/main.go
|
||||
tpl, err := template.New("example-all").Funcs(helpers()).Parse(exampleAllTpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err = tpl.Execute(&buf, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bo, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile("example/all/main.go", bo, 0600)
|
||||
}
|
||||
|
||||
func helpers() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"title": strings.Title,
|
||||
"stringify": func(a []interface{}) string {
|
||||
o := ""
|
||||
switch len(a) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return fmt.Sprintf("\"%s\"", a[0])
|
||||
default:
|
||||
for k, v := range a {
|
||||
if k == 0 {
|
||||
o += fmt.Sprintf("`%s`", v)
|
||||
} else {
|
||||
if v == "`" {
|
||||
o += fmt.Sprintf(",\"%s\"", v)
|
||||
|
||||
} else {
|
||||
o += fmt.Sprintf(",`%s`", v)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[]string{ %v }", o)
|
||||
}
|
||||
},
|
||||
"all": func(a map[string]interface{}) string {
|
||||
o := ""
|
||||
for k := range a {
|
||||
if o == "" {
|
||||
o += fmt.Sprintf("%s", strings.Title(k))
|
||||
} else {
|
||||
o += fmt.Sprintf(",%s", strings.Title(k))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("[]Name{%s}", o)
|
||||
},
|
||||
"allwithpkg": func(a map[string]interface{}) string {
|
||||
o := ""
|
||||
for k := range a {
|
||||
if o == "" {
|
||||
o += fmt.Sprintf("spin.%s", strings.Title(k))
|
||||
} else {
|
||||
o += fmt.Sprintf(",spin.%s", strings.Title(k))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("[]spin.Name{%s}", o)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const spinnersTpl = `//DO NOT EDIT : this file was automatically generated.
|
||||
package spin
|
||||
|
||||
// Spinner defines a spinner widget
|
||||
type Spinner struct{
|
||||
Name Name
|
||||
Interval int
|
||||
Frames []string
|
||||
}
|
||||
|
||||
// Name represents a name for a spinner item.
|
||||
type Name uint
|
||||
|
||||
// available spinners
|
||||
const(
|
||||
Unknown Name=iota
|
||||
{{range $k,$v:= .}}
|
||||
{{- $k|title}}
|
||||
{{end}}
|
||||
)
|
||||
|
||||
func (s Name)String()string{
|
||||
switch s{
|
||||
{{- range $k,$v:=.}}
|
||||
case {{$k|title}} :
|
||||
return "{{$k}}"
|
||||
{{- end}}
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func Get( name Name)Spinner{
|
||||
switch name{
|
||||
{{- range $k,$v:=.}}
|
||||
case {{$k|title}} :
|
||||
return Spinner{
|
||||
Name: {{$k|title}},
|
||||
Interval: {{$v.interval}},
|
||||
Frames: {{$v.frames|stringify }},
|
||||
}
|
||||
{{- end}}
|
||||
default:
|
||||
return Spinner{}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const exampleAllTpl = `//DO NOT EDIT : this file was automatically generated.
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gernest/wow"
|
||||
"github.com/gernest/wow/spin"
|
||||
)
|
||||
|
||||
var all = {{allwithpkg .}}
|
||||
|
||||
func main() {
|
||||
for _, v := range all {
|
||||
w := wow.New(os.Stdout, spin.Get(v), " "+v.String())
|
||||
w.Start()
|
||||
time.Sleep(2)
|
||||
w.Persist()
|
||||
}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
// Update updates cli-spinners to get latest changes to the spinners.json file.
|
||||
func Update() error {
|
||||
return sh.Run("git", "submodule", "update", "--remote", "cli-spinners")
|
||||
}
|
||||
|
||||
//Setup prepares the project for local development.
|
||||
//
|
||||
// This runs git submodule init && git submodule update
|
||||
func Setup() error {
|
||||
err := sh.Run("git", "submodule", "init")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sh.Run("git", "submodule", "update")
|
||||
}
|
||||
625
vendor/github.com/gernest/wow/spin/spinners.go
generated
vendored
Normal file
625
vendor/github.com/gernest/wow/spin/spinners.go
generated
vendored
Normal file
@@ -0,0 +1,625 @@
|
||||
//DO NOT EDIT : this file was automatically generated.
|
||||
package spin
|
||||
|
||||
// Spinner defines a spinner widget
|
||||
type Spinner struct {
|
||||
Name Name
|
||||
Interval int
|
||||
Frames []string
|
||||
}
|
||||
|
||||
// Name represents a name for a spinner item.
|
||||
type Name uint
|
||||
|
||||
// available spinners
|
||||
const (
|
||||
Unknown Name = iota
|
||||
Arc
|
||||
Arrow
|
||||
Arrow2
|
||||
Arrow3
|
||||
Balloon
|
||||
Balloon2
|
||||
Bounce
|
||||
BouncingBall
|
||||
BouncingBar
|
||||
BoxBounce
|
||||
BoxBounce2
|
||||
Christmas
|
||||
Circle
|
||||
CircleHalves
|
||||
CircleQuarters
|
||||
Clock
|
||||
Dots
|
||||
Dots10
|
||||
Dots11
|
||||
Dots12
|
||||
Dots2
|
||||
Dots3
|
||||
Dots4
|
||||
Dots5
|
||||
Dots6
|
||||
Dots7
|
||||
Dots8
|
||||
Dots9
|
||||
Dqpb
|
||||
Earth
|
||||
Flip
|
||||
GrowHorizontal
|
||||
GrowVertical
|
||||
Hamburger
|
||||
Hearts
|
||||
Line
|
||||
Line2
|
||||
Monkey
|
||||
Moon
|
||||
Noise
|
||||
Pipe
|
||||
Pong
|
||||
Runner
|
||||
Shark
|
||||
SimpleDots
|
||||
SimpleDotsScrolling
|
||||
Smiley
|
||||
SquareCorners
|
||||
Squish
|
||||
Star
|
||||
Star2
|
||||
Toggle
|
||||
Toggle10
|
||||
Toggle11
|
||||
Toggle12
|
||||
Toggle13
|
||||
Toggle2
|
||||
Toggle3
|
||||
Toggle4
|
||||
Toggle5
|
||||
Toggle6
|
||||
Toggle7
|
||||
Toggle8
|
||||
Toggle9
|
||||
Triangle
|
||||
Weather
|
||||
)
|
||||
|
||||
func (s Name) String() string {
|
||||
switch s {
|
||||
case Arc:
|
||||
return "arc"
|
||||
case Arrow:
|
||||
return "arrow"
|
||||
case Arrow2:
|
||||
return "arrow2"
|
||||
case Arrow3:
|
||||
return "arrow3"
|
||||
case Balloon:
|
||||
return "balloon"
|
||||
case Balloon2:
|
||||
return "balloon2"
|
||||
case Bounce:
|
||||
return "bounce"
|
||||
case BouncingBall:
|
||||
return "bouncingBall"
|
||||
case BouncingBar:
|
||||
return "bouncingBar"
|
||||
case BoxBounce:
|
||||
return "boxBounce"
|
||||
case BoxBounce2:
|
||||
return "boxBounce2"
|
||||
case Christmas:
|
||||
return "christmas"
|
||||
case Circle:
|
||||
return "circle"
|
||||
case CircleHalves:
|
||||
return "circleHalves"
|
||||
case CircleQuarters:
|
||||
return "circleQuarters"
|
||||
case Clock:
|
||||
return "clock"
|
||||
case Dots:
|
||||
return "dots"
|
||||
case Dots10:
|
||||
return "dots10"
|
||||
case Dots11:
|
||||
return "dots11"
|
||||
case Dots12:
|
||||
return "dots12"
|
||||
case Dots2:
|
||||
return "dots2"
|
||||
case Dots3:
|
||||
return "dots3"
|
||||
case Dots4:
|
||||
return "dots4"
|
||||
case Dots5:
|
||||
return "dots5"
|
||||
case Dots6:
|
||||
return "dots6"
|
||||
case Dots7:
|
||||
return "dots7"
|
||||
case Dots8:
|
||||
return "dots8"
|
||||
case Dots9:
|
||||
return "dots9"
|
||||
case Dqpb:
|
||||
return "dqpb"
|
||||
case Earth:
|
||||
return "earth"
|
||||
case Flip:
|
||||
return "flip"
|
||||
case GrowHorizontal:
|
||||
return "growHorizontal"
|
||||
case GrowVertical:
|
||||
return "growVertical"
|
||||
case Hamburger:
|
||||
return "hamburger"
|
||||
case Hearts:
|
||||
return "hearts"
|
||||
case Line:
|
||||
return "line"
|
||||
case Line2:
|
||||
return "line2"
|
||||
case Monkey:
|
||||
return "monkey"
|
||||
case Moon:
|
||||
return "moon"
|
||||
case Noise:
|
||||
return "noise"
|
||||
case Pipe:
|
||||
return "pipe"
|
||||
case Pong:
|
||||
return "pong"
|
||||
case Runner:
|
||||
return "runner"
|
||||
case Shark:
|
||||
return "shark"
|
||||
case SimpleDots:
|
||||
return "simpleDots"
|
||||
case SimpleDotsScrolling:
|
||||
return "simpleDotsScrolling"
|
||||
case Smiley:
|
||||
return "smiley"
|
||||
case SquareCorners:
|
||||
return "squareCorners"
|
||||
case Squish:
|
||||
return "squish"
|
||||
case Star:
|
||||
return "star"
|
||||
case Star2:
|
||||
return "star2"
|
||||
case Toggle:
|
||||
return "toggle"
|
||||
case Toggle10:
|
||||
return "toggle10"
|
||||
case Toggle11:
|
||||
return "toggle11"
|
||||
case Toggle12:
|
||||
return "toggle12"
|
||||
case Toggle13:
|
||||
return "toggle13"
|
||||
case Toggle2:
|
||||
return "toggle2"
|
||||
case Toggle3:
|
||||
return "toggle3"
|
||||
case Toggle4:
|
||||
return "toggle4"
|
||||
case Toggle5:
|
||||
return "toggle5"
|
||||
case Toggle6:
|
||||
return "toggle6"
|
||||
case Toggle7:
|
||||
return "toggle7"
|
||||
case Toggle8:
|
||||
return "toggle8"
|
||||
case Toggle9:
|
||||
return "toggle9"
|
||||
case Triangle:
|
||||
return "triangle"
|
||||
case Weather:
|
||||
return "weather"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func Get(name Name) Spinner {
|
||||
switch name {
|
||||
case Arc:
|
||||
return Spinner{
|
||||
Name: Arc,
|
||||
Interval: 100,
|
||||
Frames: []string{`◜`, `◠`, `◝`, `◞`, `◡`, `◟`},
|
||||
}
|
||||
case Arrow:
|
||||
return Spinner{
|
||||
Name: Arrow,
|
||||
Interval: 100,
|
||||
Frames: []string{`←`, `↖`, `↑`, `↗`, `→`, `↘`, `↓`, `↙`},
|
||||
}
|
||||
case Arrow2:
|
||||
return Spinner{
|
||||
Name: Arrow2,
|
||||
Interval: 80,
|
||||
Frames: []string{`⬆️ `, `↗️ `, `➡️ `, `↘️ `, `⬇️ `, `↙️ `, `⬅️ `, `↖️ `},
|
||||
}
|
||||
case Arrow3:
|
||||
return Spinner{
|
||||
Name: Arrow3,
|
||||
Interval: 120,
|
||||
Frames: []string{`▹▹▹▹▹`, `▸▹▹▹▹`, `▹▸▹▹▹`, `▹▹▸▹▹`, `▹▹▹▸▹`, `▹▹▹▹▸`},
|
||||
}
|
||||
case Balloon:
|
||||
return Spinner{
|
||||
Name: Balloon,
|
||||
Interval: 140,
|
||||
Frames: []string{` `, `.`, `o`, `O`, `@`, `*`, ` `},
|
||||
}
|
||||
case Balloon2:
|
||||
return Spinner{
|
||||
Name: Balloon2,
|
||||
Interval: 120,
|
||||
Frames: []string{`.`, `o`, `O`, `°`, `O`, `o`, `.`},
|
||||
}
|
||||
case Bounce:
|
||||
return Spinner{
|
||||
Name: Bounce,
|
||||
Interval: 120,
|
||||
Frames: []string{`⠁`, `⠂`, `⠄`, `⠂`},
|
||||
}
|
||||
case BouncingBall:
|
||||
return Spinner{
|
||||
Name: BouncingBall,
|
||||
Interval: 80,
|
||||
Frames: []string{`( ● )`, `( ● )`, `( ● )`, `( ● )`, `( ●)`, `( ● )`, `( ● )`, `( ● )`, `( ● )`, `(● )`},
|
||||
}
|
||||
case BouncingBar:
|
||||
return Spinner{
|
||||
Name: BouncingBar,
|
||||
Interval: 80,
|
||||
Frames: []string{`[ ]`, `[= ]`, `[== ]`, `[=== ]`, `[ ===]`, `[ ==]`, `[ =]`, `[ ]`, `[ =]`, `[ ==]`, `[ ===]`, `[====]`, `[=== ]`, `[== ]`, `[= ]`},
|
||||
}
|
||||
case BoxBounce:
|
||||
return Spinner{
|
||||
Name: BoxBounce,
|
||||
Interval: 120,
|
||||
Frames: []string{`▖`, `▘`, `▝`, `▗`},
|
||||
}
|
||||
case BoxBounce2:
|
||||
return Spinner{
|
||||
Name: BoxBounce2,
|
||||
Interval: 100,
|
||||
Frames: []string{`▌`, `▀`, `▐`, `▄`},
|
||||
}
|
||||
case Christmas:
|
||||
return Spinner{
|
||||
Name: Christmas,
|
||||
Interval: 400,
|
||||
Frames: []string{`🌲`, `🎄`},
|
||||
}
|
||||
case Circle:
|
||||
return Spinner{
|
||||
Name: Circle,
|
||||
Interval: 120,
|
||||
Frames: []string{`◡`, `⊙`, `◠`},
|
||||
}
|
||||
case CircleHalves:
|
||||
return Spinner{
|
||||
Name: CircleHalves,
|
||||
Interval: 50,
|
||||
Frames: []string{`◐`, `◓`, `◑`, `◒`},
|
||||
}
|
||||
case CircleQuarters:
|
||||
return Spinner{
|
||||
Name: CircleQuarters,
|
||||
Interval: 120,
|
||||
Frames: []string{`◴`, `◷`, `◶`, `◵`},
|
||||
}
|
||||
case Clock:
|
||||
return Spinner{
|
||||
Name: Clock,
|
||||
Interval: 100,
|
||||
Frames: []string{`🕐 `, `🕑 `, `🕒 `, `🕓 `, `🕔 `, `🕕 `, `🕖 `, `🕗 `, `🕘 `, `🕙 `, `🕚 `},
|
||||
}
|
||||
case Dots:
|
||||
return Spinner{
|
||||
Name: Dots,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠋`, `⠙`, `⠹`, `⠸`, `⠼`, `⠴`, `⠦`, `⠧`, `⠇`, `⠏`},
|
||||
}
|
||||
case Dots10:
|
||||
return Spinner{
|
||||
Name: Dots10,
|
||||
Interval: 80,
|
||||
Frames: []string{`⢄`, `⢂`, `⢁`, `⡁`, `⡈`, `⡐`, `⡠`},
|
||||
}
|
||||
case Dots11:
|
||||
return Spinner{
|
||||
Name: Dots11,
|
||||
Interval: 100,
|
||||
Frames: []string{`⠁`, `⠂`, `⠄`, `⡀`, `⢀`, `⠠`, `⠐`, `⠈`},
|
||||
}
|
||||
case Dots12:
|
||||
return Spinner{
|
||||
Name: Dots12,
|
||||
Interval: 80,
|
||||
Frames: []string{`⢀⠀`, `⡀⠀`, `⠄⠀`, `⢂⠀`, `⡂⠀`, `⠅⠀`, `⢃⠀`, `⡃⠀`, `⠍⠀`, `⢋⠀`, `⡋⠀`, `⠍⠁`, `⢋⠁`, `⡋⠁`, `⠍⠉`, `⠋⠉`, `⠋⠉`, `⠉⠙`, `⠉⠙`, `⠉⠩`, `⠈⢙`, `⠈⡙`, `⢈⠩`, `⡀⢙`, `⠄⡙`, `⢂⠩`, `⡂⢘`, `⠅⡘`, `⢃⠨`, `⡃⢐`, `⠍⡐`, `⢋⠠`, `⡋⢀`, `⠍⡁`, `⢋⠁`, `⡋⠁`, `⠍⠉`, `⠋⠉`, `⠋⠉`, `⠉⠙`, `⠉⠙`, `⠉⠩`, `⠈⢙`, `⠈⡙`, `⠈⠩`, `⠀⢙`, `⠀⡙`, `⠀⠩`, `⠀⢘`, `⠀⡘`, `⠀⠨`, `⠀⢐`, `⠀⡐`, `⠀⠠`, `⠀⢀`, `⠀⡀`},
|
||||
}
|
||||
case Dots2:
|
||||
return Spinner{
|
||||
Name: Dots2,
|
||||
Interval: 80,
|
||||
Frames: []string{`⣾`, `⣽`, `⣻`, `⢿`, `⡿`, `⣟`, `⣯`, `⣷`},
|
||||
}
|
||||
case Dots3:
|
||||
return Spinner{
|
||||
Name: Dots3,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠋`, `⠙`, `⠚`, `⠞`, `⠖`, `⠦`, `⠴`, `⠲`, `⠳`, `⠓`},
|
||||
}
|
||||
case Dots4:
|
||||
return Spinner{
|
||||
Name: Dots4,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠄`, `⠆`, `⠇`, `⠋`, `⠙`, `⠸`, `⠰`, `⠠`, `⠰`, `⠸`, `⠙`, `⠋`, `⠇`, `⠆`},
|
||||
}
|
||||
case Dots5:
|
||||
return Spinner{
|
||||
Name: Dots5,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠋`, `⠙`, `⠚`, `⠒`, `⠂`, `⠂`, `⠒`, `⠲`, `⠴`, `⠦`, `⠖`, `⠒`, `⠐`, `⠐`, `⠒`, `⠓`, `⠋`},
|
||||
}
|
||||
case Dots6:
|
||||
return Spinner{
|
||||
Name: Dots6,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠁`, `⠉`, `⠙`, `⠚`, `⠒`, `⠂`, `⠂`, `⠒`, `⠲`, `⠴`, `⠤`, `⠄`, `⠄`, `⠤`, `⠴`, `⠲`, `⠒`, `⠂`, `⠂`, `⠒`, `⠚`, `⠙`, `⠉`, `⠁`},
|
||||
}
|
||||
case Dots7:
|
||||
return Spinner{
|
||||
Name: Dots7,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠈`, `⠉`, `⠋`, `⠓`, `⠒`, `⠐`, `⠐`, `⠒`, `⠖`, `⠦`, `⠤`, `⠠`, `⠠`, `⠤`, `⠦`, `⠖`, `⠒`, `⠐`, `⠐`, `⠒`, `⠓`, `⠋`, `⠉`, `⠈`},
|
||||
}
|
||||
case Dots8:
|
||||
return Spinner{
|
||||
Name: Dots8,
|
||||
Interval: 80,
|
||||
Frames: []string{`⠁`, `⠁`, `⠉`, `⠙`, `⠚`, `⠒`, `⠂`, `⠂`, `⠒`, `⠲`, `⠴`, `⠤`, `⠄`, `⠄`, `⠤`, `⠠`, `⠠`, `⠤`, `⠦`, `⠖`, `⠒`, `⠐`, `⠐`, `⠒`, `⠓`, `⠋`, `⠉`, `⠈`, `⠈`},
|
||||
}
|
||||
case Dots9:
|
||||
return Spinner{
|
||||
Name: Dots9,
|
||||
Interval: 80,
|
||||
Frames: []string{`⢹`, `⢺`, `⢼`, `⣸`, `⣇`, `⡧`, `⡗`, `⡏`},
|
||||
}
|
||||
case Dqpb:
|
||||
return Spinner{
|
||||
Name: Dqpb,
|
||||
Interval: 100,
|
||||
Frames: []string{`d`, `q`, `p`, `b`},
|
||||
}
|
||||
case Earth:
|
||||
return Spinner{
|
||||
Name: Earth,
|
||||
Interval: 180,
|
||||
Frames: []string{`🌍 `, `🌎 `, `🌏 `},
|
||||
}
|
||||
case Flip:
|
||||
return Spinner{
|
||||
Name: Flip,
|
||||
Interval: 70,
|
||||
Frames: []string{`_`, `_`, `_`, `-`, "`", "`", `'`, `´`, `-`, `_`, `_`, `_`},
|
||||
}
|
||||
case GrowHorizontal:
|
||||
return Spinner{
|
||||
Name: GrowHorizontal,
|
||||
Interval: 120,
|
||||
Frames: []string{`▏`, `▎`, `▍`, `▌`, `▋`, `▊`, `▉`, `▊`, `▋`, `▌`, `▍`, `▎`},
|
||||
}
|
||||
case GrowVertical:
|
||||
return Spinner{
|
||||
Name: GrowVertical,
|
||||
Interval: 120,
|
||||
Frames: []string{`▁`, `▃`, `▄`, `▅`, `▆`, `▇`, `▆`, `▅`, `▄`, `▃`},
|
||||
}
|
||||
case Hamburger:
|
||||
return Spinner{
|
||||
Name: Hamburger,
|
||||
Interval: 100,
|
||||
Frames: []string{`☱`, `☲`, `☴`},
|
||||
}
|
||||
case Hearts:
|
||||
return Spinner{
|
||||
Name: Hearts,
|
||||
Interval: 100,
|
||||
Frames: []string{`💛 `, `💙 `, `💜 `, `💚 `, `❤️ `},
|
||||
}
|
||||
case Line:
|
||||
return Spinner{
|
||||
Name: Line,
|
||||
Interval: 130,
|
||||
Frames: []string{`-`, `\`, `|`, `/`},
|
||||
}
|
||||
case Line2:
|
||||
return Spinner{
|
||||
Name: Line2,
|
||||
Interval: 100,
|
||||
Frames: []string{`⠂`, `-`, `–`, `—`, `–`, `-`},
|
||||
}
|
||||
case Monkey:
|
||||
return Spinner{
|
||||
Name: Monkey,
|
||||
Interval: 300,
|
||||
Frames: []string{`🙈 `, `🙈 `, `🙉 `, `🙊 `},
|
||||
}
|
||||
case Moon:
|
||||
return Spinner{
|
||||
Name: Moon,
|
||||
Interval: 80,
|
||||
Frames: []string{`🌑 `, `🌒 `, `🌓 `, `🌔 `, `🌕 `, `🌖 `, `🌗 `, `🌘 `},
|
||||
}
|
||||
case Noise:
|
||||
return Spinner{
|
||||
Name: Noise,
|
||||
Interval: 100,
|
||||
Frames: []string{`▓`, `▒`, `░`},
|
||||
}
|
||||
case Pipe:
|
||||
return Spinner{
|
||||
Name: Pipe,
|
||||
Interval: 100,
|
||||
Frames: []string{`┤`, `┘`, `┴`, `└`, `├`, `┌`, `┬`, `┐`},
|
||||
}
|
||||
case Pong:
|
||||
return Spinner{
|
||||
Name: Pong,
|
||||
Interval: 80,
|
||||
Frames: []string{`▐⠂ ▌`, `▐⠈ ▌`, `▐ ⠂ ▌`, `▐ ⠠ ▌`, `▐ ⡀ ▌`, `▐ ⠠ ▌`, `▐ ⠂ ▌`, `▐ ⠈ ▌`, `▐ ⠂ ▌`, `▐ ⠠ ▌`, `▐ ⡀ ▌`, `▐ ⠠ ▌`, `▐ ⠂ ▌`, `▐ ⠈ ▌`, `▐ ⠂▌`, `▐ ⠠▌`, `▐ ⡀▌`, `▐ ⠠ ▌`, `▐ ⠂ ▌`, `▐ ⠈ ▌`, `▐ ⠂ ▌`, `▐ ⠠ ▌`, `▐ ⡀ ▌`, `▐ ⠠ ▌`, `▐ ⠂ ▌`, `▐ ⠈ ▌`, `▐ ⠂ ▌`, `▐ ⠠ ▌`, `▐ ⡀ ▌`, `▐⠠ ▌`},
|
||||
}
|
||||
case Runner:
|
||||
return Spinner{
|
||||
Name: Runner,
|
||||
Interval: 140,
|
||||
Frames: []string{`🚶 `, `🏃 `},
|
||||
}
|
||||
case Shark:
|
||||
return Spinner{
|
||||
Name: Shark,
|
||||
Interval: 120,
|
||||
Frames: []string{`▐|\____________▌`, `▐_|\___________▌`, `▐__|\__________▌`, `▐___|\_________▌`, `▐____|\________▌`, `▐_____|\_______▌`, `▐______|\______▌`, `▐_______|\_____▌`, `▐________|\____▌`, `▐_________|\___▌`, `▐__________|\__▌`, `▐___________|\_▌`, `▐____________|\▌`, `▐____________/|▌`, `▐___________/|_▌`, `▐__________/|__▌`, `▐_________/|___▌`, `▐________/|____▌`, `▐_______/|_____▌`, `▐______/|______▌`, `▐_____/|_______▌`, `▐____/|________▌`, `▐___/|_________▌`, `▐__/|__________▌`, `▐_/|___________▌`, `▐/|____________▌`},
|
||||
}
|
||||
case SimpleDots:
|
||||
return Spinner{
|
||||
Name: SimpleDots,
|
||||
Interval: 400,
|
||||
Frames: []string{`. `, `.. `, `...`, ` `},
|
||||
}
|
||||
case SimpleDotsScrolling:
|
||||
return Spinner{
|
||||
Name: SimpleDotsScrolling,
|
||||
Interval: 200,
|
||||
Frames: []string{`. `, `.. `, `...`, ` ..`, ` .`, ` `},
|
||||
}
|
||||
case Smiley:
|
||||
return Spinner{
|
||||
Name: Smiley,
|
||||
Interval: 200,
|
||||
Frames: []string{`😄 `, `😝 `},
|
||||
}
|
||||
case SquareCorners:
|
||||
return Spinner{
|
||||
Name: SquareCorners,
|
||||
Interval: 180,
|
||||
Frames: []string{`◰`, `◳`, `◲`, `◱`},
|
||||
}
|
||||
case Squish:
|
||||
return Spinner{
|
||||
Name: Squish,
|
||||
Interval: 100,
|
||||
Frames: []string{`╫`, `╪`},
|
||||
}
|
||||
case Star:
|
||||
return Spinner{
|
||||
Name: Star,
|
||||
Interval: 70,
|
||||
Frames: []string{`✶`, `✸`, `✹`, `✺`, `✹`, `✷`},
|
||||
}
|
||||
case Star2:
|
||||
return Spinner{
|
||||
Name: Star2,
|
||||
Interval: 80,
|
||||
Frames: []string{`+`, `x`, `*`},
|
||||
}
|
||||
case Toggle:
|
||||
return Spinner{
|
||||
Name: Toggle,
|
||||
Interval: 250,
|
||||
Frames: []string{`⊶`, `⊷`},
|
||||
}
|
||||
case Toggle10:
|
||||
return Spinner{
|
||||
Name: Toggle10,
|
||||
Interval: 100,
|
||||
Frames: []string{`㊂`, `㊀`, `㊁`},
|
||||
}
|
||||
case Toggle11:
|
||||
return Spinner{
|
||||
Name: Toggle11,
|
||||
Interval: 50,
|
||||
Frames: []string{`⧇`, `⧆`},
|
||||
}
|
||||
case Toggle12:
|
||||
return Spinner{
|
||||
Name: Toggle12,
|
||||
Interval: 120,
|
||||
Frames: []string{`☗`, `☖`},
|
||||
}
|
||||
case Toggle13:
|
||||
return Spinner{
|
||||
Name: Toggle13,
|
||||
Interval: 80,
|
||||
Frames: []string{`=`, `*`, `-`},
|
||||
}
|
||||
case Toggle2:
|
||||
return Spinner{
|
||||
Name: Toggle2,
|
||||
Interval: 80,
|
||||
Frames: []string{`▫`, `▪`},
|
||||
}
|
||||
case Toggle3:
|
||||
return Spinner{
|
||||
Name: Toggle3,
|
||||
Interval: 120,
|
||||
Frames: []string{`□`, `■`},
|
||||
}
|
||||
case Toggle4:
|
||||
return Spinner{
|
||||
Name: Toggle4,
|
||||
Interval: 100,
|
||||
Frames: []string{`■`, `□`, `▪`, `▫`},
|
||||
}
|
||||
case Toggle5:
|
||||
return Spinner{
|
||||
Name: Toggle5,
|
||||
Interval: 100,
|
||||
Frames: []string{`▮`, `▯`},
|
||||
}
|
||||
case Toggle6:
|
||||
return Spinner{
|
||||
Name: Toggle6,
|
||||
Interval: 300,
|
||||
Frames: []string{`ဝ`, `၀`},
|
||||
}
|
||||
case Toggle7:
|
||||
return Spinner{
|
||||
Name: Toggle7,
|
||||
Interval: 80,
|
||||
Frames: []string{`⦾`, `⦿`},
|
||||
}
|
||||
case Toggle8:
|
||||
return Spinner{
|
||||
Name: Toggle8,
|
||||
Interval: 100,
|
||||
Frames: []string{`◍`, `◌`},
|
||||
}
|
||||
case Toggle9:
|
||||
return Spinner{
|
||||
Name: Toggle9,
|
||||
Interval: 100,
|
||||
Frames: []string{`◉`, `◎`},
|
||||
}
|
||||
case Triangle:
|
||||
return Spinner{
|
||||
Name: Triangle,
|
||||
Interval: 50,
|
||||
Frames: []string{`◢`, `◣`, `◤`, `◥`},
|
||||
}
|
||||
case Weather:
|
||||
return Spinner{
|
||||
Name: Weather,
|
||||
Interval: 100,
|
||||
Frames: []string{`☀️ `, `☀️ `, `☀️ `, `🌤 `, `⛅️ `, `🌥 `, `☁️ `, `🌧 `, `🌨 `, `🌧 `, `🌨 `, `🌧 `, `🌨 `, `⛈ `, `🌨 `, `🌧 `, `🌨 `, `☁️ `, `🌥 `, `⛅️ `, `🌤 `, `☀️ `, `☀️ `},
|
||||
}
|
||||
default:
|
||||
return Spinner{}
|
||||
}
|
||||
}
|
||||
41
vendor/github.com/gernest/wow/symbols.go
generated
vendored
Normal file
41
vendor/github.com/gernest/wow/symbols.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package wow
|
||||
|
||||
import "runtime"
|
||||
|
||||
// LogSymbol is a log severity level
|
||||
type LogSymbol uint
|
||||
|
||||
// common log symbos
|
||||
const (
|
||||
INFO LogSymbol = iota
|
||||
SUCCESS
|
||||
WARNING
|
||||
ERROR
|
||||
)
|
||||
|
||||
func (s LogSymbol) String() string {
|
||||
if runtime.GOOS != "windows" {
|
||||
switch s {
|
||||
case INFO:
|
||||
return "ℹ"
|
||||
case SUCCESS:
|
||||
return "✔"
|
||||
case WARNING:
|
||||
return "⚠"
|
||||
case ERROR:
|
||||
return "✖"
|
||||
}
|
||||
} else {
|
||||
switch s {
|
||||
case INFO:
|
||||
return "i"
|
||||
case SUCCESS:
|
||||
return "v"
|
||||
case WARNING:
|
||||
return "!!"
|
||||
case ERROR:
|
||||
return "x"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
122
vendor/github.com/gernest/wow/wow.go
generated
vendored
Normal file
122
vendor/github.com/gernest/wow/wow.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package wow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gernest/wow/spin"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
const erase = "\033[2K\r"
|
||||
|
||||
// Wow writes beautiful spinners on the terminal.
|
||||
type Wow struct {
|
||||
txt string
|
||||
s spin.Spinner
|
||||
out io.Writer
|
||||
running bool
|
||||
done func()
|
||||
mu sync.RWMutex
|
||||
IsTerminal bool
|
||||
}
|
||||
|
||||
// New creates a new wow instance ready to start spinning.
|
||||
func New(o io.Writer, s spin.Spinner, text string, options ...func(*Wow)) *Wow {
|
||||
isTerminal := terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||
|
||||
wow := Wow{out: o, s: s, txt: text, IsTerminal: isTerminal}
|
||||
|
||||
for _, option := range options {
|
||||
option(&wow)
|
||||
}
|
||||
|
||||
return &wow
|
||||
}
|
||||
|
||||
// Start starts the spinner. The frames are written based on the spinner
|
||||
// interval.
|
||||
func (w *Wow) Start() {
|
||||
if !w.running {
|
||||
ctx, done := context.WithCancel(context.Background())
|
||||
t := time.NewTicker(time.Duration(w.s.Interval) * time.Millisecond)
|
||||
w.done = done
|
||||
w.running = true
|
||||
go func() {
|
||||
at := 0
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
break
|
||||
case <-t.C:
|
||||
txt := erase + w.s.Frames[at%len(w.s.Frames)] + w.txt
|
||||
if w.IsTerminal {
|
||||
fmt.Fprint(w.out, txt)
|
||||
}
|
||||
at++
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the spinner
|
||||
func (w *Wow) Stop() {
|
||||
if w.done != nil {
|
||||
w.done()
|
||||
}
|
||||
w.running = false
|
||||
}
|
||||
|
||||
// Spinner sets s to the current spinner
|
||||
func (w *Wow) Spinner(s spin.Spinner) *Wow {
|
||||
w.Stop()
|
||||
w.s = s
|
||||
w.Start()
|
||||
return w
|
||||
}
|
||||
|
||||
// Text adds text to the current spinner
|
||||
func (w *Wow) Text(txt string) *Wow {
|
||||
w.mu.Lock()
|
||||
w.txt = txt
|
||||
w.mu.Unlock()
|
||||
return w
|
||||
}
|
||||
|
||||
// Persist writes the last character of the currect spinner frames together with
|
||||
// the text on stdout.
|
||||
//
|
||||
// A new line is added at the end to ensure the text stay that way.
|
||||
func (w *Wow) Persist() {
|
||||
w.Stop()
|
||||
at := len(w.s.Frames) - 1
|
||||
txt := erase + w.s.Frames[at] + w.txt + "\n"
|
||||
if w.IsTerminal {
|
||||
fmt.Fprint(w.out, txt)
|
||||
}
|
||||
}
|
||||
|
||||
// PersistWith writes the last frame of s together with text with a new line
|
||||
// added to make it stick.
|
||||
func (w *Wow) PersistWith(s spin.Spinner, text string) {
|
||||
w.Stop()
|
||||
var a string
|
||||
if len(s.Frames) > 0 {
|
||||
a = s.Frames[len(s.Frames)-1]
|
||||
}
|
||||
txt := erase + a + text + "\n"
|
||||
if w.IsTerminal {
|
||||
fmt.Fprint(w.out, txt)
|
||||
}
|
||||
}
|
||||
|
||||
// ForceOutput forces all output even if not not outputting directly to a terminal
|
||||
func ForceOutput(w *Wow) {
|
||||
w.IsTerminal = true
|
||||
}
|
||||
Reference in New Issue
Block a user