209 lines
5.7 KiB
Go
209 lines
5.7 KiB
Go
package mapper
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/configuration"
|
|
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/git"
|
|
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/misc"
|
|
"github.com/charmbracelet/log"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type Items struct {
|
|
locations []configuration.OSLocation
|
|
storage string
|
|
repository git.RepositoryActions
|
|
indexer Indexer
|
|
}
|
|
|
|
type ItemsActions interface {
|
|
Action(action string)
|
|
AddItems(items []configuration.OSLocation)
|
|
CleanUp(removedLines []string) error
|
|
}
|
|
|
|
func NewItemsActions(items []configuration.OSLocation, storage string, repository git.RepositoryActions, indexer Indexer) ItemsActions {
|
|
if items == nil {
|
|
items = []configuration.OSLocation{}
|
|
}
|
|
|
|
return &Items{
|
|
locations: items,
|
|
storage: storage,
|
|
repository: repository,
|
|
indexer: indexer,
|
|
}
|
|
}
|
|
|
|
// Action performs a "save" or "load" action on all given items.
|
|
//
|
|
// Any error is printed to STDERR and item is skipped.
|
|
//
|
|
// If the performed action is "save", it'll also write the `.index` file with all new items.
|
|
func (e *Items) Action(action string) {
|
|
log.Info("performing action", "action", action)
|
|
newLines := []string{}
|
|
|
|
for i, l := range e.locations {
|
|
storagePath, systemPath, err := misc.ConfigPaths(l, e.storage)
|
|
if err != nil {
|
|
log.Error("failed to resolve item paths", "item", i, "location", l, "err", err)
|
|
continue
|
|
}
|
|
if storagePath == "" && systemPath == "" {
|
|
log.Info("item is empty", "item", i, "location", l)
|
|
continue
|
|
}
|
|
|
|
if action == "save" {
|
|
if newItem := e.saveItem(systemPath, storagePath, i); newItem != "" {
|
|
newLines = append(newLines, newItem)
|
|
} else {
|
|
continue
|
|
}
|
|
} else {
|
|
e.loadItem(storagePath, systemPath, i)
|
|
}
|
|
|
|
log.Info("item processed", "action", action, "item", i, "location", l)
|
|
}
|
|
|
|
if action == "save" && !viper.GetBool("disable-index-update") {
|
|
if err := e.indexer.Write(newLines); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// saveItem saves a given item inside the configured saved location.
|
|
//
|
|
// If an error is given during the process, the function returns an empty string
|
|
// (meaning the item hasn't been saved) and prints the error in STDERR.
|
|
//
|
|
// Else, returns the relative item location from the saved location to write the index
|
|
// (E.g: /home/user/.config => .config)
|
|
func (e *Items) saveItem(src, dst string, index int) string {
|
|
if err := os.MkdirAll(path.Dir(dst), 0755); err != nil {
|
|
log.Error("failed to create directory architecture for destination path", "path", path.Dir(dst), "err", err)
|
|
return ""
|
|
}
|
|
|
|
s, err := os.Stat(src)
|
|
if err != nil {
|
|
log.Error("failed to check if source path is a folder", "path", src, "err", err)
|
|
return ""
|
|
}
|
|
|
|
if s.IsDir() {
|
|
dstPerms := fs.FileMode(0755)
|
|
s, err := os.Stat(dst)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
log.Error("failed to check if destination folder exists", "path", dst, "err", err)
|
|
return ""
|
|
}
|
|
} else {
|
|
dstPerms = s.Mode()
|
|
}
|
|
|
|
// remove the destination if it exists. It cleans up the saved location from unused files
|
|
if err := os.RemoveAll(dst); err != nil {
|
|
log.Error("failed to truncate destination folder", "path", dst, "err", err)
|
|
}
|
|
|
|
if err := os.Mkdir(dst, dstPerms); err != nil {
|
|
if !os.IsExist(err) {
|
|
log.Error("failed to create destination folder", "path", dst, "err", err)
|
|
return ""
|
|
}
|
|
}
|
|
if err := misc.CopyFolder(src, dst, true); err != nil {
|
|
log.Error("failed to save folder from source to destination", "source", src, "destination", dst, "err", err)
|
|
return ""
|
|
}
|
|
} else {
|
|
if err := misc.CopyFile(src, dst); err != nil {
|
|
log.Error("failed to save file from source to destination", "source", src, "destination", dst, "err", err)
|
|
return ""
|
|
}
|
|
}
|
|
|
|
p, err := misc.AbsolutePath(e.storage)
|
|
if err != nil {
|
|
log.Error("failed resolve absolute path from configuration storage", "err", err)
|
|
return ""
|
|
}
|
|
|
|
return strings.ReplaceAll(dst, p+"/", "")
|
|
}
|
|
|
|
// loadItem loads a given item onto the system.
|
|
//
|
|
// If an error is given during the process, the function returns an empty string
|
|
// (meaning the item hasn't been saved) and prints the error in STDERR.
|
|
func (e *Items) loadItem(src, dst string, index int) {
|
|
if err := os.MkdirAll(path.Dir(dst), 0755); err != nil {
|
|
log.Error("failed to create directory architecture for destination path", "path", path.Dir(dst), "err", err)
|
|
return
|
|
}
|
|
|
|
s, err := os.Stat(src)
|
|
if err != nil {
|
|
log.Error("failed to check if source path is a folder", "path", src, "err", err)
|
|
return
|
|
}
|
|
|
|
if s.IsDir() {
|
|
dstPerms := fs.FileMode(0755)
|
|
s, err := os.Stat(dst)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
log.Error("failed to check if destination folder exists", "path", dst, "err", err)
|
|
return
|
|
}
|
|
} else {
|
|
dstPerms = s.Mode()
|
|
}
|
|
|
|
if err := os.Mkdir(dst, dstPerms); err != nil {
|
|
if !os.IsExist(err) {
|
|
log.Error("failed to create destination folder", "path", dst, "err", err)
|
|
return
|
|
}
|
|
}
|
|
if err := misc.CopyFolder(src, dst, false); err != nil {
|
|
log.Error("failed to load folder from source to destination", "source", src, "destination", dst, "err", err)
|
|
return
|
|
}
|
|
} else {
|
|
if err := misc.CopyFile(src, dst); err != nil {
|
|
log.Error("failed to load file from source to destination", "source", src, "destination", dst, "err", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *Items) AddItems(items []configuration.OSLocation) {
|
|
e.locations = append(e.locations, items...)
|
|
}
|
|
|
|
func (e *Items) CleanUp(removedLines []string) error {
|
|
for _, l := range removedLines {
|
|
path, err := misc.AbsolutePath(fmt.Sprintf("%s/%s", e.storage, l))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.RemoveAll(path); err != nil {
|
|
return fmt.Errorf("failed to remove item %s: %v", l, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|