diff --git a/cmd/common.go b/cmd/common.go
index 05059b4..cee8c16 100644
--- a/cmd/common.go
+++ b/cmd/common.go
@@ -18,9 +18,14 @@ package cmd
import (
"fmt"
+ "sync"
+ "time"
"github.com/spf13/cobra"
"github.com/spf13/viper"
+ "go.uploadedlobster.com/scotty/internal/backends"
+ "go.uploadedlobster.com/scotty/internal/models"
+ "go.uploadedlobster.com/scotty/internal/storage"
)
func getConfigFromFlag(cmd *cobra.Command, flagName string) (string, *viper.Viper) {
@@ -43,3 +48,114 @@ func getInt64FromFlag(cmd *cobra.Command, flagName string) (result int64) {
}
return
}
+
+type backendInfo[T models.Backend, R models.ListensResult | models.LovesResult] struct {
+ configName string
+ backend T
+}
+
+type exportBackendInfo[T models.Backend, R models.ListensResult | models.LovesResult] struct {
+ backendInfo[T, R]
+ processor backends.ExportProcessor[R]
+}
+
+type importBackendInfo[T models.Backend, R models.ListensResult | models.LovesResult] struct {
+ backendInfo[T, R]
+ processor backends.ImportProcessor[R]
+}
+
+func resolveBackends[E models.Backend, I models.ImportBackend, R models.ListensResult | models.LovesResult](cmd *cobra.Command) (*exportBackendInfo[E, R], *importBackendInfo[I, R], error) {
+ sourceName, sourceConfig := getConfigFromFlag(cmd, "from")
+ targetName, targetConfig := getConfigFromFlag(cmd, "to")
+ // Initialize backends
+ exportBackend, err := backends.ResolveBackend[E](sourceConfig)
+ if err != nil {
+ return nil, nil, err
+ }
+ importBackend, err := backends.ResolveBackend[I](targetConfig)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ exportInfo := exportBackendInfo[E, R]{
+ backendInfo: backendInfo[E, R]{
+ configName: sourceName,
+ backend: exportBackend,
+ },
+ }
+
+ importInfo := importBackendInfo[I, R]{
+ backendInfo: backendInfo[I, R]{
+ configName: targetName,
+ backend: importBackend,
+ },
+ }
+
+ return &exportInfo, &importInfo, nil
+}
+
+func cmdExportImport[E models.Backend, I models.ImportBackend, R models.ListensResult | models.LovesResult](cmd *cobra.Command, entity string, exp *exportBackendInfo[E, R], imp *importBackendInfo[I, R]) {
+ sourceName := exp.configName
+ targetName := imp.configName
+ fmt.Printf("Transferring %s from %s to %s...\n", entity, sourceName, targetName)
+
+ // Setup database
+ db, err := storage.New(viper.GetString("database"))
+ cobra.CheckErr(err)
+
+ // Authenticate backends, if needed
+ config := viper.GetViper()
+ _, err = backends.Authenticate(sourceName, exp.backend, db, config)
+ cobra.CheckErr(err)
+
+ _, err = backends.Authenticate(targetName, imp.backend, db, config)
+ cobra.CheckErr(err)
+
+ // Read timestamp
+ timestamp := time.Unix(getInt64FromFlag(cmd, "timestamp"), 0)
+ if timestamp == time.Unix(0, 0) {
+ timestamp, err = db.GetImportTimestamp(sourceName, targetName, entity)
+ cobra.CheckErr(err)
+ }
+ fmt.Printf("From timestamp: %v (%v)\n", timestamp, timestamp.Unix())
+
+ // Prepare progress bars
+ exportProgress := make(chan models.Progress)
+ importProgress := make(chan models.Progress)
+ var wg sync.WaitGroup
+ progress := progressBar(&wg, exportProgress, importProgress)
+
+ // Export from source
+ exportChan := make(chan R, 1000)
+ go exp.processor.Process(timestamp, exportChan, exportProgress)
+
+ // Import into target
+ resultChan := make(chan models.ImportResult)
+ go imp.processor.Process(exportChan, resultChan, importProgress)
+ result := <-resultChan
+ close(exportProgress)
+ wg.Wait()
+ progress.Wait()
+ if result.Error != nil {
+ fmt.Printf("Import failed, last reported timestamp was %v (%v)\n", result.LastTimestamp, result.LastTimestamp.Unix())
+ cobra.CheckErr(result.Error)
+ }
+ fmt.Printf("Imported %v of %v %s into %v.\n",
+ result.ImportCount, result.TotalCount, entity, targetName)
+
+ // Update timestamp
+ if result.LastTimestamp.Unix() < timestamp.Unix() {
+ result.LastTimestamp = timestamp
+ }
+ fmt.Printf("Latest timestamp: %v (%v)\n", result.LastTimestamp, result.LastTimestamp.Unix())
+ err = db.SetImportTimestamp(sourceName, targetName, entity, result.LastTimestamp)
+ cobra.CheckErr(err)
+
+ // Print errors
+ if len(result.ImportErrors) > 0 {
+ fmt.Printf("\nDuring the import the following errors occurred:\n")
+ for _, err := range result.ImportErrors {
+ fmt.Printf("Error: %v\n", err)
+ }
+ }
+}
diff --git a/cmd/listens.go b/cmd/listens.go
index 9093dbc..1d52dc5 100644
--- a/cmd/listens.go
+++ b/cmd/listens.go
@@ -17,15 +17,9 @@ Scotty. If not, see .
package cmd
import (
- "fmt"
- "sync"
- "time"
-
"github.com/spf13/cobra"
- "github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/backends"
"go.uploadedlobster.com/scotty/internal/models"
- "go.uploadedlobster.com/scotty/internal/storage"
)
// listensCmd represents the listens command
@@ -34,77 +28,15 @@ var listensCmd = &cobra.Command{
Short: "Transfer listens between two services",
Long: `Transfers listens between two configured services.`,
Run: func(cmd *cobra.Command, args []string) {
- sourceName, sourceConfig := getConfigFromFlag(cmd, "from")
- targetName, targetConfig := getConfigFromFlag(cmd, "to")
- fmt.Printf("Transferring listens from %s to %s...\n", sourceName, targetName)
-
- // Setup database
- db, err := storage.New(viper.GetString("database"))
+ exp, imp, err := resolveBackends[models.ListensExport, models.ListensImport, models.ListensResult](cmd)
cobra.CheckErr(err)
-
- // Initialize backends
- exportBackend, err := backends.ResolveBackend[models.ListensExport](sourceConfig)
- cobra.CheckErr(err)
- importBackend, err := backends.ResolveBackend[models.ListensImport](targetConfig)
- cobra.CheckErr(err)
-
- // Authenticate backends, if needed
- _, err = backends.Authenticate(sourceName, exportBackend, db, viper.GetViper())
- cobra.CheckErr(err)
-
- _, err = backends.Authenticate(targetName, importBackend, db, viper.GetViper())
- cobra.CheckErr(err)
-
- // Read timestamp
- timestamp := time.Unix(getInt64FromFlag(cmd, "timestamp"), 0)
- if timestamp == time.Unix(0, 0) {
- timestamp, err = db.GetImportTimestamp(sourceName, targetName, "listens")
- cobra.CheckErr(err)
+ exp.processor = backends.ListensExportProcessor{
+ Backend: exp.backend,
}
- fmt.Printf("From timestamp: %v (%v)\n", timestamp, timestamp.Unix())
-
- // Prepare progress bars
- exportProgress := make(chan models.Progress)
- importProgress := make(chan models.Progress)
- var wg sync.WaitGroup
- progress := progressBar(&wg, exportProgress, importProgress)
-
- // Export from source
- exportChan := make(chan models.ListensResult, 1000)
- go exportBackend.ExportListens(timestamp, exportChan, exportProgress)
-
- // Import into target
- resultChan := make(chan models.ImportResult)
- var processor = backends.ListensImportProcessor{
- Backend: importBackend,
- }
- go processor.Process(exportChan, resultChan, importProgress)
- result := <-resultChan
- close(exportProgress)
- wg.Wait()
- progress.Wait()
- if result.Error != nil {
- fmt.Printf("Import failed, last reported timestamp was %v (%v)\n", result.LastTimestamp, result.LastTimestamp.Unix())
- cobra.CheckErr(result.Error)
- }
- fmt.Printf("Imported %v of %v listens into %v.\n",
- result.ImportCount, result.TotalCount, targetName)
-
- // Update timestamp
- if result.LastTimestamp.Unix() < timestamp.Unix() {
- result.LastTimestamp = timestamp
- }
- fmt.Printf("Latest timestamp: %v (%v)\n", result.LastTimestamp, result.LastTimestamp.Unix())
- err = db.SetImportTimestamp(sourceName, targetName, "listens", result.LastTimestamp)
- cobra.CheckErr(err)
-
- // Print errors
- if len(result.ImportErrors) > 0 {
- fmt.Printf("\nDuring the import the following errors occurred:\n")
- for _, err := range result.ImportErrors {
- fmt.Printf("Error: %v\n", err)
- }
+ imp.processor = backends.ListensImportProcessor{
+ Backend: imp.backend,
}
+ cmdExportImport(cmd, "listens", exp, imp)
},
}
diff --git a/cmd/loves.go b/cmd/loves.go
index 623b477..ea257a0 100644
--- a/cmd/loves.go
+++ b/cmd/loves.go
@@ -17,15 +17,9 @@ Scotty. If not, see .
package cmd
import (
- "fmt"
- "sync"
- "time"
-
"github.com/spf13/cobra"
- "github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/backends"
"go.uploadedlobster.com/scotty/internal/models"
- "go.uploadedlobster.com/scotty/internal/storage"
)
// lovesCmd represents the loves command
@@ -34,77 +28,15 @@ var lovesCmd = &cobra.Command{
Short: "Transfer loves between two services",
Long: `Transfers loves between two configured services.`,
Run: func(cmd *cobra.Command, args []string) {
- sourceName, sourceConfig := getConfigFromFlag(cmd, "from")
- targetName, targetConfig := getConfigFromFlag(cmd, "to")
- fmt.Printf("Transferring loves from %s to %s...\n", sourceName, targetName)
-
- // Setup database
- db, err := storage.New(viper.GetString("database"))
+ exp, imp, err := resolveBackends[models.LovesExport, models.LovesImport, models.LovesResult](cmd)
cobra.CheckErr(err)
-
- // Initialize backends
- exportBackend, err := backends.ResolveBackend[models.LovesExport](sourceConfig)
- cobra.CheckErr(err)
- importBackend, err := backends.ResolveBackend[models.LovesImport](targetConfig)
- cobra.CheckErr(err)
-
- // Authenticate backends, if needed
- _, err = backends.Authenticate(sourceName, exportBackend, db, viper.GetViper())
- cobra.CheckErr(err)
-
- _, err = backends.Authenticate(targetName, importBackend, db, viper.GetViper())
- cobra.CheckErr(err)
-
- // Read timestamp
- timestamp := time.Unix(getInt64FromFlag(cmd, "timestamp"), 0)
- if timestamp == time.Unix(0, 0) {
- timestamp, err = db.GetImportTimestamp(sourceName, targetName, "loves")
- cobra.CheckErr(err)
+ exp.processor = backends.LovesExportProcessor{
+ Backend: exp.backend,
}
- fmt.Printf("From timestamp: %v (%v)\n", timestamp, timestamp.Unix())
-
- // Prepare progress bars
- exportProgress := make(chan models.Progress)
- importProgress := make(chan models.Progress)
- var wg sync.WaitGroup
- progress := progressBar(&wg, exportProgress, importProgress)
-
- // Export from source
- exportChan := make(chan models.LovesResult, 1000)
- go exportBackend.ExportLoves(timestamp, exportChan, exportProgress)
-
- // Import into target
- resultChan := make(chan models.ImportResult)
- var processor = backends.LovesImportProcessor{
- Backend: importBackend,
- }
- go processor.Process(exportChan, resultChan, importProgress)
- result := <-resultChan
- close(exportProgress)
- wg.Wait()
- progress.Wait()
- if result.Error != nil {
- fmt.Printf("Import failed, last reported timestamp was %v (%v)\n", result.LastTimestamp, result.LastTimestamp.Unix())
- cobra.CheckErr(result.Error)
- }
- fmt.Printf("Imported %v of %v loves into %v.\n",
- result.ImportCount, result.TotalCount, targetName)
-
- // Update timestamp
- if result.LastTimestamp.Unix() < timestamp.Unix() {
- result.LastTimestamp = timestamp
- }
- fmt.Printf("Latest timestamp: %v (%v)\n", result.LastTimestamp, result.LastTimestamp.Unix())
- err = db.SetImportTimestamp(sourceName, targetName, "loves", result.LastTimestamp)
- cobra.CheckErr(err)
-
- // Print errors
- if len(result.ImportErrors) > 0 {
- fmt.Printf("\nDuring the import the following errors occurred:\n")
- for _, err := range result.ImportErrors {
- fmt.Printf("Error: %v\n", err)
- }
+ imp.processor = backends.LovesImportProcessor{
+ Backend: imp.backend,
}
+ cmdExportImport(cmd, "loves", exp, imp)
},
}
diff --git a/internal/backends/export.go b/internal/backends/export.go
new file mode 100644
index 0000000..9bdb8a1
--- /dev/null
+++ b/internal/backends/export.go
@@ -0,0 +1,51 @@
+/*
+Copyright © 2023 Philipp Wolfer
+
+Scotty is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later version.
+
+Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+Scotty. If not, see .
+*/
+
+package backends
+
+import (
+ "time"
+
+ "go.uploadedlobster.com/scotty/internal/models"
+)
+
+type ExportProcessor[T models.ListensResult | models.LovesResult] interface {
+ ExportBackend() models.Backend
+ Process(oldestTimestamp time.Time, results chan T, progress chan models.Progress)
+}
+
+type ListensExportProcessor struct {
+ Backend models.ListensExport
+}
+
+func (p ListensExportProcessor) ExportBackend() models.Backend {
+ return p.Backend
+}
+
+func (p ListensExportProcessor) Process(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) {
+ p.Backend.ExportListens(oldestTimestamp, results, progress)
+}
+
+type LovesExportProcessor struct {
+ Backend models.LovesExport
+}
+
+func (p LovesExportProcessor) ExportBackend() models.Backend {
+ return p.Backend
+}
+
+func (p LovesExportProcessor) Process(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) {
+ p.Backend.ExportLoves(oldestTimestamp, results, progress)
+}
diff --git a/internal/backends/import.go b/internal/backends/import.go
index cd47960..6365cbb 100644
--- a/internal/backends/import.go
+++ b/internal/backends/import.go
@@ -17,7 +17,9 @@ Scotty. If not, see .
package backends
-import "go.uploadedlobster.com/scotty/internal/models"
+import (
+ "go.uploadedlobster.com/scotty/internal/models"
+)
type ImportProcessor[T models.ListensResult | models.LovesResult] interface {
ImportBackend() models.ImportBackend