Skip to content

Commit

Permalink
feat: make requests cancelable
Browse files Browse the repository at this point in the history
  • Loading branch information
wilriker committed Oct 9, 2023
1 parent 300c7cf commit d7c1ac7
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 155 deletions.
21 changes: 13 additions & 8 deletions cmd/rfm/main.go
@@ -1,9 +1,12 @@
package main

import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"

"github.com/wilriker/rfm/commands"
)
Expand All @@ -12,24 +15,26 @@ func main() {
if len(os.Args) < 2 {
commands.PrintHelp(commands.NoParameters, 1)
}
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGABRT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
defer stop()
var err error
switch os.Args[1] {
case "backup":
err = commands.DoBackup(os.Args[2:])
err = commands.DoBackup(ctx, os.Args[2:])
case "upload":
err = commands.DoUpload(os.Args[2:])
err = commands.DoUpload(ctx, os.Args[2:])
case "mkdir":
err = commands.DoMkdir(os.Args[2:])
err = commands.DoMkdir(ctx, os.Args[2:])
case "mv":
err = commands.DoMv(os.Args[2:])
err = commands.DoMv(ctx, os.Args[2:])
case "rm":
err = commands.DoRm(os.Args[2:])
err = commands.DoRm(ctx, os.Args[2:])
case "download":
err = commands.DoDownload(os.Args[2:])
err = commands.DoDownload(ctx, os.Args[2:])
case "fileinfo":
err = commands.DoFileinfo(os.Args[2:])
err = commands.DoFileinfo(ctx, os.Args[2:])
case "ls":
err = commands.DoLs(os.Args[2:])
err = commands.DoLs(ctx, os.Args[2:])
case "help":
if len(os.Args) > 2 {
commands.PrintHelp(os.Args[2:], 0)
Expand Down
56 changes: 26 additions & 30 deletions commands/backup.go
@@ -1,13 +1,14 @@
package commands

import (
"context"
"fmt"
"io/ioutil"
"io/fs"
"log"
"os"
"path/filepath"

"github.com/wilriker/librfm"
"github.com/wilriker/librfm/v2"
"github.com/wilriker/rfm"
)

Expand Down Expand Up @@ -44,7 +45,7 @@ func (b *BackupOptions) Check() {
}

// InitBackupOptions intializes a backupOptions instance from command line parameters
func InitBackupOptions(arguments []string) *BackupOptions {
func InitBackupOptions(ctx context.Context, arguments []string) *BackupOptions {
b := BackupOptions{BaseOptions: &BaseOptions{}}

fs := b.GetFlagSet()
Expand All @@ -65,23 +66,15 @@ func InitBackupOptions(arguments []string) *BackupOptions {

b.Check()

b.Connect()
b.Connect(ctx)

return &b
}

// DoBackup is a convenience function to run a backup from command line parameters
func DoBackup(arguments []string) error {
bo := InitBackupOptions(arguments)
return NewBackup(bo).Backup(bo.dirToBackup, bo.outDir, bo.excls, bo.removeLocal)
}

// Backup provides a single method to run backups
type Backup interface {
// Backup will synchronize the contents of a remote folder to a local directory.
// The boolean flag removeLocal decides whether or not files that have been remove
// remote should also be deleted locally
Backup(remoteFolder, outDir string, excls rfm.Excludes, removeLocal bool) error
func DoBackup(ctx context.Context, arguments []string) error {
bo := InitBackupOptions(ctx, arguments)
return NewBackup(bo).Backup(ctx, bo.dirToBackup, bo.outDir, bo.excls, bo.removeLocal)
}

// backup implementes the Backup interface
Expand All @@ -90,7 +83,7 @@ type backup struct {
}

// NewBackup creates a new instance of the Backup interface
func NewBackup(bo *BackupOptions) Backup {
func NewBackup(bo *BackupOptions) *backup {
return &backup{
o: bo,
}
Expand Down Expand Up @@ -130,7 +123,7 @@ func (b *backup) ensureOutDirExists(outDir string) error {
return nil
}

func (b *backup) updateLocalFiles(fl *librfm.Filelist, outDir string, excls rfm.Excludes, removeLocal bool) error {
func (b *backup) updateLocalFiles(ctx context.Context, fl *librfm.Filelist, outDir string, excls rfm.Excludes, removeLocal bool) error {

if err := b.ensureOutDirExists(outDir); err != nil {
return err
Expand Down Expand Up @@ -160,7 +153,7 @@ func (b *backup) updateLocalFiles(fl *librfm.Filelist, outDir string, excls rfm.
if fi == nil || fi.ModTime().Before(file.Date()) {

// Download file
body, duration, err := b.o.Rfm.Download(remoteFilename)
body, duration, err := b.o.Rfm.Download(ctx, remoteFilename)
if err != nil {
return err
}
Expand Down Expand Up @@ -205,7 +198,7 @@ func (b *backup) updateLocalFiles(fl *librfm.Filelist, outDir string, excls rfm.
// isManagedDirectory checks wether the given path is a directory and
// if so if it contains the marker file. It will return false in case
// any error has occured.
func (b *backup) isManagedDirectory(basePath string, f os.FileInfo) bool {
func (b *backup) isManagedDirectory(basePath string, f fs.DirEntry) bool {
if !f.IsDir() {
return false
}
Expand All @@ -228,35 +221,38 @@ func (b *backup) removeDeletedFiles(fl *librfm.Filelist, outDir string) error {
existingFiles[f.Name] = true
}

files, err := ioutil.ReadDir(outDir)
dirEntries, err := os.ReadDir(outDir)
if err != nil {
return err
}

for _, f := range files {
if !existingFiles[f.Name()] {
for _, de := range dirEntries {
if !existingFiles[de.Name()] {

// Skip directories not managed by us as well as our marker file
if (f.IsDir() && !b.isManagedDirectory(outDir, f)) || f.Name() == managedDirMarker {
if (de.IsDir() && !b.isManagedDirectory(outDir, de)) || de.Name() == managedDirMarker {
continue
}
if err := os.RemoveAll(filepath.Join(outDir, f.Name())); err != nil {
if err := os.RemoveAll(filepath.Join(outDir, de.Name())); err != nil {
return err
}
if b.o.verbose {
marker := fileMarker
if f.IsDir() {
if de.IsDir() {
marker = dirMarker
}
log.Println(" Removed: ", marker, f.Name())
log.Println(" Removed: ", marker, de.Name())
}
}
}

return nil
}

func (b *backup) Backup(folder, outDir string, excls rfm.Excludes, removeLocal bool) error {
// Backup will synchronize the contents of a remote folder to a local directory.
// The boolean flag removeLocal decides whether or not files that have been remove
// remote should also be deleted locally
func (b *backup) Backup(ctx context.Context, folder, outDir string, excls rfm.Excludes, removeLocal bool) error {

// Skip complete directories if they are covered by an exclude pattern
if excls.Contains(folder) {
Expand All @@ -265,13 +261,13 @@ func (b *backup) Backup(folder, outDir string, excls rfm.Excludes, removeLocal b
}

log.Println("Fetching filelist for", folder)
fl, err := b.o.Rfm.Filelist(folder, false)
fl, err := b.o.Rfm.Filelist(ctx, folder, false)
if err != nil {
return err
}

log.Println("Downloading new/changed files from", folder, "to", outDir)
if err = b.updateLocalFiles(fl, outDir, excls, removeLocal); err != nil {
if err = b.updateLocalFiles(ctx, fl, outDir, excls, removeLocal); err != nil {
return err
}

Expand All @@ -289,7 +285,7 @@ func (b *backup) Backup(folder, outDir string, excls rfm.Excludes, removeLocal b
}
remoteFilename := fmt.Sprintf("%s/%s", fl.Dir, file.Name)
fileName := filepath.Join(outDir, file.Name)
if err = b.Backup(remoteFilename, fileName, excls, removeLocal); err != nil {
if err = b.Backup(ctx, remoteFilename, fileName, excls, removeLocal); err != nil {
return err
}
}
Expand Down
9 changes: 5 additions & 4 deletions commands/baseoptions.go
@@ -1,12 +1,13 @@
package commands

import (
"context"
"flag"
"log"
"os"
"sync"

"github.com/wilriker/librfm"
"github.com/wilriker/librfm/v2"
"github.com/wilriker/rfm"
)

Expand All @@ -21,7 +22,7 @@ type BaseOptions struct {
optionsSeen map[string]bool
fs *flag.FlagSet
once sync.Once
Rfm librfm.RRFFileManager
Rfm *librfm.RRFFileManager
}

// GetFlagSet returns the basic flag.FlagSet shared by all commands
Expand Down Expand Up @@ -92,9 +93,9 @@ func (b *BaseOptions) Check() {
}

// Connect initializes the connection to RepRapFirmware
func (b *BaseOptions) Connect() {
func (b *BaseOptions) Connect(ctx context.Context) {
b.Rfm = librfm.New(b.domain, b.port, b.debug)
if err := b.Rfm.Connect(b.password); err != nil {
if err := b.Rfm.Connect(ctx, b.password); err != nil {
log.Println("Duet currently not available")
os.Exit(0)
}
Expand Down
22 changes: 9 additions & 13 deletions commands/download.go
@@ -1,6 +1,7 @@
package commands

import (
"context"
"log"
"os"

Expand Down Expand Up @@ -34,7 +35,7 @@ func (d *DownloadOptions) Check() {
}

// InitDownloadOptions initializes a DownloadOptions instance from command-line parameters
func InitDownloadOptions(arguments []string) *DownloadOptions {
func InitDownloadOptions(ctx context.Context, arguments []string) *DownloadOptions {
d := DownloadOptions{BaseOptions: &BaseOptions{}}

fs := d.GetFlagSet()
Expand All @@ -50,20 +51,15 @@ func InitDownloadOptions(arguments []string) *DownloadOptions {

d.Check()

d.Connect()
d.Connect(ctx)

return &d
}

// DoDownload is a convenience method to run a download form command-line parameters
func DoDownload(arguments []string) error {
do := InitDownloadOptions(arguments)
return NewDownload(do).Download(do.remotePath, do.localName)
}

// Download provides a single method to run a download
type Download interface {
Download(remotePath, localName string) error
func DoDownload(ctx context.Context, arguments []string) error {
do := InitDownloadOptions(ctx, arguments)
return NewDownload(do).Download(ctx, do.remotePath, do.localName)
}

// download implements the Download interface
Expand All @@ -72,15 +68,15 @@ type download struct {
}

// NewDownload creates a new instance of the Download interface
func NewDownload(do *DownloadOptions) Download {
func NewDownload(do *DownloadOptions) *download {
return &download{
o: do,
}
}

// Download downloads a remote file to a local path
func (d *download) Download(remotePath, localName string) error {
content, duration, err := d.o.Rfm.Download(remotePath)
func (d *download) Download(ctx context.Context, remotePath, localName string) error {
content, duration, err := d.o.Rfm.Download(ctx, remotePath)
if err != nil {
return err
}
Expand Down
24 changes: 10 additions & 14 deletions commands/fileinfo.go
@@ -1,12 +1,13 @@
package commands

import (
"context"
"fmt"
"log"
"strings"
"time"

"github.com/wilriker/librfm"
"github.com/wilriker/librfm/v2"
"github.com/wilriker/rfm"
)

Expand All @@ -28,7 +29,7 @@ func (f *FileinfoOptions) Check() {
}

// InitFileinfoOptions inializes a FileinfoOptions instance from command-line parameters
func InitFileinfoOptions(arguments []string) *FileinfoOptions {
func InitFileinfoOptions(ctx context.Context, arguments []string) *FileinfoOptions {
f := FileinfoOptions{BaseOptions: &BaseOptions{}}

fs := f.GetFlagSet()
Expand All @@ -41,20 +42,15 @@ func InitFileinfoOptions(arguments []string) *FileinfoOptions {

f.Check()

f.Connect()
f.Connect(ctx)

return &f
}

// DoFileinfo is a convenience function to run a download from command-line parameters
func DoFileinfo(arguments []string) error {
fo := InitFileinfoOptions(arguments)
return NewFileinfo(fo).Fileinfo(fo.path)
}

// Fileinfo provides a singl method to fetch infos on a file
type Fileinfo interface {
Fileinfo(path string) error
func DoFileinfo(ctx context.Context, arguments []string) error {
fo := InitFileinfoOptions(ctx, arguments)
return NewFileinfo(fo).Fileinfo(ctx, fo.path)
}

// fileinfo implement the Fileinfo interface
Expand All @@ -63,15 +59,15 @@ type fileinfo struct {
}

// NewFileinfo creates a new instance of the Fileinfo interface
func NewFileinfo(fo *FileinfoOptions) Fileinfo {
func NewFileinfo(fo *FileinfoOptions) *fileinfo {
return &fileinfo{
o: fo,
}
}

// Fileinfo fetches information on a file and prints it to sdtout
func (f *fileinfo) Fileinfo(path string) error {
fi, err := f.o.Rfm.Fileinfo(path)
func (f *fileinfo) Fileinfo(ctx context.Context, path string) error {
fi, err := f.o.Rfm.Fileinfo(ctx, path)
if err != nil {
return err
}
Expand Down

0 comments on commit d7c1ac7

Please sign in to comment.