diff --git a/client/backend.go b/client/backend.go deleted file mode 100644 index e1610a1..0000000 --- a/client/backend.go +++ /dev/null @@ -1,153 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "errors" - "github.com/sqshq/sampler/metadata" - "io/ioutil" - "net/http" -) - -const ( - backendUrl = "http://localhost/api/v1" - installationPath = "/telemetry/installation" - statisticsPath = "/telemetry/statistics" - crashPath = "/telemetry/crash" - registrationPath = "/license/registration" - verificationPath = "/license/verification" -) - -// BackendClient is used to verify license and to send telemetry -// for analyses (anonymous usage data statistics and crash reports) -type BackendClient struct { - client http.Client -} - -func NewBackendClient() *BackendClient { - return &BackendClient{ - client: http.Client{}, - } -} - -func (c *BackendClient) ReportInstallation(statistics *metadata.Statistics) { - - buf := new(bytes.Buffer) - err := json.NewEncoder(buf).Encode(statistics) - if err != nil { - c.ReportCrash(err.Error(), statistics) - } - - _, err = sendRequest(backendUrl+installationPath, buf) - - if err != nil { - c.ReportCrash(err.Error(), statistics) - } -} - -func (c *BackendClient) ReportStatistics(statistics *metadata.Statistics) { - - buf := new(bytes.Buffer) - err := json.NewEncoder(buf).Encode(statistics) - if err != nil { - c.ReportCrash(err.Error(), statistics) - } - - _, err = sendRequest(backendUrl+statisticsPath, buf) - if err != nil { - c.ReportCrash(err.Error(), statistics) - } -} - -func (c *BackendClient) ReportCrash(error string, statistics *metadata.Statistics) { - - req := struct { - Error string - Statistics *metadata.Statistics - }{ - error, - statistics, - } - - buf := new(bytes.Buffer) - err := json.NewEncoder(buf).Encode(req) - if err != nil { - return - } - - _, _ = sendRequest(backendUrl+crashPath, buf) -} - -func (c *BackendClient) RegisterLicenseKey(licenseKey string, statistics *metadata.Statistics) (*metadata.License, error) { - - req := struct { - LicenseKey string - Statistics *metadata.Statistics - }{ - licenseKey, - statistics, - } - - buf := new(bytes.Buffer) - err := json.NewEncoder(buf).Encode(req) - if err != nil { - c.ReportCrash(err.Error(), statistics) - } - - response, err := sendRequest(backendUrl+registrationPath, buf) - - if err != nil { - return nil, err - } - - if response.StatusCode != 200 { - body, _ := ioutil.ReadAll(response.Body) - return nil, errors.New(string(body)) - } - - var license metadata.License - json.NewDecoder(response.Body).Decode(&license) - - return &license, nil -} - -func (c *BackendClient) VerifyLicenseKey(licenseKey string) (*metadata.License, error) { - - req := struct { - LicenseKey string - }{ - licenseKey, - } - - buf := new(bytes.Buffer) - err := json.NewEncoder(buf).Encode(req) - if err != nil { - c.ReportCrash(err.Error(), nil) - } - - response, err := sendRequest(backendUrl+verificationPath, buf) - - if err != nil { - return nil, err - } - - if response.StatusCode != 200 { - body, _ := ioutil.ReadAll(response.Body) - return nil, errors.New(string(body)) - } - - var license metadata.License - json.NewDecoder(response.Body).Decode(&license) - - return &license, nil -} - -func sendRequest(url string, body *bytes.Buffer) (resp *http.Response, err error) { - c := http.DefaultClient - req, err := http.NewRequest("POST", url, body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - return c.Do(req) -} diff --git a/component/intro.go b/component/intro.go deleted file mode 100644 index 4bfb41d..0000000 --- a/component/intro.go +++ /dev/null @@ -1,142 +0,0 @@ -package component - -import ( - ui "github.com/gizak/termui/v3" - "github.com/sqshq/sampler/component/util" - "github.com/sqshq/sampler/console" -) - -type Intro struct { - *ui.Block - page IntroPage - option introOption - palette console.Palette -} - -type IntroPage rune - -const ( - IntroPageWelcome IntroPage = 0 - IntroPageCommercial IntroPage = 1 - IntroPagePersonal IntroPage = 2 -) - -type introOption rune - -const ( - introOptionCommercial introOption = 0 - introOptionPersonal introOption = 1 -) - -const ( - buttonCommercial string = " COMMERCIAL USE " - buttonPersonal string = " PERSONAL USE " - buttonOk string = " OK " -) - -func (intro *Intro) Up() { - intro.option = introOptionCommercial -} - -func (intro *Intro) Down() { - intro.option = introOptionPersonal -} - -func (intro *Intro) NextPage() { - if intro.option == introOptionCommercial { - intro.page = IntroPageCommercial - } else { - intro.page = IntroPagePersonal - } -} - -func (intro *Intro) GetSelectedPage() IntroPage { - return intro.page -} - -func NewIntro(palette console.Palette) *Intro { - return &Intro{ - Block: NewBlock("", false, palette), - palette: palette, - } -} - -func (intro *Intro) Draw(buffer *ui.Buffer) { - - introText := append(util.AsciiLogo, []string{ - "", "", "", - "Welcome.", - "", - "Sampler is an OSS project, and it needs funding to be alive and keep developing", - "Before the first start, please explore our licensing options below. For more details, visit WWW.SAMPLER.DEV", - "", "", "", - "How do you plan to use Sampler?", - }...) - - commericalText := append(util.AsciiLogo, []string{ - "", "", "", "", - "With Sampler, you can easily save time and solve some of your business problems.", - "That's why support of the project is in the interest of your organization.", - "", - "", - "We are offering commercial licenses which provide priority support and technical assistance.", - "After entering the licence key, your company name will appear in the status bar.", - "", - "", - "To make a purchase, please visit WWW.SAMPLER.DEV", - }...) - - personalText := append(util.AsciiLogo, []string{ - "", "", "", "", - "Sampler is always free to use, but you can support the project and donate any amount to get a personal license.", - "Once it is activated, your name will appear in the status bar.", - "", - "", - "To become a sponsor, please visit WWW.SAMPLER.DEV", - }...) - - text := introText - - switch intro.page { - case IntroPageWelcome: - text = introText - case IntroPageCommercial: - text = commericalText - case IntroPagePersonal: - text = personalText - } - - for i, a := range text { - util.PrintString( - a, - ui.NewStyle(intro.palette.BaseColor), - util.GetMiddlePoint(intro.Block.Rectangle, a, i-15), - buffer) - } - - highlightedStyle := ui.NewStyle(intro.palette.ReverseColor, intro.palette.BaseColor) - regularStyle := ui.NewStyle(intro.palette.BaseColor, intro.palette.ReverseColor) - - if intro.page == IntroPageWelcome { - - commercialButtonStyle := highlightedStyle - if intro.option == introOptionPersonal { - commercialButtonStyle = regularStyle - } - - personalButtonStyle := highlightedStyle - if intro.option == introOptionCommercial { - personalButtonStyle = regularStyle - } - - buffer.SetString(buttonCommercial, commercialButtonStyle, - util.GetMiddlePoint(intro.Block.Rectangle, buttonCommercial, 5)) - buffer.SetString(buttonPersonal, personalButtonStyle, - util.GetMiddlePoint(intro.Block.Rectangle, buttonPersonal, 7)) - } else { - buffer.SetString(buttonOk, highlightedStyle, - util.GetMiddlePoint(intro.Block.Rectangle, buttonOk, 7)) - } - - intro.Block.Draw(buffer) -} diff --git a/component/layout/layout.go b/component/layout/layout.go index ee95aac..831b41c 100644 --- a/component/layout/layout.go +++ b/component/layout/layout.go @@ -19,8 +19,6 @@ type Layout struct { Components []*component.Component statusbar *component.StatusBar menu *component.Menu - intro *component.Intro - nag *component.NagWindow ChangeModeEvents chan Mode mode Mode selection int @@ -33,28 +31,24 @@ type Mode rune const ( ModeDefault Mode = 0 ModeIntro Mode = 1 - ModeNag Mode = 2 - ModePause Mode = 3 - ModeComponentSelect Mode = 4 - ModeMenuOptionSelect Mode = 5 - ModeComponentMove Mode = 6 - ModeComponentResize Mode = 7 - ModeChartPinpoint Mode = 8 + ModePause Mode = 2 + ModeComponentSelect Mode = 3 + ModeMenuOptionSelect Mode = 4 + ModeComponentMove Mode = 5 + ModeComponentResize Mode = 6 + ModeChartPinpoint Mode = 7 ) const ( - minDimension = 3 - statusbarHeight = 1 - nagWindowDurationSec = 5 + minDimension = 3 + statusbarHeight = 1 ) -func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *component.Intro, nag *component.NagWindow) *Layout { +func NewLayout(statusline *component.StatusBar, menu *component.Menu) *Layout { width, height := ui.TerminalDimensions() block := *ui.NewBlock() block.SetRect(0, 0, width, height) - intro.SetRect(0, 0, width, height) - nag.SetRect(0, 0, width, height) statusline.SetRect(0, height-statusbarHeight, width, height) return &Layout{ @@ -62,8 +56,6 @@ func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *com Components: make([]*component.Component, 0), statusbar: statusline, menu: menu, - intro: intro, - nag: nag, mode: ModeDefault, selection: 0, ChangeModeEvents: make(chan Mode, 10), @@ -79,10 +71,6 @@ func (l *Layout) StartWithIntro() { l.mode = ModeIntro } -func (l *Layout) StartWithNagWindow() { - l.mode = ModeNag -} - func (l *Layout) changeMode(m Mode) { if m == ModeComponentResize || m == ModeComponentMove { l.positionsChanged = true @@ -92,7 +80,7 @@ func (l *Layout) changeMode(m Mode) { } func (l *Layout) HandleMouseClick(x int, y int) { - if l.mode == ModeIntro || l.mode == ModeNag { + if l.mode == ModeIntro { return } l.menu.Idle() @@ -151,15 +139,6 @@ func (l *Layout) HandleKeyboardEvent(e string) { l.menu.Idle() l.changeMode(ModeDefault) break - case ModeIntro: - page := l.intro.GetSelectedPage() - if page == component.IntroPageWelcome { - l.intro.NextPage() - } else { - l.changeMode(ModeDefault) - } - case ModeNag: - l.nag.Accept() } case console.KeyEsc: l.resetAlerts() @@ -222,8 +201,6 @@ func (l *Layout) HandleKeyboardEvent(e string) { selected.Move(0, -1) case ModeComponentResize: selected.Resize(0, -1) - case ModeIntro: - l.intro.Up() } case console.KeyDown: switch l.mode { @@ -239,8 +216,6 @@ func (l *Layout) HandleKeyboardEvent(e string) { selected.Move(0, 1) case ModeComponentResize: selected.Resize(0, 1) - case ModeIntro: - l.intro.Down() } } } @@ -330,22 +305,6 @@ func (l *Layout) Draw(buffer *ui.Buffer) { c.SetRect(rectangle.Min.X, rectangle.Min.Y, rectangle.Max.X, rectangle.Max.Y) } - if l.mode == ModeIntro { - l.intro.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y) - l.intro.Draw(buffer) - return - } - - if l.mode == ModeNag { - if l.nag.IsAccepted() && time.Since(l.startupTime).Seconds() > nagWindowDurationSec { - l.mode = ModeDefault - } else { - l.nag.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y) - l.nag.Draw(buffer) - return - } - } - for _, c := range l.Components { c.Draw(buffer) } diff --git a/component/nag.go b/component/nag.go deleted file mode 100644 index 9d335ad..0000000 --- a/component/nag.go +++ /dev/null @@ -1,54 +0,0 @@ -package component - -import ( - ui "github.com/gizak/termui/v3" - "github.com/sqshq/sampler/component/util" - "github.com/sqshq/sampler/console" -) - -type NagWindow struct { - *ui.Block - palette console.Palette - accepted bool -} - -func NewNagWindow(palette console.Palette) *NagWindow { - return &NagWindow{ - Block: NewBlock("", false, palette), - palette: palette, - accepted: false, - } -} - -func (n *NagWindow) Accept() { - n.accepted = true -} - -func (n *NagWindow) IsAccepted() bool { - return n.accepted -} - -func (n *NagWindow) Draw(buffer *ui.Buffer) { - - text := append(util.AsciiLogo, []string{ - "", "", "", - "Thank you for using Sampler.", - "It is always free, but you can sponsor the project and buy the personal or commercial license", - "for priority support and technical assistance.", - "", - "Please visit www.sampler.dev", - }...) - - for i, a := range text { - util.PrintString( - a, - ui.NewStyle(n.palette.BaseColor), - util.GetMiddlePoint(n.Block.Rectangle, a, i-15), - buffer) - } - - buffer.SetString(buttonOk, ui.NewStyle(n.palette.ReverseColor, n.palette.BaseColor), - util.GetMiddlePoint(n.Block.Rectangle, buttonOk, 4)) - - n.Block.Draw(buffer) -} diff --git a/component/statusbar.go b/component/statusbar.go index 8d43033..5bd89cd 100644 --- a/component/statusbar.go +++ b/component/statusbar.go @@ -4,7 +4,6 @@ import ( "fmt" ui "github.com/gizak/termui/v3" "github.com/sqshq/sampler/console" - "github.com/sqshq/sampler/metadata" "image" ) @@ -20,24 +19,11 @@ type StatusBar struct { pause bool } -func NewStatusBar(configFileName string, palette console.Palette, license *metadata.License) *StatusBar { +func NewStatusBar(configFileName string, palette console.Palette) *StatusBar { block := *ui.NewBlock() block.Border = false - text := fmt.Sprintf(" %s %s | ", console.AppTitle, console.AppVersion) - - if license == nil || !license.Valid || license.Type == nil { - text += console.AppLicenseWarning - } else if *license.Type == metadata.TypePersonal { - text += fmt.Sprintf("%s | personal license: %s", configFileName, *license.Username) - } else if license.Username != nil { - text += fmt.Sprintf("%s | licensed to %s", configFileName, *license.Username) - if license.Company != nil { - text += fmt.Sprintf(", %s", *license.Company) - } - } else { - text += fmt.Sprintf("%s | licensed to %s", configFileName, *license.Company) - } + text := fmt.Sprintf(" %s %s | %s", console.AppTitle, console.AppVersion, configFileName) return &StatusBar{ Block: NewBlock("", false, palette), diff --git a/config/config.go b/config/config.go index 1160d0c..fcf8486 100644 --- a/config/config.go +++ b/config/config.go @@ -34,14 +34,10 @@ func LoadConfig() (*Config, Options) { console.Exit(console.AppVersion) } - if opt.ConfigFile == nil && opt.LicenseKey == nil { + if opt.ConfigFile == nil { console.Exit("Please specify config file using --config flag. Example: sampler --config example.yml") } - if opt.LicenseKey != nil { - return nil, opt - } - cfg := readFile(opt.ConfigFile) cfg.validate() cfg.setDefaults() diff --git a/config/options.go b/config/options.go index 6b63b9e..72f5a0a 100644 --- a/config/options.go +++ b/config/options.go @@ -2,9 +2,7 @@ package config // Options with cli flags type Options struct { - ConfigFile *string `short:"c" long:"config" description:"Path to YAML config file"` - LicenseKey *string `short:"l" long:"license" description:"License key. Visit www.sampler.dev for details"` - Environment []string `short:"e" long:"env" description:"Specify name=value variable to use in script placeholder as $name. This flag takes precedence over the same name variables, specified in config yml"` - Version bool `short:"v" long:"version" description:"Print version"` - DisableTelemetry bool `long:"disable-telemetry" description:"Disable anonymous usage statistics and errors to be sent to Sampler online service for analyses"` + ConfigFile *string `short:"c" long:"config" description:"Path to YAML config file"` + Environment []string `short:"e" long:"env" description:"Specify name=value variable to use in script placeholder as $name. This flag takes precedence over the same name variables, specified in config yml"` + Version bool `short:"v" long:"version" description:"Print version"` } diff --git a/console/console.go b/console/console.go index d267d24..bfc3a5a 100644 --- a/console/console.go +++ b/console/console.go @@ -16,7 +16,6 @@ const ( RowsCount = 40 AppTitle = "sampler" AppVersion = "1.0.3" - AppLicenseWarning = "NOT ACTIVATED. PLEASE CONSIDER TO PURCHASE PERSONAL OR COMMERCIAL LICENSE. WWW.SAMPLER.DEV " ) const ( diff --git a/main.go b/main.go index 47ebccf..a97ad8f 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,8 @@ package main import ( - "fmt" ui "github.com/gizak/termui/v3" "github.com/sqshq/sampler/asset" - "github.com/sqshq/sampler/client" "github.com/sqshq/sampler/component" "github.com/sqshq/sampler/component/asciibox" "github.com/sqshq/sampler/component/barchart" @@ -17,8 +15,6 @@ import ( "github.com/sqshq/sampler/console" "github.com/sqshq/sampler/data" "github.com/sqshq/sampler/event" - "github.com/sqshq/sampler/metadata" - "runtime/debug" "time" ) @@ -71,14 +67,6 @@ func (s *Starter) start(drawable ui.Drawable, consumer *data.Consumer, component func main() { cfg, opt := config.LoadConfig() - bc := client.NewBackendClient() - - statistics := metadata.GetStatistics(cfg) - license := metadata.GetLicense() - - if opt.LicenseKey != nil { - registerLicense(statistics, opt, bc) - } console.Init() defer console.Close() @@ -88,28 +76,8 @@ func main() { defer player.Close() } - defer handleCrash(statistics, opt, bc) - defer updateStatistics(cfg, time.Now()) - palette := console.GetPalette(*cfg.Theme) - lout := layout.NewLayout(component.NewStatusBar(*opt.ConfigFile, palette, license), - component.NewMenu(palette), component.NewIntro(palette), component.NewNagWindow(palette)) - - if statistics.LaunchCount == 0 { - if !opt.DisableTelemetry { - go bc.ReportInstallation(statistics) - } - lout.StartWithIntro() - } else if statistics.LaunchCount%20 == 0 { // once in a while - if license == nil || !license.Valid { - lout.StartWithNagWindow() - } else { - go verifyLicense(license, bc) - } - if !opt.DisableTelemetry { - go bc.ReportStatistics(statistics) - } - } + lout := layout.NewLayout(component.NewStatusBar(*opt.ConfigFile, palette), component.NewMenu(palette)) starter := &Starter{player, lout, palette, opt, *cfg} samplers := starter.startAll() @@ -117,34 +85,3 @@ func main() { handler := event.NewHandler(samplers, opt, lout) handler.HandleEvents() } - -func handleCrash(statistics *metadata.Statistics, opt config.Options, bc *client.BackendClient) { - if rec := recover(); rec != nil { - err := rec.(error) - if !opt.DisableTelemetry { - bc.ReportCrash(fmt.Sprintf("%s\n%s", err.Error(), string(debug.Stack())), statistics) - } - panic(err) - } -} - -func updateStatistics(cfg *config.Config, startTime time.Time) { - metadata.PersistStatistics(cfg, time.Since(startTime)) -} - -func registerLicense(statistics *metadata.Statistics, opt config.Options, bc *client.BackendClient) { - lc, err := bc.RegisterLicenseKey(*opt.LicenseKey, statistics) - if err != nil { - console.Exit("License registration failed: " + err.Error()) - } else { - metadata.SaveLicense(*lc) - console.Exit("License successfully verified, Sampler can be restarted without --license flag now. Thank you.") - } -} - -func verifyLicense(license *metadata.License, bc *client.BackendClient) { - verifiedLicense, _ := bc.VerifyLicenseKey(*license.Key) - if verifiedLicense != nil { - metadata.SaveLicense(*verifiedLicense) - } -} diff --git a/metadata/license.go b/metadata/license.go deleted file mode 100644 index 9518017..0000000 --- a/metadata/license.go +++ /dev/null @@ -1,53 +0,0 @@ -package metadata - -import ( - "gopkg.in/yaml.v3" - "log" -) - -type License struct { - Key *string `yaml:"k"` - Username *string `yaml:"u"` - Company *string `yaml:"c"` - Type *LicenseType `yaml:"t"` - Valid bool `yaml:"v"` -} - -type LicenseType rune - -const ( - TypePersonal LicenseType = 0 - TypeCommercial LicenseType = 1 -) - -const licenseFileName = "license.yml" - -func GetLicense() *License { - - if !fileExists(licenseFileName) { - return nil - } - - file := readStorageFile(getPlatformStoragePath(licenseFileName)) - - license := new(License) - err := yaml.Unmarshal(file, license) - - if err != nil { - log.Fatalf("Failed to read license file: %v", err) - } - - return license -} - -func SaveLicense(license License) { - - initStorage() - - file, err := yaml.Marshal(license) - if err != nil { - log.Fatalf("Failed to marshal license file: %v", err) - } - - saveStorageFile(file, licenseFileName) -} diff --git a/metadata/license_test.go b/metadata/license_test.go deleted file mode 100644 index aeaa1f8..0000000 --- a/metadata/license_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package metadata - -import ( - "os" - "testing" -) - -func Test_getEmptyLicense(t *testing.T) { - - cleanupPlatformStorage() - license := GetLicense() - - if license != nil { - t.Errorf("expected to be nil") - } -} - -func Test_saveAndGetExistingLicense(t *testing.T) { - - cleanupPlatformStorage() - - original := License{ - Valid: true, - } - - SaveLicense(original) - - retrieved := *GetLicense() - - if original != retrieved { - t.Errorf("read file != saved file") - } -} - -func cleanupPlatformStorage() { - _ = os.RemoveAll(getPlatformStoragePath("")) - _ = os.Remove(getPlatformStoragePath("")) -} diff --git a/metadata/statistics.go b/metadata/statistics.go deleted file mode 100644 index 240b9ac..0000000 --- a/metadata/statistics.go +++ /dev/null @@ -1,113 +0,0 @@ -package metadata - -import ( - ui "github.com/gizak/termui/v3" - "github.com/sqshq/sampler/config" - "github.com/sqshq/sampler/console" - "gopkg.in/yaml.v3" - "log" - "runtime" - "time" -) - -// Statistics represents anonymous usage data, which we collect for analyses and improvements -// User can disable it, along with crash reports, using --disable-telemetry flag -type Statistics struct { - Version string - OS string - WindowWidth int `yaml:"ww"` - WindowHeight int `yaml:"wh"` - LaunchCount int `yaml:"lc"` - UsageTime int `yaml:"ut"` - ComponentsCount map[string]int `yaml:"cc"` -} - -const statisticsFileName = "statistics.yml" - -func PersistStatistics(config *config.Config, uptime time.Duration) *Statistics { - - statistics := new(Statistics) - w, h := ui.TerminalDimensions() - - if fileExists(statisticsFileName) { - file := readStorageFile(getPlatformStoragePath(statisticsFileName)) - err := yaml.Unmarshal(file, statistics) - - if err != nil { - log.Fatalf("Failed to read statistics file: %v", err) - } - - if config != nil { - statistics.ComponentsCount = countComponentsPerType(config) - } - - statistics.Version = console.AppVersion - statistics.WindowWidth = w - statistics.WindowHeight = h - statistics.LaunchCount += 1 - statistics.UsageTime += int(uptime.Seconds()) - - } else { - statistics = &Statistics{ - Version: console.AppVersion, - OS: runtime.GOOS, - WindowWidth: w, - WindowHeight: h, - LaunchCount: 1, - UsageTime: 0, - ComponentsCount: countComponentsPerType(config), - } - initStorage() - } - - file, err := yaml.Marshal(statistics) - if err != nil { - log.Fatalf("Failed to marshal statistics file: %v", err) - } - - saveStorageFile(file, statisticsFileName) - - return statistics -} - -func GetStatistics(cfg *config.Config) *Statistics { - - if !fileExists(statisticsFileName) { - return &Statistics{ - Version: console.AppVersion, - OS: runtime.GOOS, - LaunchCount: 0, - WindowWidth: 0, - WindowHeight: 0, - ComponentsCount: countComponentsPerType(cfg), - } - } - - file := readStorageFile(getPlatformStoragePath(statisticsFileName)) - license := new(Statistics) - - err := yaml.Unmarshal(file, license) - if err != nil { - log.Fatalf("Failed to read statistics file: %v", err) - } - - return license -} - -func countComponentsPerType(config *config.Config) map[string]int { - - m := make(map[string]int) - - if config == nil { - return m - } - - m["runcharts"] = len(config.RunCharts) - m["sparkLines"] = len(config.SparkLines) - m["barcharts"] = len(config.BarCharts) - m["gauges"] = len(config.Gauges) - m["asciiboxes"] = len(config.AsciiBoxes) - m["textboxes"] = len(config.TextBoxes) - - return m -}