diff --git a/sync/db.go b/sync/db.go index d57886d..6fbafc3 100644 --- a/sync/db.go +++ b/sync/db.go @@ -29,6 +29,16 @@ func (s *Sync) initSchema() error { return err } +func (s *Sync) deleteDBFileInfo(side, relativeName string) error { + stmt, err := s.db.Prepare(fmt.Sprintf(`DELETE FROM %s_state WHERE relative_name = ?`, side)) + if err != nil { + return errors.Wrap(err, "Unable to prepare query") + } + + _, err = stmt.Exec(relativeName) + return errors.Wrap(err, "Unable to delete file info") +} + func (s *Sync) getDBFileInfo(side, relativeName string) (providers.FileInfo, error) { info := providers.FileInfo{} @@ -48,7 +58,7 @@ func (s *Sync) getDBFileInfo(side, relativeName string) (providers.FileInfo, err return info, nil } -func (s *Sync) setDBFileInfo(side, info providers.FileInfo) error { +func (s *Sync) setDBFileInfo(side string, info providers.FileInfo) error { stmt, err := s.db.Prepare(fmt.Sprintf( `INSERT INTO %s_state VALUES(?, ?, ?, ?) ON CONFLICT(relative_name) DO UPDATE SET diff --git a/sync/execute.go b/sync/execute.go new file mode 100644 index 0000000..2d9a5f2 --- /dev/null +++ b/sync/execute.go @@ -0,0 +1,45 @@ +package sync + +import ( + "github.com/pkg/errors" + + "github.com/Luzifer/cloudbox/providers" +) + +func (s *Sync) deleteFile(on providers.CloudProvider, fileName string) error { + if err := on.DeleteFile(fileName); err != nil { + return errors.Wrap(err, "Unable to delete file") + } + + if err := s.deleteDBFileInfo(sideLocal, fileName); err != nil { + return errors.Wrap(err, "Umable to delete local file info") + } + + if err := s.deleteDBFileInfo(sideRemote, fileName); err != nil { + return errors.Wrap(err, "Umable to delete remote file info") + } + + return nil +} + +func (s *Sync) transferFile(from, to providers.CloudProvider, sideFrom, sideTo, fileName string) error { + file, err := from.GetFile(fileName) + if err != nil { + return errors.Wrap(err, "Unable to retrieve file") + } + + newFile, err := to.PutFile(file) + if err != nil { + return errors.Wrap(err, "Unable to put file") + } + + if err := s.setDBFileInfo(sideTo, newFile.Info()); err != nil { + return errors.Wrap(err, "Unable to update DB info for target file") + } + + if err := s.setDBFileInfo(sideFrom, file.Info()); err != nil { + return errors.Wrap(err, "Unable to update DB info for source file") + } + + return nil +} diff --git a/sync/logic.go b/sync/logic.go new file mode 100644 index 0000000..b2673a3 --- /dev/null +++ b/sync/logic.go @@ -0,0 +1,61 @@ +package sync + +import log "github.com/sirupsen/logrus" + +func (s *Sync) decideAction(syncState *state, fileName string) error { + var ( + change = syncState.GetChangeFor(fileName) + logger = log.WithField("filename", fileName) + ) + + switch { + case !change.Changed(): + // No changes at all: Get out of here + logger.Debug("File in sync") + return nil + + case change.HasAll(ChangeLocalUpdate, ChangeRemoteUpdate): + // We do have local and remote changes: Check both are now the same or leave this to manual resolve + logger.Warn("File has local and remote updates, sync not possible") + + case change.HasAll(ChangeLocalAdd, ChangeRemoteAdd): + // Special case: Both are added, check thet are the same file or break + logger.Debug("File added locally as well as remotely") + // TODO: Handle special case + + case change.HasAll(ChangeLocalDelete, ChangeRemoteDelete): + // Special case: Both vanished, we just need to clean up the sync cache + logger.Debug("File deleted locally as well as remotely") + // TODO: Handle special case + + case change.Is(ChangeLocalAdd) || change.Is(ChangeLocalUpdate): + logger.Debug("File added or changed locally, uploading...") + if err := s.transferFile(s.local, s.remote, sideLocal, sideRemote, fileName); err != nil { + logger.WithError(err).Error("Unable to upload file") + } + + case change.Is(ChangeLocalDelete): + logger.Debug("File deleted locally, removing from remote...") + if err := s.deleteFile(s.remote, fileName); err != nil { + logger.WithError(err).Error("Unable to delete file from remote") + } + + case change.Is(ChangeRemoteAdd) || change.Is(ChangeRemoteUpdate): + logger.Debug("File added or changed remotely, downloading...") + if err := s.transferFile(s.remote, s.local, sideRemote, sideLocal, fileName); err != nil { + logger.WithError(err).Error("Unable to download file") + } + + case change.Is(ChangeRemoteDelete): + logger.Debug("File deleted remotely, removing from local...") + if err := s.deleteFile(s.local, fileName); err != nil { + logger.WithError(err).Error("Unable to delete file from local") + } + + default: + // Unhandled case + logger.WithField("change", change).Warn("Unhandled change case") + } + + return nil +} diff --git a/sync/state.go b/sync/state.go index 9eed6fd..f972706 100644 --- a/sync/state.go +++ b/sync/state.go @@ -27,6 +27,16 @@ func (c *Change) Register(add Change) { *c = *c | add } +func (c Change) HasAll(test ...Change) bool { + for _, t := range test { + if c&t == 0 { + return false + } + } + + return true +} + func (c Change) HasOne(test ...Change) bool { for _, t := range test { if c&t != 0 { diff --git a/sync/sync.go b/sync/sync.go index 923df29..0364601 100644 --- a/sync/sync.go +++ b/sync/sync.go @@ -103,8 +103,11 @@ func (s *Sync) runSync() error { return errors.Wrap(err, "Unable to load remote files") } - // TODO: Do something with sync database - s.log.Printf("%#v", syncState) + for _, fileName := range syncState.GetRelativeNames() { + if err := s.decideAction(syncState, fileName); err != nil { + return errors.Wrap(err, "Could not execute sync") + } + } return nil }