github.com/anishathalye/periscope@v0.3.5/internal/periscope/periscope.go (about) 1 // Copyright (c) Anish Athalye. Released under GPLv3. 2 3 package periscope 4 5 import ( 6 "github.com/anishathalye/periscope/internal/db" 7 "github.com/anishathalye/periscope/internal/herror" 8 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "time" 15 16 "github.com/cheggaaa/pb/v3" 17 "github.com/spf13/afero" 18 "golang.org/x/term" 19 ) 20 21 const dbDirectory = "periscope" 22 const dbName = "periscope.sqlite" 23 24 type Periscope struct { 25 fs afero.Fs 26 realFs bool 27 db *db.Session 28 dbPath string 29 outStream io.Writer 30 errStream io.Writer 31 options *Options 32 } 33 34 type Options struct { 35 Debug bool 36 } 37 38 func dbPath() (string, herror.Interface) { 39 cacheDirRoot, err := os.UserCacheDir() 40 if err != nil { 41 return "", herror.Unlikely(err, "unable to determine cache directory", ` 42 Ensure that $HOME or $XDG_CACHE_HOME is set. 43 `) 44 } 45 cacheDir := filepath.Join(cacheDirRoot, dbDirectory) 46 err = os.MkdirAll(cacheDir, 0o755) 47 if err != nil { 48 return "", herror.Unlikely(err, fmt.Sprintf("unable to create cache directory '%s'", cacheDir), fmt.Sprintf(` 49 Ensure that the user cache directory '%s' exists and is writable. 50 `, cacheDirRoot)) 51 } 52 dbPath := filepath.Join(cacheDir, dbName) 53 return dbPath, nil 54 } 55 56 func New(options *Options) (*Periscope, herror.Interface) { 57 dbPath, err := dbPath() 58 if err != nil { 59 return nil, err 60 } 61 db, err := db.New(dbPath, options.Debug) 62 if err != nil { 63 return nil, err 64 } 65 fs := afero.NewOsFs() 66 return &Periscope{ 67 fs: fs, 68 realFs: true, 69 db: db, 70 dbPath: dbPath, 71 outStream: os.Stdout, 72 errStream: os.Stderr, 73 options: options, 74 }, nil 75 } 76 77 func (ps *Periscope) progressBar(total int, template string) *pb.ProgressBar { 78 bar := pb.New(total) 79 bar.SetRefreshRate(25 * time.Millisecond) 80 bar.SetTemplateString(template) 81 bar.SetMaxWidth(99) 82 bar.Start() 83 if w, ok := ps.errStream.(*os.File); ok && term.IsTerminal(int(w.Fd())) { 84 bar.SetWriter(ps.errStream) 85 } else { 86 bar.SetWriter(ioutil.Discard) 87 } 88 return bar 89 }