Skip to content

Commit

Permalink
repository: Implementation of deleteItem done.
Browse files Browse the repository at this point in the history
  • Loading branch information
cbrand committed Mar 12, 2015
1 parent 0960f09 commit 716932a
Show file tree
Hide file tree
Showing 2 changed files with 338 additions and 56 deletions.
275 changes: 219 additions & 56 deletions repository/clientRepository.go
Expand Up @@ -14,10 +14,9 @@ import (
"github.com/hoffie/larasync/helpers/path"
"github.com/hoffie/larasync/repository/nib"
"github.com/hoffie/larasync/repository/tracker"
"github.com/hoffie/larasync/helpers"
)



// ClientRepository is a Repository from a client-side view; it has all the keys
// and a work dir (comapred to the base Repository)
type ClientRepository struct {
Expand All @@ -38,7 +37,10 @@ func NewClient(path string) *ClientRepository {
func (r *ClientRepository) NIBTracker() (tracker.NIBTracker, error) {
if r.nibTracker == nil {
repo := r.Repository
tracker, err := tracker.NewDatabaseNIBTracker(filepath.Join(repo.GetManagementDir(), "nib_tracker.db"))
tracker, err := tracker.NewDatabaseNIBTracker(
filepath.Join(repo.GetManagementDir(), "nib_tracker.db"),
repo.Path,
)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -105,6 +107,31 @@ func (r *ClientRepository) splitFileToChunks(path string, handler func(string, [
return ids, nil
}

// fileToChunkIds returnes te current chunk hashes for the given path.
func (r *ClientRepository) fileToChunkIds(path string) ([]string, error) {
chunker, err := NewChunker(path, chunkSize)
if err != nil {
return nil, err
}
defer chunker.Close()
var ids []string
for chunker.HasNext() {
chunk, err := chunker.Next()
if err != nil {
return nil, err
}

// hash for content-addressing
hexHash, err := r.hashChunk(chunk)
if err != nil {
return nil, err
}

ids = append(ids, hexHash)
}
return ids, nil
}

// GetSigningPrivateKey exposes the signing private key as it is required
// in foreign packages such as api.
func (r *ClientRepository) GetSigningPrivateKey() ([PrivateKeySize]byte, error) {
Expand Down Expand Up @@ -230,7 +257,7 @@ func (r *ClientRepository) writeMetadata(absPath string) (string, error) {
return hexHash, nil
}

// getFilesNIBUUID returns the NIB for the given relative path
// pathToNIBID returns the NIB ID for the given relative path
func (r *ClientRepository) pathToNIBID(relPath string) (string, error) {
return r.hashChunk([]byte(relPath))
}
Expand Down Expand Up @@ -314,61 +341,61 @@ func (r *ClientRepository) checkoutNIB(nib *nib.NIB) error {
return err
}

return r.checkoutRevision(nib, rev)
return r.checkoutRevision(nib, rev)
}

// checkoutRevision checks out the provided Revision into the working directory.
func (r *ClientRepository) checkoutRevision(nib *nib.NIB, rev *nib.Revision) error {
metadata, err := r.metadataByID(rev.MetadataID)
if err != nil {
return err
}

relPath := metadata.RepoRelativePath
if relPath == "" {
return errors.New("metadata lacks path")
}
absPath := filepath.Join(r.Path, relPath)

targetDir := filepath.Dir(absPath)

err = os.MkdirAll(targetDir, defaultDirPerms)
if err != nil && !os.IsExist(err) {
return err
}
err = nil

if len(rev.ContentIDs) > 0 {
writer, err := atomic.NewWriter(absPath, ".lara.checkout.", defaultFilePerms)
defer writer.Close()
if err != nil {
writer.Abort()
return err
}

for _, contentID := range rev.ContentIDs {
content, err := r.readEncryptedObject(contentID)
_, err = writer.Write(content)
if err != nil {
writer.Abort()
return err
}
}

hasChanges, err := r.pathHasConflictingChanges(nib, absPath)
if err != nil {
writer.Abort()
return err
}
if hasChanges {
writer.Abort()
return ErrWorkDirConflict
}
} else if _, errExistCheck := os.Stat(absPath); errExistCheck == nil {
err = os.Remove(absPath)
}

return err
metadata, err := r.metadataByID(rev.MetadataID)
if err != nil {
return err
}

relPath := metadata.RepoRelativePath
if relPath == "" {
return errors.New("metadata lacks path")
}
absPath := filepath.Join(r.Path, relPath)

targetDir := filepath.Dir(absPath)

err = os.MkdirAll(targetDir, defaultDirPerms)
if err != nil && !os.IsExist(err) {
return err
}
err = nil

if len(rev.ContentIDs) > 0 {
writer, err := atomic.NewWriter(absPath, ".lara.checkout.", defaultFilePerms)
defer writer.Close()
if err != nil {
writer.Abort()
return err
}

for _, contentID := range rev.ContentIDs {
content, err := r.readEncryptedObject(contentID)
_, err = writer.Write(content)
if err != nil {
writer.Abort()
return err
}
}

hasChanges, err := r.pathHasConflictingChanges(nib, absPath)
if err != nil {
writer.Abort()
return err
}
if hasChanges {
writer.Abort()
return ErrWorkDirConflict
}
} else if _, errExistCheck := os.Stat(absPath); errExistCheck == nil {
err = os.Remove(absPath)
}

return err
}

// AddItem adds a new file or directory to the repository.
Expand Down Expand Up @@ -457,12 +484,148 @@ func (r *ClientRepository) addDirectory(absPath string) error {
err = r.AddItem(path)
if err == ErrRefusingWorkOnDotLara {
continue
} else if err != nil {
return err
}
}
return nil
}

// DeleteItem removes the given item with the passed absolute path.
func (r *ClientRepository) DeleteItem(absPath string) error {
relPath, err := r.getRepoRelativePath(absPath)
if err != nil {
return err
}

nibID, err := r.pathToNIBID(relPath)
if err != nil {
return err
}

nib, err := r.nibStore.Get(nibID)
if os.IsNotExist(err) {
return r.deleteDirectory(absPath)
} else if err != nil {
return err
}
rev, err := nib.LatestRevision()
if err != nil {
return err
}

if !rev.IsDelete() {
err = r.deleteFile(absPath)
} else {
err = r.deleteDirectory(absPath)
}
return err
}

// deleteDirectory checks the ClientRepositories path lookup and removes all
// files and directories in the given path.
func (r *ClientRepository) deleteDirectory(absPath string) error {
relPath, err := r.getRepoRelativePath(absPath)
if err != nil {
return err
}
paths, err := r.nibTracker.SearchPrefix(relPath)
if err != nil {
return err
}

for _, path := range paths {
err = r.DeleteItem(path.AbsPath())
if err != nil {
return err
}
}
return nil

return path.CleanUpEmptyDirs(absPath)
}

// deleteFile removes the specific file from the repository. Returns an error
// if the file does not exist in the repository.
func (r *ClientRepository) deleteFile(absPath string) error {
relPath, err := r.getRepoRelativePath(absPath)
if err != nil {
return err
}

nibID, err := r.pathToNIBID(relPath)
if err != nil {
return err
}

nibItem, err := r.nibStore.Get(nibID)
if err != nil {
return err
}

latestRevision, err := nibItem.LatestRevision()
if err != nil && err != nib.ErrNoRevision {
return err
}

deleteFileIfExisting := func() error {
if r.revisionIsFile(absPath, latestRevision) && !latestRevision.IsDelete() {
os.Remove(absPath)
}

stat, fileErr := os.Stat(absPath)
if fileErr != nil {
return nil
}

if !stat.IsDir() && latestRevision != nil {
ids, err := r.fileToChunkIds(absPath)
if err != nil {
return err
}
if helpers.StringsEqual(ids, latestRevision.ContentIDs) {
return os.Remove(absPath)
}
}
return nil
}

if err == nil && latestRevision != nil {
if latestRevision.IsDelete() {
return deleteFileIfExisting()
}
deleteRevision := latestRevision.Clone()
deleteRevision.ContentIDs = []string{}
nibItem.AppendRevision(deleteRevision)
err = r.nibStore.Add(nibItem)
if err != nil {
return err
}
}

return deleteFileIfExisting()
}

// revisionIsFile returns if the given revision is represented by the passed revision.
func (r *ClientRepository) revisionIsFile(absPath string, rev *nib.Revision) bool {
stat, err := os.Stat(absPath)
if rev == nil || (err == nil && stat.IsDir()) {
return false
}

if os.IsNotExist(err) {
if rev.IsDelete() {
return true
} else {
return false
}
}

ids, err := r.fileToChunkIds(absPath)
if err != nil {
return false
}

return helpers.StringsEqual(ids, rev.ContentIDs)
}

// SetAuthorization adds a authorization with the given publicKey and encrypts it with the
Expand Down

0 comments on commit 716932a

Please sign in to comment.