commit a0b8f9aa42c62d6afbaa20c92e488fbedf68bb56 Author: jolheiser Date: Wed Oct 5 23:15:56 2022 -0500 initial commit Signed-off-by: jolheiser diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bedac8c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Binaries +/caniuse +/caniuse.exe diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ec2045e --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 John Olheiser + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..484cb7a --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# caniuse + +CLI for [caniuse](https://caniuse.com) + +Retrieves data from the [repository](https://github.com/Fyrd/caniuse) + +## License + +[MIT](LICENSE) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f887c6e --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module go.jolheiser.com/caniuse + +go 1.19 + +require github.com/peterbourgon/ff/v3 v3.3.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bdc8b56 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/peterbourgon/ff/v3 v3.3.0 h1:PaKe7GW8orVFh8Unb5jNHS+JZBwWUMa2se0HM6/BI24= +github.com/peterbourgon/ff/v3 v3.3.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= diff --git a/io.go b/io.go new file mode 100644 index 0000000..119e130 --- /dev/null +++ b/io.go @@ -0,0 +1,45 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" +) + +const downloadURL = "https://raw.githubusercontent.com/Fyrd/caniuse/main/data.json" + +// Download downloads caniuse data to path +func Download(path string) error { + res, err := http.Get(downloadURL) + if err != nil { + return fmt.Errorf("could not get data: %v", err) + } + defer res.Body.Close() + + if res.StatusCode != 200 { + return fmt.Errorf("could not download data: %s", res.Status) + } + + fi, err := os.Create(path) + if err != nil { + return fmt.Errorf("could not create %q: %v", path, err) + } + defer fi.Close() + + _, err = io.Copy(fi, res.Body) + return err +} + +func Load(path string) (Data, error) { + var data Data + + fi, err := os.Open(path) + if err != nil { + return data, fmt.Errorf("could not read data: %v", err) + } + defer fi.Close() + + return data, json.NewDecoder(fi).Decode(&data) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..c35bd90 --- /dev/null +++ b/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "io/fs" + "os" + "path/filepath" + "time" + + "github.com/peterbourgon/ff/v3" +) + +var ( + resultmap = map[string]string{ + "y": "✔", + "n": "✘", + "a": "◒", + "u": "‽", + "i": "ⓘ", + "w": "⚠", + } + supernums = "⁰¹²³⁴⁵⁶⁷⁸⁹" + refresh = time.Hour * 24 * 30 +) + +func main() { + fs := flag.NewFlagSet("caniuse", flag.ContinueOnError) + + if err := ff.Parse(fs, os.Args[1:], + ff.WithEnvVarPrefix("CANIUSE"), + ); err != nil { + panic(err) + } + + if fs.NArg() != 1 { + fmt.Println("caniuse requires one argument") + return + } + + data, err := loadData() + if err != nil { + fmt.Printf("could not load data: %v", err) + } + + fmt.Println(data) +} + +func loadData() (Data, error) { + var data Data + + cache, err := os.UserCacheDir() + if err != nil { + return data, fmt.Errorf("could not determine cache dir: %v", err) + } + + caniuseCache := filepath.Join(cache, "caniuse") + if err := os.MkdirAll(caniuseCache, os.ModePerm); err != nil { + return data, fmt.Errorf("could not create dir %q: %v", caniuseCache, err) + } + + dataPath := filepath.Join(caniuseCache, "data.json") + fi, err := os.Stat(dataPath) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + return data, fmt.Errorf("could not stat %q: %v", dataPath, err) + } else if errors.Is(err, fs.ErrNotExist) || time.Since(fi.ModTime()) > refresh { + if err := Download(dataPath); err != nil { + return data, fmt.Errorf("could not download data: %v", err) + } + } + + return Load(dataPath) +} diff --git a/struct.go b/struct.go new file mode 100644 index 0000000..b409bad --- /dev/null +++ b/struct.go @@ -0,0 +1,53 @@ +package main + +type Data struct { + Eras map[string]string `json:"eras"` + Agents map[string]struct { + Browser string `json:"browser"` + LongName string `json:"long_name"` + Abbr string `json:"abbr"` + Prefix string `json:"prefix"` + 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]struct { + Title string `json:"title"` + Description string `json:"description"` + Spec string `json:"spec"` + Status string `json:"status"` + Links []struct { + URL string `json:"url"` + Title string `json:"title"` + } `json:"links"` + Categories []string `json:"categories"` + Stats map[string]map[string]stat `json:"stats"` + Notes string `json:"notes"` + NotesByNum map[string]string `json:"notes_by_num"` + UsagePercY float64 `json:"usage_perc_y"` + UsagePercA float64 `json:"usage_perc_a"` + UCPrefix bool `json:"ucprefix"` + Parent string `json:"parent"` + Keywords string `json:"keywords"` + IEID string `json:"ie_id"` + ChromeID string `json:"chrome_id"` + FirefoxID string `json:"firefox_id"` + WebkitID string `json:"webkit_id"` + } `json:"data"` +} + +type stat string + +func (s stat) Bool() bool { + return s == "y" +}