parent
8c3242b07e
commit
273aded7ff
195
main.go
195
main.go
|
@ -5,8 +5,11 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -46,18 +49,196 @@ func main() {
|
|||
fmt.Printf("could not load data: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(data)
|
||||
query := fs.Arg(0)
|
||||
|
||||
// Find a single feature
|
||||
if feat, ok := data.Data[query]; ok {
|
||||
fmt.Println(formatFeat(feat, data, false))
|
||||
return
|
||||
}
|
||||
|
||||
func showFeat(d data) {
|
||||
var p []string
|
||||
if d.UsagePercY > 0 {
|
||||
p = append(p, fmt.Sprintf("%s %s%%", resultmap["y"], color.GreenString("%f", d.UsagePercY)))
|
||||
// Find multiple features
|
||||
features := search(query, data)
|
||||
switch len(features) {
|
||||
case 0:
|
||||
fmt.Printf("%q not found", query)
|
||||
case 1:
|
||||
fmt.Println(formatFeat(data.Data[features[0]], data, false))
|
||||
default:
|
||||
for _, feat := range features {
|
||||
fmt.Println(formatFeat(data.Data[feat], data, true))
|
||||
}
|
||||
if d.UsagePercA > 0 {
|
||||
p = append(p, fmt.Sprintf("%s %s%%", resultmap["a"], color.YellowString("%f", d.UsagePercA)))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var clumpRe = regexp.MustCompile(`\W*`)
|
||||
|
||||
func search(query string, data Data) []string {
|
||||
var results []string
|
||||
query = strings.ToLower(query)
|
||||
for key, dat := range data.Data {
|
||||
matcher := clumpRe.ReplaceAllString(strings.ToLower(key+dat.Title+dat.Description+dat.Keywords+strings.Join(dat.Categories, "")), "")
|
||||
if strings.Contains(matcher, query) {
|
||||
results = append(results, key)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
var replaceNoteRe = regexp.MustCompile(`[\r\n]+`)
|
||||
|
||||
func formatFeat(feat data, d Data, short bool) string {
|
||||
bold := color.New(color.Bold)
|
||||
|
||||
var out strings.Builder
|
||||
out.WriteString(bold.Sprint(feat.Title))
|
||||
|
||||
var p []string
|
||||
if feat.UsagePercY > 0 {
|
||||
p = append(p, fmt.Sprintf("%s %s%%", resultmap["y"], color.GreenString("%f", feat.UsagePercY)))
|
||||
}
|
||||
if feat.UsagePercA > 0 {
|
||||
p = append(p, fmt.Sprintf("%s %s%%", resultmap["a"], color.YellowString("%f", feat.UsagePercA)))
|
||||
}
|
||||
percentages := strings.Join(p, " ")
|
||||
out.WriteString(" " + percentages)
|
||||
|
||||
status := fmt.Sprintf(" [%s]\n", d.Statuses[feat.Status])
|
||||
out.WriteString(status)
|
||||
|
||||
if !short {
|
||||
out.WriteString("\t" + strings.TrimSpace(feat.Description) + "\n\n")
|
||||
}
|
||||
|
||||
if short {
|
||||
out.WriteString("\t")
|
||||
}
|
||||
|
||||
needNote := make(map[int]struct{})
|
||||
for browser, stats := range feat.Stats {
|
||||
if d.Agents[browser].Type != "desktop" {
|
||||
continue
|
||||
}
|
||||
if !short {
|
||||
out.WriteString("\t")
|
||||
}
|
||||
out.WriteString(d.Agents[browser].Browser + " ")
|
||||
|
||||
results := makeResults(d.Agents[browser], stats)
|
||||
if len(results) == 1 {
|
||||
results[0].version = ""
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
out.WriteString(makeResult(result, needNote))
|
||||
}
|
||||
if !short {
|
||||
out.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if !short {
|
||||
out.WriteString("\n")
|
||||
}
|
||||
|
||||
if !short {
|
||||
for num, note := range feat.NotesByNum {
|
||||
n, _ := strconv.Atoi(num)
|
||||
if _, ok := needNote[n]; !ok {
|
||||
continue
|
||||
}
|
||||
out.WriteString(fmt.Sprintf("\t\t%s%s", color.YellowString(string(supernums[n])), note))
|
||||
}
|
||||
if feat.Notes != "" {
|
||||
out.WriteString(fmt.Sprintf("\t %s %s", resultmap["i"], replaceNoteRe.ReplaceAllString(feat.Notes, " ")))
|
||||
}
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
var supportRe = regexp.MustCompile(`#(\d+)`)
|
||||
|
||||
func makeResult(stat browserStat, nums map[int]struct{}) string {
|
||||
support := string(stat.support[0])
|
||||
var out strings.Builder
|
||||
bace := "\u2800"
|
||||
if s, ok := resultmap[support]; ok {
|
||||
out.WriteString(s)
|
||||
} else {
|
||||
out.WriteString(support)
|
||||
}
|
||||
out.WriteString(bace)
|
||||
if stat.version != "" {
|
||||
out.WriteString(stat.version)
|
||||
}
|
||||
if strings.Contains(string(stat.support), "x") {
|
||||
out.WriteString("ᵖ")
|
||||
}
|
||||
match := supportRe.FindStringSubmatch(string(stat.support))
|
||||
if match != nil {
|
||||
num, _ := strconv.Atoi(match[1])
|
||||
nums[num] = struct{}{}
|
||||
out.WriteString(string(supernums[num]))
|
||||
}
|
||||
if stat.usage > 0 {
|
||||
str := out.String()
|
||||
if string(str[len(str)-1]) != bace {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
out.WriteString(fmt.Sprintf("(%f) ", math.Round(stat.usage*1)/1))
|
||||
}
|
||||
out.WriteString(" ")
|
||||
|
||||
str := out.String()
|
||||
switch support {
|
||||
case "y":
|
||||
return color.GreenString(str)
|
||||
case "n":
|
||||
return color.RedString(str)
|
||||
case "a":
|
||||
return color.YellowString(str)
|
||||
default:
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
func makeResults(browser browser, stats map[string]support) []browserStat {
|
||||
var results []browserStat
|
||||
var current browserStat
|
||||
for idx, version := range browser.Versions {
|
||||
if version == "" {
|
||||
continue
|
||||
}
|
||||
support := stats[version]
|
||||
|
||||
var usage float64
|
||||
if u, ok := browser.UsageGlobal[version]; ok {
|
||||
usage = u
|
||||
}
|
||||
|
||||
ver := version
|
||||
if len(browser.Versions) > idx {
|
||||
ver += "+"
|
||||
}
|
||||
|
||||
if support[0] == 'p' {
|
||||
support = "n" + support[1:]
|
||||
}
|
||||
|
||||
if current.version == "" || current.support != support {
|
||||
current = browserStat{
|
||||
version: ver,
|
||||
support: support,
|
||||
usage: 0,
|
||||
}
|
||||
results = append(results, current)
|
||||
}
|
||||
|
||||
current.usage += usage
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func loadData() (Data, error) {
|
||||
|
|
32
struct.go
32
struct.go
|
@ -2,7 +2,13 @@ package main
|
|||
|
||||
type Data struct {
|
||||
Eras map[string]string `json:"eras"`
|
||||
Agents map[string]struct {
|
||||
Agents map[string]browser `json:"agents"`
|
||||
Statuses map[string]string `json:"statuses"`
|
||||
Cats map[string][]string `json:"cats"`
|
||||
Data map[string]data `json:"data"`
|
||||
}
|
||||
|
||||
type browser struct {
|
||||
Browser string `json:"browser"`
|
||||
LongName string `json:"long_name"`
|
||||
Abbr string `json:"abbr"`
|
||||
|
@ -10,23 +16,11 @@ type Data struct {
|
|||
Type string `json:"type"`
|
||||
UsageGlobal map[string]float64 `json:"usage_global"`
|
||||
Versions []string `json:"versions"`
|
||||
} `json:"agents"`
|
||||
Statuses struct {
|
||||
Rec string `json:"rec"`
|
||||
Pr string `json:"pr"`
|
||||
Cr string `json:"cr"`
|
||||
Wd string `json:"wd"`
|
||||
Ls string `json:"ls"`
|
||||
Other string `json:"other"`
|
||||
Unoff string `json:"unoff"`
|
||||
} `json:"statuses"`
|
||||
Cats map[string][]string `json:"cats"`
|
||||
Data map[string]data `json:"data"`
|
||||
}
|
||||
|
||||
type stat string
|
||||
type support string
|
||||
|
||||
func (s stat) Bool() bool {
|
||||
func (s support) Bool() bool {
|
||||
return s == "y"
|
||||
}
|
||||
|
||||
|
@ -40,7 +34,7 @@ type data struct {
|
|||
Title string `json:"title"`
|
||||
} `json:"links"`
|
||||
Categories []string `json:"categories"`
|
||||
Stats map[string]map[string]stat `json:"stats"`
|
||||
Stats map[string]map[string]support `json:"stats"`
|
||||
Notes string `json:"notes"`
|
||||
NotesByNum map[string]string `json:"notes_by_num"`
|
||||
UsagePercY float64 `json:"usage_perc_y"`
|
||||
|
@ -53,3 +47,9 @@ type data struct {
|
|||
FirefoxID string `json:"firefox_id"`
|
||||
WebkitID string `json:"webkit_id"`
|
||||
}
|
||||
|
||||
type browserStat struct {
|
||||
version string
|
||||
support support
|
||||
usage float64
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue