Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / cmd / go-module-query / main.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.0.1-2020.1.5/cmd/go-module-query/main.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.0.1-2020.1.5/cmd/go-module-query/main.go
new file mode 100644 (file)
index 0000000..aa59ba0
--- /dev/null
@@ -0,0 +1,189 @@
+package main
+
+import (
+       "encoding/json"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "log"
+       "net/http"
+       "os"
+       "path"
+       "path/filepath"
+       "strings"
+       "sync"
+       "sync/atomic"
+       "time"
+
+       "github.com/google/renameio"
+       "github.com/rogpeppe/go-internal/modfile"
+       "golang.org/x/mod/module"
+)
+
+/*
+Q: which versions of our module are being used
+A: find the latest version of every Go module, find the dependency on our module
+
+Q: what modules have stopped using our module
+A: find every module where a version [0..N) uses us, but version N doesn't.
+*/
+
+func Fetch(since time.Time) ([]module.Version, time.Time, error) {
+       var out []module.Version
+       for {
+               out2, since2, err := fetch(since, out)
+               if err != nil {
+                       return nil, since, err
+               }
+               if len(out) == len(out2) {
+                       break
+               }
+               out = out2
+               since = since2
+       }
+       return out, since, nil
+}
+
+func fetch(since time.Time, out []module.Version) ([]module.Version, time.Time, error) {
+       // +1µs because of bug in index.golang.org that returns results
+       // >=since instead of >since
+       ts := since.Add(1 * time.Microsecond)
+       u := `https://index.golang.org/index?since=` + ts.Format(time.RFC3339Nano)
+       resp, err := http.Get(u)
+       if err != nil {
+               return nil, since, err
+       }
+       defer resp.Body.Close()
+       dec := json.NewDecoder(resp.Body)
+
+       var entry struct {
+               module.Version
+               Timestamp time.Time
+       }
+       for {
+               if err := dec.Decode(&entry); err != nil {
+                       if err == io.EOF {
+                               break
+                       }
+                       return out, since, err
+               }
+
+               out = append(out, entry.Version)
+               since = entry.Timestamp
+       }
+
+       return out, since, nil
+}
+
+func main() {
+       cache, err := os.UserCacheDir()
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       var since time.Time
+       b, err := ioutil.ReadFile(filepath.Join(cache, "go-module-query", "last"))
+       if err == nil {
+               t, err := time.Parse(time.RFC3339Nano, string(b))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               since = t
+               log.Println("Resuming at", since)
+       } else if !os.IsNotExist(err) {
+               log.Fatal(err)
+       }
+
+       out, since, err := Fetch(since)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       sem := make(chan struct{}, 8)
+       var wg sync.WaitGroup
+       var errs uint64
+       for _, v := range out {
+               mpath, _ := module.EscapePath(v.Path)
+               p := filepath.Join(cache, "go-module-query", mpath, "@v", v.Version+".mod")
+               // XXX is this atomic?
+               if err := os.MkdirAll(filepath.Join(cache, "go-module-query", mpath, "@v"), 0777); err != nil {
+                       log.Println(err)
+                       continue
+               }
+               if _, err := os.Stat(p); os.IsNotExist(err) {
+                       fmt.Println("Fetching", v)
+                       sem <- struct{}{}
+                       wg.Add(1)
+                       go func(p string, v module.Version) {
+                               defer wg.Done()
+                               defer func() { <-sem }()
+                               resp, err := http.Get("https://proxy.golang.org/" + path.Join(mpath, "@v", v.Version+".mod"))
+                               if err != nil {
+                                       atomic.AddUint64(&errs, 1)
+                                       log.Println(err)
+                                       return
+                               }
+                               defer resp.Body.Close()
+                               // XXX handle response code
+                               pf, err := renameio.TempFile("", p)
+                               if err != nil {
+                                       atomic.AddUint64(&errs, 1)
+                                       log.Println(err)
+                                       return
+                               }
+                               defer pf.Cleanup()
+                               if _, err := io.Copy(pf, resp.Body); err != nil {
+                                       atomic.AddUint64(&errs, 1)
+                                       log.Println(err)
+                                       return
+                               }
+                               if err := pf.CloseAtomicallyReplace(); err != nil {
+                                       atomic.AddUint64(&errs, 1)
+                                       log.Println("Couldn't store go.mod:", err)
+                               }
+                       }(p, v)
+               }
+       }
+
+       wg.Wait()
+
+       if errs > 0 {
+               log.Println("Couldn't download all go.mod, not storing timestamp")
+               return
+       }
+
+       if err := renameio.WriteFile(filepath.Join(cache, "go-module-query", "last"), []byte(since.Format(time.RFC3339Nano)), 0666); err != nil {
+               log.Println("Couldn't store timestamp:", err)
+       }
+}
+
+func printGraph() {
+       cache, err := os.UserCacheDir()
+       if err != nil {
+               log.Fatal(err)
+       }
+       filepath.Walk(filepath.Join(cache, "go-module-query"), func(path string, info os.FileInfo, err error) error {
+               if err != nil {
+                       return nil
+               }
+               if strings.HasSuffix(path, ".mod") {
+                       name := filepath.Base(path)
+                       name = name[:len(name)-4]
+                       b, err := ioutil.ReadFile(path)
+                       if err != nil {
+                               log.Println(err)
+                               return nil
+                       }
+                       f, err := modfile.Parse(path, b, nil)
+                       if err != nil {
+                               log.Println(err)
+                               return nil
+                       }
+                       f.Module.Mod.Version = name
+                       for _, dep := range f.Require {
+                               fmt.Printf("%s@%s %s@%s\n", f.Module.Mod.Path, f.Module.Mod.Version, dep.Mod.Path, dep.Mod.Version)
+                       }
+               }
+               return nil
+       })
+}