diff --git a/LICENSE b/LICENSE index 5043d75..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,202 @@ -MIT License - -Copyright (c) 2021 Bisons - -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. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 7081f28..3290804 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,6 @@ saido means monitor in Hausa ![Logo](assets/logo.jpg) -[![Build Status](https://github.com/bisoncorps/saido/workflows/Test/badge.svg)](https://github.com/bisoncorps/saido/actions) +[![Build Status](https://github.com/bisohns/saido/workflows/Test/badge.svg)](https://github.com/bisohns/saido/actions) NOTE: `!windows` flag is our current specification of what `unix` means, seee [issue](https://github.com/golang/go/issues/20322) for why *_unix.go files will still attempt to run on windows diff --git a/charts/charts.go b/charts/charts.go new file mode 100644 index 0000000..3756a01 --- /dev/null +++ b/charts/charts.go @@ -0,0 +1,941 @@ +package charts + +import ( + "context" + "fmt" + "image" + "math" + "math/rand" + "sync" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/mum4k/termdash" + "github.com/mum4k/termdash/align" + "github.com/mum4k/termdash/cell" + "github.com/mum4k/termdash/container" + "github.com/mum4k/termdash/container/grid" + "github.com/mum4k/termdash/keyboard" + "github.com/mum4k/termdash/linestyle" + "github.com/mum4k/termdash/terminal/tcell" + "github.com/mum4k/termdash/terminal/terminalapi" + "github.com/mum4k/termdash/widgets/barchart" + "github.com/mum4k/termdash/widgets/button" + "github.com/mum4k/termdash/widgets/donut" + "github.com/mum4k/termdash/widgets/gauge" + "github.com/mum4k/termdash/widgets/linechart" + "github.com/mum4k/termdash/widgets/segmentdisplay" + "github.com/mum4k/termdash/widgets/sparkline" + "github.com/mum4k/termdash/widgets/text" + "github.com/mum4k/termdash/widgets/textinput" +) + +// widgets holds the widgets used by this demo. +type widgets struct { + segDist *segmentdisplay.SegmentDisplay + input *textinput.TextInput + rollT *text.Text + spGreen *sparkline.SparkLine + spRed *sparkline.SparkLine + gauge *gauge.Gauge + heartLC *linechart.LineChart + barChart *barchart.BarChart + donut *donut.Donut + leftB *button.Button + rightB *button.Button + sineLC *linechart.LineChart + + buttons *layoutButtons +} + +// newWidgets creates all widgets used by this demo. +func newWidgets(ctx context.Context, t terminalapi.Terminal, c *container.Container) (*widgets, error) { + updateText := make(chan string) + sd, err := newSegmentDisplay(ctx, t, updateText) + if err != nil { + return nil, err + } + + input, err := newTextInput(updateText) + if err != nil { + return nil, err + } + + rollT, err := newRollText(ctx) + if err != nil { + return nil, err + } + spGreen, spRed, err := newSparkLines(ctx) + if err != nil { + return nil, err + } + g, err := newGauge(ctx) + if err != nil { + return nil, err + } + + heartLC, err := newHeartbeat(ctx) + if err != nil { + return nil, err + } + + bc, err := newBarChart(ctx) + if err != nil { + return nil, err + } + + don, err := newDonut(ctx) + if err != nil { + return nil, err + } + + leftB, rightB, sineLC, err := newSines(ctx) + if err != nil { + return nil, err + } + return &widgets{ + segDist: sd, + input: input, + rollT: rollT, + spGreen: spGreen, + spRed: spRed, + gauge: g, + heartLC: heartLC, + barChart: bc, + donut: don, + leftB: leftB, + rightB: rightB, + sineLC: sineLC, + }, nil +} + +// layoutType represents the possible layouts the buttons switch between. +type layoutType int + +const ( + // layoutAll displays all the widgets. + layoutAll layoutType = iota + // layoutText focuses onto the text widget. + layoutText + // layoutSparkLines focuses onto the sparklines. + layoutSparkLines + // layoutLineChart focuses onto the linechart. + layoutLineChart +) + +// gridLayout prepares container options that represent the desired screen layout. +// This function demonstrates the use of the grid builder. +// gridLayout() and contLayout() demonstrate the two available layout APIs and +// both produce equivalent layouts for layoutType layoutAll. +func gridLayout(w *widgets, lt layoutType) ([]container.Option, error) { + leftRows := []grid.Element{ + grid.RowHeightPerc(25, + grid.Widget(w.segDist, + container.Border(linestyle.Light), + container.BorderTitle("Press Esc to quit"), + ), + ), + grid.RowHeightPerc(5, + grid.Widget(w.input), + ), + + grid.RowHeightPerc(5, + grid.ColWidthPerc(25, + grid.Widget(w.buttons.allB), + ), + grid.ColWidthPerc(25, + grid.Widget(w.buttons.textB), + ), + grid.ColWidthPerc(25, + grid.Widget(w.buttons.spB), + ), + grid.ColWidthPerc(25, + grid.Widget(w.buttons.lcB), + ), + ), + } + switch lt { + case layoutAll: + leftRows = append(leftRows, + grid.RowHeightPerc(20, + grid.ColWidthPerc(50, + grid.Widget(w.rollT, + container.Border(linestyle.Light), + container.BorderTitle("A rolling text"), + ), + ), + grid.ColWidthPerc(50, + grid.RowHeightPerc(50, + grid.Widget(w.spGreen, + container.Border(linestyle.Light), + container.BorderTitle("Green SparkLine"), + ), + ), + grid.RowHeightPerc(50, + grid.Widget(w.spRed, + container.Border(linestyle.Light), + container.BorderTitle("Red SparkLine"), + ), + ), + ), + ), + grid.RowHeightPerc(7, + grid.Widget(w.gauge, + container.Border(linestyle.Light), + container.BorderTitle("A Gauge"), + container.BorderColor(cell.ColorNumber(39)), + ), + ), + grid.RowHeightPerc(38, + grid.Widget(w.heartLC, + container.Border(linestyle.Light), + container.BorderTitle("A LineChart"), + ), + ), + ) + case layoutText: + leftRows = append(leftRows, + grid.RowHeightPerc(65, + grid.Widget(w.rollT, + container.Border(linestyle.Light), + container.BorderTitle("A rolling text"), + ), + ), + ) + + case layoutSparkLines: + leftRows = append(leftRows, + grid.RowHeightPerc(32, + grid.Widget(w.spGreen, + container.Border(linestyle.Light), + container.BorderTitle("Green SparkLine"), + ), + ), + grid.RowHeightPerc(33, + grid.Widget(w.spRed, + container.Border(linestyle.Light), + container.BorderTitle("Red SparkLine"), + ), + ), + ) + + case layoutLineChart: + leftRows = append(leftRows, + grid.RowHeightPerc(65, + grid.Widget(w.heartLC, + container.Border(linestyle.Light), + container.BorderTitle("A LineChart"), + ), + ), + ) + } + + builder := grid.New() + builder.Add( + grid.ColWidthPerc(70, leftRows...), + ) + + builder.Add( + grid.ColWidthPerc(30, + grid.RowHeightPerc(30, + grid.Widget(w.barChart, + container.Border(linestyle.Light), + container.BorderTitle("BarChart"), + container.BorderTitleAlignRight(), + ), + ), + grid.RowHeightPerc(21, + grid.Widget(w.donut, + container.Border(linestyle.Light), + container.BorderTitle("A Donut"), + container.BorderTitleAlignRight(), + ), + ), + grid.RowHeightPerc(40, + grid.Widget(w.sineLC, + container.Border(linestyle.Light), + container.BorderTitle("Multiple series"), + container.BorderTitleAlignRight(), + ), + ), + grid.RowHeightPerc(9, + grid.ColWidthPerc(50, + grid.Widget(w.leftB, + container.AlignHorizontal(align.HorizontalRight), + container.PaddingRight(1), + ), + ), + grid.ColWidthPerc(50, + grid.Widget(w.rightB, + container.AlignHorizontal(align.HorizontalLeft), + container.PaddingLeft(1), + ), + ), + ), + ), + ) + + gridOpts, err := builder.Build() + if err != nil { + return nil, err + } + return gridOpts, nil +} + +// contLayout prepares container options that represent the desired screen layout. +// This function demonstrates the direct use of the container API. +// gridLayout() and contLayout() demonstrate the two available layout APIs and +// both produce equivalent layouts for layoutType layoutAll. +// contLayout only produces layoutAll. +func contLayout(w *widgets) ([]container.Option, error) { + buttonRow := []container.Option{ + container.SplitVertical( + container.Left( + container.SplitVertical( + container.Left( + container.PlaceWidget(w.buttons.allB), + ), + container.Right( + container.PlaceWidget(w.buttons.textB), + ), + ), + ), + container.Right( + container.SplitVertical( + container.Left( + container.PlaceWidget(w.buttons.spB), + ), + container.Right( + container.PlaceWidget(w.buttons.lcB), + ), + ), + ), + ), + } + + textAndSparks := []container.Option{ + container.SplitVertical( + container.Left( + container.Border(linestyle.Light), + container.BorderTitle("A rolling text"), + container.PlaceWidget(w.rollT), + ), + container.Right( + container.SplitHorizontal( + container.Top( + container.Border(linestyle.Light), + container.BorderTitle("Green SparkLine"), + container.PlaceWidget(w.spGreen), + ), + container.Bottom( + container.Border(linestyle.Light), + container.BorderTitle("Red SparkLine"), + container.PlaceWidget(w.spRed), + ), + ), + ), + ), + } + + segmentTextInputSparks := []container.Option{ + container.SplitHorizontal( + container.Top( + container.Border(linestyle.Light), + container.BorderTitle("Press Esc to quit"), + container.PlaceWidget(w.segDist), + ), + container.Bottom( + container.SplitHorizontal( + container.Top( + container.SplitHorizontal( + container.Top( + container.PlaceWidget(w.input), + ), + container.Bottom(buttonRow...), + ), + ), + container.Bottom(textAndSparks...), + container.SplitPercent(40), + ), + ), + container.SplitPercent(50), + ), + } + + gaugeAndHeartbeat := []container.Option{ + container.SplitHorizontal( + container.Top( + container.Border(linestyle.Light), + container.BorderTitle("A Gauge"), + container.BorderColor(cell.ColorNumber(39)), + container.PlaceWidget(w.gauge), + ), + container.Bottom( + container.Border(linestyle.Light), + container.BorderTitle("A LineChart"), + container.PlaceWidget(w.heartLC), + ), + container.SplitPercent(20), + ), + } + + leftSide := []container.Option{ + container.SplitHorizontal( + container.Top(segmentTextInputSparks...), + container.Bottom(gaugeAndHeartbeat...), + container.SplitPercent(50), + ), + } + + lcAndButtons := []container.Option{ + container.SplitHorizontal( + container.Top( + container.Border(linestyle.Light), + container.BorderTitle("Multiple series"), + container.BorderTitleAlignRight(), + container.PlaceWidget(w.sineLC), + ), + container.Bottom( + container.SplitVertical( + container.Left( + container.PlaceWidget(w.leftB), + container.AlignHorizontal(align.HorizontalRight), + container.PaddingRight(1), + ), + container.Right( + container.PlaceWidget(w.rightB), + container.AlignHorizontal(align.HorizontalLeft), + container.PaddingLeft(1), + ), + ), + ), + container.SplitPercent(80), + ), + } + + rightSide := []container.Option{ + container.SplitHorizontal( + container.Top( + container.Border(linestyle.Light), + container.BorderTitle("BarChart"), + container.PlaceWidget(w.barChart), + container.BorderTitleAlignRight(), + ), + container.Bottom( + container.SplitHorizontal( + container.Top( + container.Border(linestyle.Light), + container.BorderTitle("A Donut"), + container.BorderTitleAlignRight(), + container.PlaceWidget(w.donut), + ), + container.Bottom(lcAndButtons...), + container.SplitPercent(30), + ), + ), + container.SplitPercent(30), + ), + } + + return []container.Option{ + container.SplitVertical( + container.Left(leftSide...), + container.Right(rightSide...), + container.SplitPercent(70), + ), + }, nil +} + +// rootID is the ID assigned to the root container. +const rootID = "root" + +// Terminal implementations +const ( + termboxTerminal = "termbox" + tcellTerminal = "tcell" +) + +func charts_main() { + log.Debug("Starting Saido Main") + t, err := tcell.New(tcell.ColorMode(terminalapi.ColorMode256)) + if err != nil { + panic(err) + } + defer t.Close() + + c, err := container.New(t, container.ID(rootID)) + if err != nil { + panic(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + w, err := newWidgets(ctx, t, c) + if err != nil { + panic(err) + } + lb, err := newLayoutButtons(c, w) + if err != nil { + panic(err) + } + w.buttons = lb + + gridOpts, err := gridLayout(w, layoutAll) // equivalent to contLayout(w) + if err != nil { + panic(err) + } + + if err := c.Update(rootID, gridOpts...); err != nil { + panic(err) + } + + quitter := func(k *terminalapi.Keyboard) { + if k.Key == keyboard.KeyEsc || k.Key == keyboard.KeyCtrlC { + cancel() + } + } + if err := termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(RedrawInterval)); err != nil { + panic(err) + } +} + +// textState creates a rotated state for the text we are displaying. +func textState(text string, capacity, step int) []rune { + if capacity == 0 { + return nil + } + + var state []rune + for i := 0; i < capacity; i++ { + state = append(state, ' ') + } + state = append(state, []rune(text)...) + step = step % len(state) + return rotateRunes(state, step) +} + +// newTextInput creates a new TextInput field that changes the text on the +// SegmentDisplay. +func newTextInput(updateText chan<- string) (*textinput.TextInput, error) { + input, err := textinput.New( + textinput.Label("Change text to: ", cell.FgColor(cell.ColorNumber(33))), + textinput.MaxWidthCells(20), + textinput.PlaceHolder("enter any text"), + textinput.OnSubmit(func(text string) error { + updateText <- text + return nil + }), + textinput.ClearOnSubmit(), + ) + if err != nil { + return nil, err + } + return input, err +} + +// newSegmentDisplay creates a new SegmentDisplay that initially shows the +// Termdash name. Shows any text that is sent over the channel. +func newSegmentDisplay(ctx context.Context, t terminalapi.Terminal, updateText <-chan string) (*segmentdisplay.SegmentDisplay, error) { + sd, err := segmentdisplay.New() + if err != nil { + return nil, err + } + + colors := []cell.Color{ + cell.ColorNumber(33), + cell.ColorRed, + cell.ColorYellow, + cell.ColorNumber(33), + cell.ColorGreen, + cell.ColorRed, + cell.ColorGreen, + cell.ColorRed, + } + + text := "Termdash" + step := 0 + + go func() { + ticker := time.NewTicker(500 * time.Millisecond) + defer ticker.Stop() + + capacity := 0 + termSize := t.Size() + for { + select { + case <-ticker.C: + if capacity == 0 { + // The segment display only knows its capacity after both + // text size and terminal size are known. + capacity = sd.Capacity() + } + if t.Size().Eq(image.ZP) || !t.Size().Eq(termSize) { + // Update the capacity initially the first time the + // terminal reports a non-zero size and then every time the + // terminal resizes. + // + // This is better than updating the capacity on every + // iteration since that leads to edge cases - segment + // display capacity depends on the length of text and here + // we are trying to adjust the text length to the capacity. + termSize = t.Size() + capacity = sd.Capacity() + } + + state := textState(text, capacity, step) + var chunks []*segmentdisplay.TextChunk + for i := 0; i < capacity; i++ { + if i >= len(state) { + break + } + + color := colors[i%len(colors)] + chunks = append(chunks, segmentdisplay.NewChunk( + string(state[i]), + segmentdisplay.WriteCellOpts(cell.FgColor(color)), + )) + } + if len(chunks) == 0 { + continue + } + if err := sd.Write(chunks); err != nil { + panic(err) + } + step++ + + case t := <-updateText: + text = t + sd.Reset() + step = 0 + + case <-ctx.Done(): + return + } + } + }() + return sd, nil +} + +// newRollText creates a new Text widget that displays rolling text. +func newRollText(ctx context.Context) (*text.Text, error) { + t, err := text.New(text.RollContent()) + if err != nil { + return nil, err + } + + i := 0 + go Periodic(ctx, 1*time.Second, func() error { + if err := t.Write(fmt.Sprintf("Writing line %d.\n", i), text.WriteCellOpts(cell.FgColor(cell.ColorNumber(142)))); err != nil { + return err + } + i++ + return nil + }) + return t, nil +} + +// newSparkLines creates two new sparklines displaying random values. +func newSparkLines(ctx context.Context) (*sparkline.SparkLine, *sparkline.SparkLine, error) { + spGreen, err := sparkline.New( + sparkline.Color(cell.ColorGreen), + ) + if err != nil { + return nil, nil, err + } + + const max = 100 + go Periodic(ctx, 250*time.Millisecond, func() error { + v := int(rand.Int31n(max + 1)) + return spGreen.Add([]int{v}) + }) + + spRed, err := sparkline.New( + sparkline.Color(cell.ColorRed), + ) + if err != nil { + return nil, nil, err + } + go Periodic(ctx, 500*time.Millisecond, func() error { + v := int(rand.Int31n(max + 1)) + return spRed.Add([]int{v}) + }) + return spGreen, spRed, nil + +} + +// newGauge creates a demo Gauge widget. +func newGauge(ctx context.Context) (*gauge.Gauge, error) { + g, err := gauge.New() + if err != nil { + return nil, err + } + + const start = 35 + progress := start + + go Periodic(ctx, 2*time.Second, func() error { + if err := g.Percent(progress); err != nil { + return err + } + progress++ + if progress > 100 { + progress = start + } + return nil + }) + return g, nil +} + +// newDonut creates a demo Donut widget. +func newDonut(ctx context.Context) (*donut.Donut, error) { + d, err := donut.New(donut.CellOpts( + cell.FgColor(cell.ColorNumber(33))), + ) + if err != nil { + return nil, err + } + + const start = 35 + progress := start + + go Periodic(ctx, 500*time.Millisecond, func() error { + if err := d.Percent(progress); err != nil { + return err + } + progress++ + if progress > 100 { + progress = start + } + return nil + }) + return d, nil +} + +// newHeartbeat returns a line chart that displays a heartbeat-like progression. +func newHeartbeat(ctx context.Context) (*linechart.LineChart, error) { + var inputs []float64 + for i := 0; i < 100; i++ { + v := math.Pow(math.Sin(float64(i)), 63) * math.Sin(float64(i)+1.5) * 8 + inputs = append(inputs, v) + } + + lc, err := linechart.New( + linechart.AxesCellOpts(cell.FgColor(cell.ColorRed)), + linechart.YLabelCellOpts(cell.FgColor(cell.ColorGreen)), + linechart.XLabelCellOpts(cell.FgColor(cell.ColorGreen)), + ) + if err != nil { + return nil, err + } + step := 0 + go Periodic(ctx, RedrawInterval/3, func() error { + step = (step + 1) % len(inputs) + return lc.Series("heartbeat", rotateFloats(inputs, step), + linechart.SeriesCellOpts(cell.FgColor(cell.ColorNumber(87))), + linechart.SeriesXLabels(map[int]string{ + 0: "zero", + }), + ) + }) + return lc, nil +} + +// newBarChart returns a BarcChart that displays random values on multiple bars. +func newBarChart(ctx context.Context) (*barchart.BarChart, error) { + bc, err := barchart.New( + barchart.BarColors([]cell.Color{ + cell.ColorNumber(33), + cell.ColorNumber(39), + cell.ColorNumber(45), + cell.ColorNumber(51), + cell.ColorNumber(81), + cell.ColorNumber(87), + }), + barchart.ValueColors([]cell.Color{ + cell.ColorBlack, + cell.ColorBlack, + cell.ColorBlack, + cell.ColorBlack, + cell.ColorBlack, + cell.ColorBlack, + }), + barchart.ShowValues(), + ) + if err != nil { + return nil, err + } + + const ( + bars = 6 + max = 100 + ) + values := make([]int, bars) + go Periodic(ctx, 1*time.Second, func() error { + for i := range values { + values[i] = int(rand.Int31n(max + 1)) + } + + return bc.Values(values, max) + }) + return bc, nil +} + +// distance is a thread-safe int value used by the newSince method. +// Buttons write it and the line chart reads it. +type distance struct { + v int + mu sync.Mutex +} + +// add adds the provided value to the one stored. +func (d *distance) add(v int) { + d.mu.Lock() + defer d.mu.Unlock() + d.v += v +} + +// get returns the current value. +func (d *distance) get() int { + d.mu.Lock() + defer d.mu.Unlock() + return d.v +} + +// newSines returns a line chart that displays multiple sine series and two buttons. +// The left button shifts the second series relative to the first series to +// the left and the right button shifts it to the right. +func newSines(ctx context.Context) (left, right *button.Button, lc *linechart.LineChart, err error) { + var inputs []float64 + for i := 0; i < 200; i++ { + v := math.Sin(float64(i) / 100 * math.Pi) + inputs = append(inputs, v) + } + + sineLc, err := linechart.New( + linechart.AxesCellOpts(cell.FgColor(cell.ColorRed)), + linechart.YLabelCellOpts(cell.FgColor(cell.ColorGreen)), + linechart.XLabelCellOpts(cell.FgColor(cell.ColorGreen)), + ) + if err != nil { + return nil, nil, nil, err + } + step1 := 0 + secondDist := &distance{v: 100} + go Periodic(ctx, RedrawInterval/3, func() error { + step1 = (step1 + 1) % len(inputs) + if err := lc.Series("first", rotateFloats(inputs, step1), + linechart.SeriesCellOpts(cell.FgColor(cell.ColorNumber(33))), + ); err != nil { + return err + } + + step2 := (step1 + secondDist.get()) % len(inputs) + return lc.Series("second", rotateFloats(inputs, step2), linechart.SeriesCellOpts(cell.FgColor(cell.ColorWhite))) + }) + + // diff is the difference a single button press adds or removes to the + // second series. + const diff = 20 + leftB, err := button.New("(l)eft", func() error { + secondDist.add(diff) + return nil + }, + button.GlobalKey('l'), + button.WidthFor("(r)ight"), + button.FillColor(cell.ColorNumber(220)), + ) + if err != nil { + return nil, nil, nil, err + } + + rightB, err := button.New("(r)ight", func() error { + secondDist.add(-diff) + return nil + }, + button.GlobalKey('r'), + button.FillColor(cell.ColorNumber(196)), + ) + if err != nil { + return nil, nil, nil, err + } + return leftB, rightB, sineLc, nil +} + +// setLayout sets the specified layout. +func setLayout(c *container.Container, w *widgets, lt layoutType) error { + gridOpts, err := gridLayout(w, lt) + if err != nil { + return err + } + return c.Update(rootID, gridOpts...) +} + +// layoutButtons are buttons that change the layout. +type layoutButtons struct { + allB *button.Button + textB *button.Button + spB *button.Button + lcB *button.Button +} + +// newLayoutButtons returns buttons that dynamically switch the layouts. +func newLayoutButtons(c *container.Container, w *widgets) (*layoutButtons, error) { + opts := []button.Option{ + button.WidthFor("sparklines"), + button.FillColor(cell.ColorNumber(220)), + button.Height(1), + } + + allB, err := button.New("all", func() error { + return setLayout(c, w, layoutAll) + }, opts...) + if err != nil { + return nil, err + } + + textB, err := button.New("text", func() error { + return setLayout(c, w, layoutText) + }, opts...) + if err != nil { + return nil, err + } + + spB, err := button.New("sparklines", func() error { + return setLayout(c, w, layoutSparkLines) + }, opts...) + if err != nil { + return nil, err + } + + lcB, err := button.New("linechart", func() error { + return setLayout(c, w, layoutLineChart) + }, opts...) + if err != nil { + return nil, err + } + + return &layoutButtons{ + allB: allB, + textB: textB, + spB: spB, + lcB: lcB, + }, nil +} + +// rotateFloats returns a new slice with inputs rotated by step. +// I.e. for a step of one: +// inputs[0] -> inputs[len(inputs)-1] +// inputs[1] -> inputs[0] +// And so on. +func rotateFloats(inputs []float64, step int) []float64 { + return append(inputs[step:], inputs[:step]...) +} + +// rotateRunes returns a new slice with inputs rotated by step. +// I.e. for a step of one: +// inputs[0] -> inputs[len(inputs)-1] +// inputs[1] -> inputs[0] +// And so on. +func rotateRunes(inputs []rune, step int) []rune { + return append(inputs[step:], inputs[:step]...) +} diff --git a/cmd/root.go b/cmd/root.go index 3e0f114..1269c00 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,23 +1,43 @@ +/* +Copyright © 2021 Bisohns + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package cmd import ( "fmt" + "os" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/viper" - "os" + + "github.com/bisohns/saido/config" ) var ( + cfgFile string // Verbose : Should display verbose logs verbose bool ) +const appName = "saido" + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "saido", Short: "TUI for monitoring specific host metrics", - Long: `Saido`, + Long: ``, PersistentPreRun: func(cmd *cobra.Command, args []string) { // Log only errors except in Verbose mode if verbose { @@ -27,7 +47,13 @@ var rootCmd = &cobra.Command{ } }, PersistentPostRun: func(cmd *cobra.Command, args []string) { - fmt.Println("\n\nSaido - Bisoncorp (2020) (https://github.com/bisoncorps/saido)") + fmt.Println("\n\nSaido - Bisoncorp (2020) (https://github.com/bisohns/saido)") + }, + Run: func(cmd *cobra.Command, args []string) { + log.Info("Saido is running ...") + + cfg := config.GetConfig() + log.Infof("%v", cfg) }, } @@ -41,6 +67,13 @@ func Execute() { } func init() { - // cobra.OnInitialize(initConfig) - viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose")) + cobra.OnInitialize(initConfig) + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Path to config file") + + cobra.MarkFlagRequired(rootCmd.PersistentFlags(), "config") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + _ = config.LoadConfig(cfgFile) } diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..a6821b3 --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,32 @@ +# Ansible inspired Config +hosts: + connection: + type: ssh + username: root + password: somethingSecret + x.example.net: + y.example.net: + z.example.net: + "192.0.1.5": + alias: home-server + connection: + type: ssh + username: root + password: somethingSecret + port: 33 + "192.0.1.4": + connection: + type: local + children: + eu-west1: + hosts: + "192.0.10.3": + "192.0.10.5": + connection: + type: ssh + privateKeyPath: /path/to/private/key + port: 2222 + +metrics: +- memory +- cpu diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..c70ad8b --- /dev/null +++ b/config/config.go @@ -0,0 +1,71 @@ +package config + +import ( + "io/ioutil" + "strings" + + log "github.com/sirupsen/logrus" + + "gopkg.in/yaml.v2" +) + +var config = &Config{} + +type Connection struct { + Type string + Username string + Password string + PrivateKeyPath string +} + +// Group represents group specified under children +type Group struct { + Name string + Hosts map[string]*Host + Children map[string]*Group + Connection *Connection +} + +type Host struct { + Alias string + Port int32 + Connection *Connection + Hosts map[string]*Host + directGroups map[string]*Group +} + +type Config struct { + Hosts map[string]interface{} `yaml:"hosts"` + Metrics []string `yaml:"metrics"` +} + +func LoadConfig(configPath string) *Config { + confYaml, err := ioutil.ReadFile(configPath) + if err != nil { + log.Errorf("yamlFile.Get err %v ", err) + } + err = yaml.Unmarshal([]byte(confYaml), &config) + if err != nil { + log.Fatalf("error: %v", err) + } + + for k, v := range config.Hosts { + // Parser Children + if strings.ToLower(k) == "children" { + + } else { + + } + log.Info(k, v) + } + + return config +} + +func GetConfig() *Config { + return config +} + +func parseHosts(h interface{}) []*Host { + return []*Host{} +} diff --git a/go.mod b/go.mod index 74774b2..77fcce3 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,16 @@ -module github.com/bisoncorps/saido +module github.com/bisohns/saido go 1.14 require ( + github.com/kr/pretty v0.2.0 // indirect github.com/melbahja/goph v1.2.1 github.com/mum4k/termdash v0.16.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.2.1 - github.com/spf13/viper v1.9.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect + golang.org/x/text v0.3.6 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index d0780c1..7f1dabe 100644 --- a/go.sum +++ b/go.sum @@ -18,11 +18,6 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -32,7 +27,6 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -45,16 +39,13 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -62,11 +53,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -74,13 +65,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U4= @@ -103,7 +90,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -121,7 +107,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -135,12 +120,10 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -152,29 +135,20 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -182,15 +156,11 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -204,61 +174,49 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/melbahja/goph v1.2.1 h1:msPxbLxf1PnbxRQGhv9mVqm7T16tPo3wqLWah0hJhKQ= github.com/melbahja/goph v1.2.1/go.mod h1:y+wS4c0UtZOLSwNz6ktaGiyUeYZBeT1e8PiSz+YK77o= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mum4k/termdash v0.16.0 h1:oez5/noRpk8Lc+4u05QTU1LGGcvUkhEFk2kV9lClQAg= github.com/mum4k/termdash v0.16.0/go.mod h1:bkSQsw2tif8pLQtGmfxh20N1idek+Hzol/wj+1ZC3cM= -github.com/nsf/termbox-go v0.0.0-20201107200903-9b52a5faed9e h1:T8/SzSWIDoWV9trslLNfUdJ5yHrIXXuODEy5M0vou4U= github.com/nsf/termbox-go v0.0.0-20201107200903-9b52a5faed9e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.12.0 h1:/f3b24xrDhkhddlaobPe2JgBqfdt+gC/NYl0QY9IOuI= github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -266,36 +224,28 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= -github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -312,7 +262,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= @@ -321,7 +270,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -377,7 +325,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -400,7 +347,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -413,10 +359,6 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -432,7 +374,6 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -440,18 +381,13 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -476,14 +412,8 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -516,7 +446,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -553,11 +482,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -584,12 +509,6 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -637,18 +556,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -668,13 +576,7 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -687,20 +589,19 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= -gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/integration/integration_test.go b/integration/integration_test.go index 116906b..e2049b3 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/bisoncorps/saido/driver" - "github.com/bisoncorps/saido/inspector" + "github.com/bisohns/saido/driver" + "github.com/bisohns/saido/inspector" ) func TestDFonSSH(t *testing.T) { diff --git a/integration/integration_unix_test.go b/integration/integration_unix_test.go index f9b8a3f..1f44d01 100644 --- a/integration/integration_unix_test.go +++ b/integration/integration_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package integration @@ -7,8 +8,8 @@ import ( "strings" "testing" - "github.com/bisoncorps/saido/driver" - "github.com/bisoncorps/saido/inspector" + "github.com/bisohns/saido/driver" + "github.com/bisohns/saido/inspector" ) func TestDFonLocal(t *testing.T) { diff --git a/integration/integration_windows_test.go b/integration/integration_windows_test.go index 62fd6f4..6871f10 100644 --- a/integration/integration_windows_test.go +++ b/integration/integration_windows_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/bisoncorps/saido/driver" - "github.com/bisoncorps/saido/inspector" + "github.com/bisohns/saido/driver" + "github.com/bisohns/saido/inspector" ) func TestTasklistonLocal(t *testing.T) { diff --git a/main.go b/main.go index e6403e2..4df5f09 100644 --- a/main.go +++ b/main.go @@ -1,943 +1,22 @@ -package main - -import ( - "context" - "fmt" - log "github.com/sirupsen/logrus" - "image" - "math" - "math/rand" - "sync" - "time" - - "github.com/bisoncorps/saido/charts" - "github.com/bisoncorps/saido/cmd" - "github.com/mum4k/termdash" - "github.com/mum4k/termdash/align" - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/container" - "github.com/mum4k/termdash/container/grid" - "github.com/mum4k/termdash/keyboard" - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/terminal/tcell" - "github.com/mum4k/termdash/terminal/terminalapi" - "github.com/mum4k/termdash/widgets/barchart" - "github.com/mum4k/termdash/widgets/button" - "github.com/mum4k/termdash/widgets/donut" - "github.com/mum4k/termdash/widgets/gauge" - "github.com/mum4k/termdash/widgets/linechart" - "github.com/mum4k/termdash/widgets/segmentdisplay" - "github.com/mum4k/termdash/widgets/sparkline" - "github.com/mum4k/termdash/widgets/text" - "github.com/mum4k/termdash/widgets/textinput" -) - -// widgets holds the widgets used by this demo. -type widgets struct { - segDist *segmentdisplay.SegmentDisplay - input *textinput.TextInput - rollT *text.Text - spGreen *sparkline.SparkLine - spRed *sparkline.SparkLine - gauge *gauge.Gauge - heartLC *linechart.LineChart - barChart *barchart.BarChart - donut *donut.Donut - leftB *button.Button - rightB *button.Button - sineLC *linechart.LineChart - - buttons *layoutButtons -} - -// newWidgets creates all widgets used by this demo. -func newWidgets(ctx context.Context, t terminalapi.Terminal, c *container.Container) (*widgets, error) { - updateText := make(chan string) - sd, err := newSegmentDisplay(ctx, t, updateText) - if err != nil { - return nil, err - } - - input, err := newTextInput(updateText) - if err != nil { - return nil, err - } - - rollT, err := newRollText(ctx) - if err != nil { - return nil, err - } - spGreen, spRed, err := newSparkLines(ctx) - if err != nil { - return nil, err - } - g, err := newGauge(ctx) - if err != nil { - return nil, err - } - - heartLC, err := newHeartbeat(ctx) - if err != nil { - return nil, err - } - - bc, err := newBarChart(ctx) - if err != nil { - return nil, err - } - - don, err := newDonut(ctx) - if err != nil { - return nil, err - } - - leftB, rightB, sineLC, err := newSines(ctx) - if err != nil { - return nil, err - } - return &widgets{ - segDist: sd, - input: input, - rollT: rollT, - spGreen: spGreen, - spRed: spRed, - gauge: g, - heartLC: heartLC, - barChart: bc, - donut: don, - leftB: leftB, - rightB: rightB, - sineLC: sineLC, - }, nil -} - -// layoutType represents the possible layouts the buttons switch between. -type layoutType int - -const ( - // layoutAll displays all the widgets. - layoutAll layoutType = iota - // layoutText focuses onto the text widget. - layoutText - // layoutSparkLines focuses onto the sparklines. - layoutSparkLines - // layoutLineChart focuses onto the linechart. - layoutLineChart -) - -// gridLayout prepares container options that represent the desired screen layout. -// This function demonstrates the use of the grid builder. -// gridLayout() and contLayout() demonstrate the two available layout APIs and -// both produce equivalent layouts for layoutType layoutAll. -func gridLayout(w *widgets, lt layoutType) ([]container.Option, error) { - leftRows := []grid.Element{ - grid.RowHeightPerc(25, - grid.Widget(w.segDist, - container.Border(linestyle.Light), - container.BorderTitle("Press Esc to quit"), - ), - ), - grid.RowHeightPerc(5, - grid.Widget(w.input), - ), - - grid.RowHeightPerc(5, - grid.ColWidthPerc(25, - grid.Widget(w.buttons.allB), - ), - grid.ColWidthPerc(25, - grid.Widget(w.buttons.textB), - ), - grid.ColWidthPerc(25, - grid.Widget(w.buttons.spB), - ), - grid.ColWidthPerc(25, - grid.Widget(w.buttons.lcB), - ), - ), - } - switch lt { - case layoutAll: - leftRows = append(leftRows, - grid.RowHeightPerc(20, - grid.ColWidthPerc(50, - grid.Widget(w.rollT, - container.Border(linestyle.Light), - container.BorderTitle("A rolling text"), - ), - ), - grid.ColWidthPerc(50, - grid.RowHeightPerc(50, - grid.Widget(w.spGreen, - container.Border(linestyle.Light), - container.BorderTitle("Green SparkLine"), - ), - ), - grid.RowHeightPerc(50, - grid.Widget(w.spRed, - container.Border(linestyle.Light), - container.BorderTitle("Red SparkLine"), - ), - ), - ), - ), - grid.RowHeightPerc(7, - grid.Widget(w.gauge, - container.Border(linestyle.Light), - container.BorderTitle("A Gauge"), - container.BorderColor(cell.ColorNumber(39)), - ), - ), - grid.RowHeightPerc(38, - grid.Widget(w.heartLC, - container.Border(linestyle.Light), - container.BorderTitle("A LineChart"), - ), - ), - ) - case layoutText: - leftRows = append(leftRows, - grid.RowHeightPerc(65, - grid.Widget(w.rollT, - container.Border(linestyle.Light), - container.BorderTitle("A rolling text"), - ), - ), - ) - - case layoutSparkLines: - leftRows = append(leftRows, - grid.RowHeightPerc(32, - grid.Widget(w.spGreen, - container.Border(linestyle.Light), - container.BorderTitle("Green SparkLine"), - ), - ), - grid.RowHeightPerc(33, - grid.Widget(w.spRed, - container.Border(linestyle.Light), - container.BorderTitle("Red SparkLine"), - ), - ), - ) - - case layoutLineChart: - leftRows = append(leftRows, - grid.RowHeightPerc(65, - grid.Widget(w.heartLC, - container.Border(linestyle.Light), - container.BorderTitle("A LineChart"), - ), - ), - ) - } - - builder := grid.New() - builder.Add( - grid.ColWidthPerc(70, leftRows...), - ) - - builder.Add( - grid.ColWidthPerc(30, - grid.RowHeightPerc(30, - grid.Widget(w.barChart, - container.Border(linestyle.Light), - container.BorderTitle("BarChart"), - container.BorderTitleAlignRight(), - ), - ), - grid.RowHeightPerc(21, - grid.Widget(w.donut, - container.Border(linestyle.Light), - container.BorderTitle("A Donut"), - container.BorderTitleAlignRight(), - ), - ), - grid.RowHeightPerc(40, - grid.Widget(w.sineLC, - container.Border(linestyle.Light), - container.BorderTitle("Multiple series"), - container.BorderTitleAlignRight(), - ), - ), - grid.RowHeightPerc(9, - grid.ColWidthPerc(50, - grid.Widget(w.leftB, - container.AlignHorizontal(align.HorizontalRight), - container.PaddingRight(1), - ), - ), - grid.ColWidthPerc(50, - grid.Widget(w.rightB, - container.AlignHorizontal(align.HorizontalLeft), - container.PaddingLeft(1), - ), - ), - ), - ), - ) - - gridOpts, err := builder.Build() - if err != nil { - return nil, err - } - return gridOpts, nil -} - -// contLayout prepares container options that represent the desired screen layout. -// This function demonstrates the direct use of the container API. -// gridLayout() and contLayout() demonstrate the two available layout APIs and -// both produce equivalent layouts for layoutType layoutAll. -// contLayout only produces layoutAll. -func contLayout(w *widgets) ([]container.Option, error) { - buttonRow := []container.Option{ - container.SplitVertical( - container.Left( - container.SplitVertical( - container.Left( - container.PlaceWidget(w.buttons.allB), - ), - container.Right( - container.PlaceWidget(w.buttons.textB), - ), - ), - ), - container.Right( - container.SplitVertical( - container.Left( - container.PlaceWidget(w.buttons.spB), - ), - container.Right( - container.PlaceWidget(w.buttons.lcB), - ), - ), - ), - ), - } - - textAndSparks := []container.Option{ - container.SplitVertical( - container.Left( - container.Border(linestyle.Light), - container.BorderTitle("A rolling text"), - container.PlaceWidget(w.rollT), - ), - container.Right( - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("Green SparkLine"), - container.PlaceWidget(w.spGreen), - ), - container.Bottom( - container.Border(linestyle.Light), - container.BorderTitle("Red SparkLine"), - container.PlaceWidget(w.spRed), - ), - ), - ), - ), - } +/* +Copyright © 2021 Bisohns - segmentTextInputSparks := []container.Option{ - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("Press Esc to quit"), - container.PlaceWidget(w.segDist), - ), - container.Bottom( - container.SplitHorizontal( - container.Top( - container.SplitHorizontal( - container.Top( - container.PlaceWidget(w.input), - ), - container.Bottom(buttonRow...), - ), - ), - container.Bottom(textAndSparks...), - container.SplitPercent(40), - ), - ), - container.SplitPercent(50), - ), - } +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - gaugeAndHeartbeat := []container.Option{ - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("A Gauge"), - container.BorderColor(cell.ColorNumber(39)), - container.PlaceWidget(w.gauge), - ), - container.Bottom( - container.Border(linestyle.Light), - container.BorderTitle("A LineChart"), - container.PlaceWidget(w.heartLC), - ), - container.SplitPercent(20), - ), - } + http://www.apache.org/licenses/LICENSE-2.0 - leftSide := []container.Option{ - container.SplitHorizontal( - container.Top(segmentTextInputSparks...), - container.Bottom(gaugeAndHeartbeat...), - container.SplitPercent(50), - ), - } - - lcAndButtons := []container.Option{ - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("Multiple series"), - container.BorderTitleAlignRight(), - container.PlaceWidget(w.sineLC), - ), - container.Bottom( - container.SplitVertical( - container.Left( - container.PlaceWidget(w.leftB), - container.AlignHorizontal(align.HorizontalRight), - container.PaddingRight(1), - ), - container.Right( - container.PlaceWidget(w.rightB), - container.AlignHorizontal(align.HorizontalLeft), - container.PaddingLeft(1), - ), - ), - ), - container.SplitPercent(80), - ), - } - - rightSide := []container.Option{ - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("BarChart"), - container.PlaceWidget(w.barChart), - container.BorderTitleAlignRight(), - ), - container.Bottom( - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("A Donut"), - container.BorderTitleAlignRight(), - container.PlaceWidget(w.donut), - ), - container.Bottom(lcAndButtons...), - container.SplitPercent(30), - ), - ), - container.SplitPercent(30), - ), - } - - return []container.Option{ - container.SplitVertical( - container.Left(leftSide...), - container.Right(rightSide...), - container.SplitPercent(70), - ), - }, nil -} - -// rootID is the ID assigned to the root container. -const rootID = "root" +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main -// Terminal implementations -const ( - termboxTerminal = "termbox" - tcellTerminal = "tcell" -) +import "github.com/bisohns/saido/cmd" func main() { cmd.Execute() - log.Debug("Starting Saido Main") - t, err := tcell.New(tcell.ColorMode(terminalapi.ColorMode256)) - if err != nil { - panic(err) - } - defer t.Close() - - c, err := container.New(t, container.ID(rootID)) - if err != nil { - panic(err) - } - - ctx, cancel := context.WithCancel(context.Background()) - w, err := newWidgets(ctx, t, c) - if err != nil { - panic(err) - } - lb, err := newLayoutButtons(c, w) - if err != nil { - panic(err) - } - w.buttons = lb - - gridOpts, err := gridLayout(w, layoutAll) // equivalent to contLayout(w) - if err != nil { - panic(err) - } - - if err := c.Update(rootID, gridOpts...); err != nil { - panic(err) - } - - quitter := func(k *terminalapi.Keyboard) { - if k.Key == keyboard.KeyEsc || k.Key == keyboard.KeyCtrlC { - cancel() - } - } - if err := termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(charts.RedrawInterval)); err != nil { - panic(err) - } -} - -// textState creates a rotated state for the text we are displaying. -func textState(text string, capacity, step int) []rune { - if capacity == 0 { - return nil - } - - var state []rune - for i := 0; i < capacity; i++ { - state = append(state, ' ') - } - state = append(state, []rune(text)...) - step = step % len(state) - return rotateRunes(state, step) -} - -// newTextInput creates a new TextInput field that changes the text on the -// SegmentDisplay. -func newTextInput(updateText chan<- string) (*textinput.TextInput, error) { - input, err := textinput.New( - textinput.Label("Change text to: ", cell.FgColor(cell.ColorNumber(33))), - textinput.MaxWidthCells(20), - textinput.PlaceHolder("enter any text"), - textinput.OnSubmit(func(text string) error { - updateText <- text - return nil - }), - textinput.ClearOnSubmit(), - ) - if err != nil { - return nil, err - } - return input, err -} - -// newSegmentDisplay creates a new SegmentDisplay that initially shows the -// Termdash name. Shows any text that is sent over the channel. -func newSegmentDisplay(ctx context.Context, t terminalapi.Terminal, updateText <-chan string) (*segmentdisplay.SegmentDisplay, error) { - sd, err := segmentdisplay.New() - if err != nil { - return nil, err - } - - colors := []cell.Color{ - cell.ColorNumber(33), - cell.ColorRed, - cell.ColorYellow, - cell.ColorNumber(33), - cell.ColorGreen, - cell.ColorRed, - cell.ColorGreen, - cell.ColorRed, - } - - text := "Termdash" - step := 0 - - go func() { - ticker := time.NewTicker(500 * time.Millisecond) - defer ticker.Stop() - - capacity := 0 - termSize := t.Size() - for { - select { - case <-ticker.C: - if capacity == 0 { - // The segment display only knows its capacity after both - // text size and terminal size are known. - capacity = sd.Capacity() - } - if t.Size().Eq(image.ZP) || !t.Size().Eq(termSize) { - // Update the capacity initially the first time the - // terminal reports a non-zero size and then every time the - // terminal resizes. - // - // This is better than updating the capacity on every - // iteration since that leads to edge cases - segment - // display capacity depends on the length of text and here - // we are trying to adjust the text length to the capacity. - termSize = t.Size() - capacity = sd.Capacity() - } - - state := textState(text, capacity, step) - var chunks []*segmentdisplay.TextChunk - for i := 0; i < capacity; i++ { - if i >= len(state) { - break - } - - color := colors[i%len(colors)] - chunks = append(chunks, segmentdisplay.NewChunk( - string(state[i]), - segmentdisplay.WriteCellOpts(cell.FgColor(color)), - )) - } - if len(chunks) == 0 { - continue - } - if err := sd.Write(chunks); err != nil { - panic(err) - } - step++ - - case t := <-updateText: - text = t - sd.Reset() - step = 0 - - case <-ctx.Done(): - return - } - } - }() - return sd, nil -} - -// newRollText creates a new Text widget that displays rolling text. -func newRollText(ctx context.Context) (*text.Text, error) { - t, err := text.New(text.RollContent()) - if err != nil { - return nil, err - } - - i := 0 - go charts.Periodic(ctx, 1*time.Second, func() error { - if err := t.Write(fmt.Sprintf("Writing line %d.\n", i), text.WriteCellOpts(cell.FgColor(cell.ColorNumber(142)))); err != nil { - return err - } - i++ - return nil - }) - return t, nil -} - -// newSparkLines creates two new sparklines displaying random values. -func newSparkLines(ctx context.Context) (*sparkline.SparkLine, *sparkline.SparkLine, error) { - spGreen, err := sparkline.New( - sparkline.Color(cell.ColorGreen), - ) - if err != nil { - return nil, nil, err - } - - const max = 100 - go charts.Periodic(ctx, 250*time.Millisecond, func() error { - v := int(rand.Int31n(max + 1)) - return spGreen.Add([]int{v}) - }) - - spRed, err := sparkline.New( - sparkline.Color(cell.ColorRed), - ) - if err != nil { - return nil, nil, err - } - go charts.Periodic(ctx, 500*time.Millisecond, func() error { - v := int(rand.Int31n(max + 1)) - return spRed.Add([]int{v}) - }) - return spGreen, spRed, nil - -} - -// newGauge creates a demo Gauge widget. -func newGauge(ctx context.Context) (*gauge.Gauge, error) { - g, err := gauge.New() - if err != nil { - return nil, err - } - - const start = 35 - progress := start - - go charts.Periodic(ctx, 2*time.Second, func() error { - if err := g.Percent(progress); err != nil { - return err - } - progress++ - if progress > 100 { - progress = start - } - return nil - }) - return g, nil -} - -// newDonut creates a demo Donut widget. -func newDonut(ctx context.Context) (*donut.Donut, error) { - d, err := donut.New(donut.CellOpts( - cell.FgColor(cell.ColorNumber(33))), - ) - if err != nil { - return nil, err - } - - const start = 35 - progress := start - - go charts.Periodic(ctx, 500*time.Millisecond, func() error { - if err := d.Percent(progress); err != nil { - return err - } - progress++ - if progress > 100 { - progress = start - } - return nil - }) - return d, nil -} - -// newHeartbeat returns a line chart that displays a heartbeat-like progression. -func newHeartbeat(ctx context.Context) (*linechart.LineChart, error) { - var inputs []float64 - for i := 0; i < 100; i++ { - v := math.Pow(math.Sin(float64(i)), 63) * math.Sin(float64(i)+1.5) * 8 - inputs = append(inputs, v) - } - - lc, err := linechart.New( - linechart.AxesCellOpts(cell.FgColor(cell.ColorRed)), - linechart.YLabelCellOpts(cell.FgColor(cell.ColorGreen)), - linechart.XLabelCellOpts(cell.FgColor(cell.ColorGreen)), - ) - if err != nil { - return nil, err - } - step := 0 - go charts.Periodic(ctx, charts.RedrawInterval/3, func() error { - step = (step + 1) % len(inputs) - return lc.Series("heartbeat", rotateFloats(inputs, step), - linechart.SeriesCellOpts(cell.FgColor(cell.ColorNumber(87))), - linechart.SeriesXLabels(map[int]string{ - 0: "zero", - }), - ) - }) - return lc, nil -} - -// newBarChart returns a BarcChart that displays random values on multiple bars. -func newBarChart(ctx context.Context) (*barchart.BarChart, error) { - bc, err := barchart.New( - barchart.BarColors([]cell.Color{ - cell.ColorNumber(33), - cell.ColorNumber(39), - cell.ColorNumber(45), - cell.ColorNumber(51), - cell.ColorNumber(81), - cell.ColorNumber(87), - }), - barchart.ValueColors([]cell.Color{ - cell.ColorBlack, - cell.ColorBlack, - cell.ColorBlack, - cell.ColorBlack, - cell.ColorBlack, - cell.ColorBlack, - }), - barchart.ShowValues(), - ) - if err != nil { - return nil, err - } - - const ( - bars = 6 - max = 100 - ) - values := make([]int, bars) - go charts.Periodic(ctx, 1*time.Second, func() error { - for i := range values { - values[i] = int(rand.Int31n(max + 1)) - } - - return bc.Values(values, max) - }) - return bc, nil -} - -// distance is a thread-safe int value used by the newSince method. -// Buttons write it and the line chart reads it. -type distance struct { - v int - mu sync.Mutex -} - -// add adds the provided value to the one stored. -func (d *distance) add(v int) { - d.mu.Lock() - defer d.mu.Unlock() - d.v += v -} - -// get returns the current value. -func (d *distance) get() int { - d.mu.Lock() - defer d.mu.Unlock() - return d.v -} - -// newSines returns a line chart that displays multiple sine series and two buttons. -// The left button shifts the second series relative to the first series to -// the left and the right button shifts it to the right. -func newSines(ctx context.Context) (left, right *button.Button, lc *linechart.LineChart, err error) { - var inputs []float64 - for i := 0; i < 200; i++ { - v := math.Sin(float64(i) / 100 * math.Pi) - inputs = append(inputs, v) - } - - sineLc, err := linechart.New( - linechart.AxesCellOpts(cell.FgColor(cell.ColorRed)), - linechart.YLabelCellOpts(cell.FgColor(cell.ColorGreen)), - linechart.XLabelCellOpts(cell.FgColor(cell.ColorGreen)), - ) - if err != nil { - return nil, nil, nil, err - } - step1 := 0 - secondDist := &distance{v: 100} - go charts.Periodic(ctx, charts.RedrawInterval/3, func() error { - step1 = (step1 + 1) % len(inputs) - if err := lc.Series("first", rotateFloats(inputs, step1), - linechart.SeriesCellOpts(cell.FgColor(cell.ColorNumber(33))), - ); err != nil { - return err - } - - step2 := (step1 + secondDist.get()) % len(inputs) - return lc.Series("second", rotateFloats(inputs, step2), linechart.SeriesCellOpts(cell.FgColor(cell.ColorWhite))) - }) - - // diff is the difference a single button press adds or removes to the - // second series. - const diff = 20 - leftB, err := button.New("(l)eft", func() error { - secondDist.add(diff) - return nil - }, - button.GlobalKey('l'), - button.WidthFor("(r)ight"), - button.FillColor(cell.ColorNumber(220)), - ) - if err != nil { - return nil, nil, nil, err - } - - rightB, err := button.New("(r)ight", func() error { - secondDist.add(-diff) - return nil - }, - button.GlobalKey('r'), - button.FillColor(cell.ColorNumber(196)), - ) - if err != nil { - return nil, nil, nil, err - } - return leftB, rightB, sineLc, nil -} - -// setLayout sets the specified layout. -func setLayout(c *container.Container, w *widgets, lt layoutType) error { - gridOpts, err := gridLayout(w, lt) - if err != nil { - return err - } - return c.Update(rootID, gridOpts...) -} - -// layoutButtons are buttons that change the layout. -type layoutButtons struct { - allB *button.Button - textB *button.Button - spB *button.Button - lcB *button.Button -} - -// newLayoutButtons returns buttons that dynamically switch the layouts. -func newLayoutButtons(c *container.Container, w *widgets) (*layoutButtons, error) { - opts := []button.Option{ - button.WidthFor("sparklines"), - button.FillColor(cell.ColorNumber(220)), - button.Height(1), - } - - allB, err := button.New("all", func() error { - return setLayout(c, w, layoutAll) - }, opts...) - if err != nil { - return nil, err - } - - textB, err := button.New("text", func() error { - return setLayout(c, w, layoutText) - }, opts...) - if err != nil { - return nil, err - } - - spB, err := button.New("sparklines", func() error { - return setLayout(c, w, layoutSparkLines) - }, opts...) - if err != nil { - return nil, err - } - - lcB, err := button.New("linechart", func() error { - return setLayout(c, w, layoutLineChart) - }, opts...) - if err != nil { - return nil, err - } - - return &layoutButtons{ - allB: allB, - textB: textB, - spB: spB, - lcB: lcB, - }, nil -} - -// rotateFloats returns a new slice with inputs rotated by step. -// I.e. for a step of one: -// inputs[0] -> inputs[len(inputs)-1] -// inputs[1] -> inputs[0] -// And so on. -func rotateFloats(inputs []float64, step int) []float64 { - return append(inputs[step:], inputs[:step]...) -} - -// rotateRunes returns a new slice with inputs rotated by step. -// I.e. for a step of one: -// inputs[0] -> inputs[len(inputs)-1] -// inputs[1] -> inputs[0] -// And so on. -func rotateRunes(inputs []rune, step int) []rune { - return append(inputs[step:], inputs[:step]...) }