Paul Ruane avatar Paul Ruane committed dda9aa9

Initial work on having untagged directory contents in the database.

Comments (0)

Files changed (11)

src/tmsu/commands/delete.go

 	}
 
 	for _, fileTag := range fileTags {
-		hasTags, err := db.AnyFileTagsForFile(fileTag.FileId)
+		tags, err := db.TagsByFileId(fileTag.FileId)
 		if err != nil {
 			return err
 		}
 
-		if !hasTags {
+		if len(tags) == 0 {
 			db.RemoveFile(fileTag.FileId)
 		}
 	}

src/tmsu/commands/dupes.go

 		fmt.Printf("Set of %v duplicates:\n", len(fileSet))
 
 		for _, file := range fileSet {
-			relPath := common.MakeRelative(file.Path())
+			relPath := common.RelPath(file.Path())
 			fmt.Printf("  %v\n", relPath)
 		}
 	}
 			fmt.Printf("%v duplicates of %v:\n", len(dupes), path)
 
 			for _, dupe := range dupes {
-				relPath := common.MakeRelative(dupe.Path())
+				relPath := common.RelPath(dupe.Path())
 				fmt.Printf("  %v\n", relPath)
 			}
 		} else {
 			for _, dupe := range dupes {
-				relPath := common.MakeRelative(dupe.Path())
+				relPath := common.RelPath(dupe.Path())
 				fmt.Println(relPath)
 			}
 		}

src/tmsu/commands/files.go

 
 	paths := make([]string, len(files))
 	for _, file := range files {
-		relPath := common.MakeRelative(file.Path())
+		relPath := common.RelPath(file.Path())
 		paths = append(paths, relPath)
 	}
 

src/tmsu/commands/tag.go

 	if file == nil {
 		// new file
 
-		files, err := db.FilesByFingerprint(fingerprint)
-		if err != nil {
-			return nil, err
-		}
+		if !info.IsDir() {
+			duplicateCount, err := db.FileCountByFingerprint(fingerprint)
+			if err != nil {
+				return nil, err
+			}
 
-		if len(files) > 0 {
-			common.Warn("File is a duplicate of previously tagged files.")
-
-			for _, duplicateFile := range files {
-				common.Warnf("  %v", common.MakeRelative(duplicateFile.Path()))
+			if duplicateCount > 0 {
+				common.Warn("'" + common.RelPath(path) + "' is a duplicate of previously tagged files.")
 			}
 		}
 
 		if err != nil {
 			return nil, err
 		}
+
+		if info.IsDir() {
+			fsFile, err := os.Open(file.Path())
+			if err != nil {
+				return nil, err
+			}
+			defer fsFile.Close()
+
+			dirFilenames, err := fsFile.Readdirnames(0)
+			if err != nil {
+				return nil, err
+			}
+
+			for _, dirFilename := range dirFilenames {
+				command.addFile(db, filepath.Join(path, dirFilename))
+			}
+		}
 	} else {
 		// existing file
 

src/tmsu/commands/untag.go

 		}
 	}
 
-	hasTags, err := db.AnyFileTagsForFile(file.Id)
+	filetags, err := db.FileTagsByFileId(file.Id, false)
 	if err != nil {
 		return err
 	}
 
-	if !hasTags {
+	if len(filetags) == 0 {
 		err := db.RemoveFile(file.Id)
 		if err != nil {
 			return err

src/tmsu/common/config.go

 	"errors"
 	"fmt"
 	"os"
+	"os/user"
 	"path/filepath"
 	"strconv"
 	"strings"
 
 func resolvePath(path string) (string, error) {
 	if strings.HasPrefix(path, "~"+string(filepath.Separator)) {
-		homeDirectory := os.Getenv("HOME")
-		if homeDirectory == "" {
-			return "", errors.New("Could not identify home directory.")
+		user, err := user.Current()
+		if err != nil {
+			return "", errors.New("Could not identify home directory: " + err.Error())
 		}
 
-		path = strings.Join([]string{homeDirectory, path[2:]}, string(filepath.Separator))
+		path = strings.Join([]string{user.HomeDir, path[2:]}, string(filepath.Separator))
 	}
 
 	return path, nil

src/tmsu/common/config_test.go

 
 import (
 	"os"
+	"strings"
 	"testing"
 )
 
 func TestDefaultDatabase(test *testing.T) {
-	os.Clearenv()
-	os.Setenv("HOME", "/foo/bar/home")
-
 	config, err := GetDefaultDatabaseConfig()
 	if err != nil {
 		test.Fatal(err.Error())
 	if config.Name != "default" {
 		test.Fatal("Default database has incorrect name '" + config.Name + "'.")
 	}
-	if config.DatabasePath != "/foo/bar/home/.tmsu/default.db" {
+	if strings.Index(config.DatabasePath, "/.tmsu/default.db") != len(config.DatabasePath)-17 {
 		test.Fatal("Default database config has incorrect path '" + config.DatabasePath + "'.")
 	}
 }

src/tmsu/common/path.go

 	return info.IsDir()
 }
 
-func MakeRelative(path string) string {
+func RelPath(path string) string {
 	workingDirectory, err := os.Getwd()
 	if err != nil {
 		return path

src/tmsu/database/file.go

 	return files, nil
 }
 
+func (db Database) FileCountByFingerprint(fingerprint fingerprint.Fingerprint) (uint, error) {
+	sql := `SELECT count(id)
+            FROM file
+            WHERE fingerprint = ?`
+
+	rows, err := db.connection.Query(sql, string(fingerprint))
+	if err != nil {
+		return 0, err
+	}
+	defer rows.Close()
+
+	if !rows.Next() {
+		return 0, errors.New("Could not get file count.")
+	}
+	if rows.Err() != nil {
+		return 0, err
+	}
+
+	var count uint
+	err = rows.Scan(&count)
+	if err != nil {
+		return 0, err
+	}
+
+	return count, nil
+}
+
 func (db Database) FilesByFingerprint(fingerprint fingerprint.Fingerprint) (Files, error) {
 	sql := `SELECT id, directory, name, mod_time
 	        FROM file
 }
 
 func (db Database) RemoveFile(fileId uint) error {
+	file, err := db.File(fileId)
+	if err != nil {
+		return err
+	}
+	if file == nil {
+		return errors.New("No such file '" + string(fileId) + "'.")
+	}
+
 	sql := `DELETE FROM file
 	        WHERE id = ?`
 
 		return errors.New("Expected exactly one row to be affected.")
 	}
 
+	files, err := db.FilesByDirectory(file.Path())
+	if err != nil {
+		return err
+	}
+
+	for _, file := range files {
+		filetags, err := db.FileTagsByFileId(file.Id, false)
+		if err != nil {
+			return err
+		}
+
+		if len(filetags) == 0 {
+			db.RemoveFile(file.Id)
+		}
+	}
+
 	return nil
 }
 

src/tmsu/database/filetag.go

 }
 
 func (db Database) FileCountWithTags(tagIds []uint, explicit bool) (uint, error) {
+	//TODO optimize
 	files, err := db.FilesWithTags(tagIds, []uint{}, explicit)
 	if err != nil {
 		return 0, err
 	return readFileTags(rows, make(FileTags, 0, 10))
 }
 
-func (db Database) AnyFileTagsForFile(fileId uint) (bool, error) {
-	sql := `SELECT 1
+func (db Database) FileTagsByFileId(fileId uint, explicitOnly bool) (FileTags, error) {
+	sql := `SELECT id, file_id, tag_id
             FROM file_tag
-            WHERE file_id = ?
-            LIMIT 1`
+            WHERE file_id = ?`
 
 	rows, err := db.connection.Query(sql, fileId)
 	if err != nil {
-		return false, err
+		return nil, err
 	}
-	defer rows.Close()
+	fileTags, err := readFileTags(rows, make(FileTags, 0, 10))
+	rows.Close()
 
-	if rows.Next() {
-		return true, nil
-	}
-	if rows.Err() != nil {
-		return false, err
+	if !explicitOnly {
+		file, err := db.File(fileId)
+		if err != nil {
+			return nil, err
+		}
+
+		parentFile, err := db.FileByPath(file.Directory)
+		if err != nil {
+			return nil, err
+		}
+
+		if parentFile != nil {
+			additionalFileTags, err := db.FileTagsByFileId(parentFile.Id, explicitOnly)
+			if err != nil {
+				return nil, err
+			}
+
+			for _, additionalFileTag := range additionalFileTags {
+				fileTags = append(fileTags, additionalFileTag)
+			}
+		}
 	}
 
-	return false, nil
+	return fileTags, nil
 }
 
 func (db Database) AddFileTag(fileId uint, tagId uint) (*FileTag, error) {

src/tmsu/fingerprint/fingerprinter.go

 package fingerprint
 
 import (
-	"bytes"
 	"crypto/sha256"
-	"encoding/binary"
 	"encoding/hex"
 	"os"
-	"path/filepath"
-	"sort"
 	"tmsu/common"
 )
 
 
 func Create(path string) (Fingerprint, error) {
 	if common.IsDir(path) {
-		return createDirectoryFingerprint(path)
+		return EMPTY, nil
 	}
 
 	stat, err := os.Stat(path)
 	return createFullFingerprint(path)
 }
 
-func createDirectoryFingerprint(path string) (Fingerprint, error) {
-	file, err := os.Open(path)
-	if err != nil {
-		return EMPTY, err
-	}
-	defer file.Close()
-
-	entries, err := file.Readdir(0)
-	if err != nil {
-		return EMPTY, err
-	}
-	sort.Sort(FileInfoSlice(entries))
-
-	buffer := new(bytes.Buffer)
-	for _, entry := range entries {
-		entryPath := filepath.Join(path, entry.Name())
-		stat, err := os.Stat(entryPath)
-
-		if err != nil {
-			return EMPTY, err
-		}
-
-		binary.Write(buffer, binary.LittleEndian, stat.ModTime().UnixNano())
-	}
-
-	hash := sha256.New()
-	buffer.WriteTo(hash)
-
-	sum := hash.Sum(make([]byte, 0, 64))
-	fingerprint := hex.EncodeToString(sum)
-
-	return Fingerprint(fingerprint), nil
-}
-
 func createSparseFingerprint(path string, fileSize int64) (Fingerprint, error) {
 	buffer := make([]byte, sparseFingerprintSize)
 	hash := sha256.New()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.