Skip to content

Instantly share code, notes, and snippets.

@spagettikod
Created November 17, 2020 22:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spagettikod/f8e196c5f4e3d10fd5b346ccab186622 to your computer and use it in GitHub Desktop.
Save spagettikod/f8e196c5f4e3d10fd5b346ccab186622 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
type Extension struct {
UniqueID string
Version string
}
var (
errLog = log.New(os.Stderr, "", 0)
infoLog *log.Logger
path string
dest string
verbose bool
)
func printUsage(msg string) {
if msg != "" {
fmt.Println(msg)
}
fmt.Println(`
Usage: dlvsix [OPTIONS] PATH DEST
Download Visual Studio Extensions.
Options:
-v Be more talkative`)
}
func (e Extension) Filename() string {
return fmt.Sprintf("%s.vsix", e.String())
}
func (e Extension) String() string {
return fmt.Sprintf("%s-%s", e.UniqueID, e.Version)
}
func init() {
flag.BoolVar(&verbose, "v", false, "Be more talkative")
}
func isPlainText(data []byte) bool {
mime := http.DetectContentType(data)
return strings.Index(mime, "text/plain") == 0
}
func isDir(p string) (bool, error) {
info, err := os.Stat(p)
if err != nil {
return false, err
}
return info.IsDir(), nil
}
func parse(data []byte, destFiles map[string]bool) (extensions []Extension, err error) {
buf := bytes.NewBuffer(data)
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.Index(line, "#") == 0 || len(line) == 0 {
continue
}
splitLine := strings.Split(line, " ")
ext := Extension{}
if len(splitLine) > 0 {
ext.UniqueID = splitLine[0]
if len(splitLine) == 2 {
ext.Version = splitLine[1]
}
if destFiles[ext.Filename()] {
infoLog.Printf(" extension '%s' already exist at destination, skipping", ext.String())
} else {
extensions = append(extensions, ext)
infoLog.Printf(" extension '%s' has not been downloaded", ext.String())
}
}
}
return extensions, scanner.Err()
}
func findExtensions(p string, destFiles map[string]bool) (extensions []Extension, err error) {
dir, err := isDir(p)
if err != nil {
return
}
if dir {
infoLog.Printf("found directory '%s'\n", p)
fis, err := ioutil.ReadDir(p)
if err != nil {
return extensions, err
}
for _, fi := range fis {
if !fi.IsDir() {
return findExtensions(fmt.Sprintf("%s%s%s", p, string(os.PathSeparator), fi.Name()), destFiles)
}
}
} else {
infoLog.Printf("found file '%s'\n", p)
data, err := ioutil.ReadFile(p)
if err != nil {
return extensions, err
}
if isPlainText(data) {
exts, err := parse(data, destFiles)
if err != nil {
return extensions, err
}
extensions = append(extensions, exts...)
}
}
return
}
func listDestFiles(dest string) (files map[string]bool, err error) {
if _, e := os.Stat(dest); os.IsNotExist(e) {
infoLog.Printf("destination directory '%s' not found, creating", dest)
os.Mkdir(dest, os.ModeDir|os.ModePerm)
}
fis, err := ioutil.ReadDir(dest)
if err != nil {
return
}
files = make(map[string]bool)
for _, fi := range fis {
files[fi.Name()] = true
}
return
}
func main() {
flag.Parse()
if verbose {
infoLog = log.New(os.Stdout, "", 0)
path = flag.Arg(0)
dest = flag.Arg(1)
log.New(os.Stdout, "", 0)
} else {
infoLog = log.New(ioutil.Discard, "", 0)
path = flag.Arg(0)
dest = flag.Arg(1)
}
if path == "" {
printUsage("missing path")
os.Exit(1)
}
if dest == "" {
printUsage("missing destination path")
os.Exit(1)
}
infoLog.Printf("searching path '%s' for extensions\n", path)
destFiles, err := listDestFiles(dest)
if err != nil {
errLog.Fatal(err)
}
extensions, err := findExtensions(path, destFiles)
if err != nil {
errLog.Fatal(err)
}
if len(extensions) == 0 {
errLog.Fatalf("no extensions found at path '%s'", path)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment