func lookupTagNames(store *storage.Storage, fileTags entities.FileTags) ([]string, error) { tagNames := make([]string, 0, len(fileTags)) for _, fileTag := range fileTags { tag, err := store.Tag(fileTag.TagId) if err != nil { return nil, fmt.Errorf("could not lookup tag: %v", err) } if tag == nil { return nil, fmt.Errorf("tag '%v' does not exist", fileTag.TagId) } var tagName string if fileTag.ValueId == 0 { tagName = tag.Name } else { value, err := store.Value(fileTag.ValueId) if err != nil { return nil, fmt.Errorf("could not lookup value: %v", err) } if value == nil { return nil, fmt.Errorf("value '%v' does not exist", fileTag.ValueId) } tagName = tag.Name + "=" + value.Name } tagNames = append(tagNames, tagName) } sort.Strings(tagNames) return tagNames, nil }
func listValuesForTag(store *storage.Storage, tagName string, showCount, onePerLine bool) error { tag, err := store.TagByName(tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { return fmt.Errorf("no such tag, '%v'.", tagName) } log.Infof(2, "retrieving values for tag '%v'.", tagName) values, err := store.ValuesByTag(tag.Id) if err != nil { return fmt.Errorf("could not retrieve values for tag '%v': %v", tagName, err) } if showCount { fmt.Println(len(values)) } else { if onePerLine { for _, value := range values { fmt.Println(value.Name) } } else { valueNames := make([]string, len(values)) for index, value := range values { valueNames[index] = value.Name } format.Columns(valueNames, terminalWidth()) } } return nil }
func repairModified(store *storage.Storage, modified fileIdAndInfoMap, pretend bool, fingerprintAlgorithm string) error { log.Infof(2, "repairing modified files") for path, fileIdAndStat := range modified { fileId := fileIdAndStat.fileId stat := fileIdAndStat.stat log.Infof(1, "%v: modified", path) fingerprint, err := fingerprint.Create(path, fingerprintAlgorithm) if err != nil { return fmt.Errorf("%v: could not create fingerprint: %v", path, err) } if !pretend { _, err := store.UpdateFile(fileId, path, fingerprint, stat.ModTime(), stat.Size(), stat.IsDir()) if err != nil { return fmt.Errorf("%v: could not update file in database: %v", path, err) } } } return nil }
func (command RepairCommand) repairModified(store *storage.Storage, modified fileIdAndInfoMap) error { if command.verbose { log.Info("repairing modified files") } for path, fileIdAndStat := range modified { fileId := fileIdAndStat.fileId stat := fileIdAndStat.stat log.Infof("%v: modified", path) fingerprint, err := fingerprint.Create(path) if err != nil { return fmt.Errorf("%v: could not create fingerprint: %v", path, err) } if !command.pretend { _, err := store.UpdateFile(fileId, path, fingerprint, stat.ModTime(), stat.Size(), stat.IsDir()) if err != nil { return fmt.Errorf("%v: could not update file in database: %v", path, err) } } } return nil }
func (command ImplyCommand) deleteImplication(store *storage.Storage, tagName, impliedTagName string) error { tag, err := store.Db.TagByName(tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { return fmt.Errorf("no such tag '%v'.", tagName) } impliedTag, err := store.Db.TagByName(impliedTagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", impliedTagName, err) } if impliedTag == nil { return fmt.Errorf("no such tag '%v'.", impliedTagName) } if command.verbose { log.Infof("removing tag implication of '%v' to '%v'.", tagName, impliedTagName) } if err = store.RemoveImplication(tag.Id, impliedTag.Id); err != nil { return fmt.Errorf("could not add delete tag implication of '%v' to '%v': %v", tagName, impliedTagName, err) } return nil }
func (command TagsCommand) listTagsForPath(store *storage.Storage, path string) error { if command.verbose { log.Infof("%v: retrieving tags.", path) } var tags, err = store.TagsForPath(path) if err != nil { return fmt.Errorf("%v: could not retrieve tags: %v", path, err) } if len(tags) == 0 { _, err := os.Stat(path) if err != nil { switch { case os.IsPermission(err): log.Warnf("%v: permission denied", path) case os.IsNotExist(err): return fmt.Errorf("%v: file not found", path) default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } } if command.count { log.Print(len(tags)) } else { for _, tag := range tags { log.Print(tag.Name) } } return nil }
func listTagsForWorkingDirectory(store *storage.Storage, showCount, onePerLine bool) error { file, err := os.Open(".") if err != nil { return fmt.Errorf("could not open working directory: %v", err) } defer file.Close() dirNames, err := file.Readdirnames(0) if err != nil { return fmt.Errorf("could not list working directory contents: %v", err) } sort.Strings(dirNames) for _, dirName := range dirNames { log.Infof(2, "%v: retrieving tags.", dirName) file, err := store.FileByPath(dirName) if err != nil { log.Warn(err.Error()) continue } if file == nil { continue } fileTags, err := store.FileTagsByFileId(file.Id) if err != nil { return fmt.Errorf("could not retrieve file-tags: %v", err) } tagNames, err := lookupTagNames(store, fileTags) if err != nil { return err } if showCount { fmt.Println(dirName + ": " + strconv.Itoa(len(tagNames))) } else { if onePerLine { fmt.Println(dirName) for _, tagName := range tagNames { fmt.Println(tagName) } fmt.Println() } else { fmt.Println(dirName + ": " + strings.Join(tagNames, " ")) } } } return nil }
func getOrCreateValue(store *storage.Storage, valueName string) (*entities.Value, error) { value, err := store.ValueByName(valueName) if err != nil { return nil, err } if value == nil { value, err = store.AddValue(valueName) if err != nil { return nil, err } } return value, nil }
func repairMissing(store *storage.Storage, missing databaseFileMap, pretend, force bool) error { for path, dbFile := range missing { if force && !pretend { if err := store.DeleteFileTagsByFileId(dbFile.Id); err != nil { return fmt.Errorf("%v: could not delete file-tags: %v", path, err) } log.Infof(1, "%v: removed", path) } else { log.Infof(1, "%v: missing", path) } } return nil }
func expectTags(test *testing.T, store *storage.Storage, file *entities.File, tags ...*entities.Tag) { fileTags, err := store.FileTagsByFileId(file.Id) if err != nil { test.Fatal(err) } if len(fileTags) != len(tags) { test.Fatalf("File '%v' has %v tags but expected %v.", file.Path(), len(fileTags), len(tags)) } for index, filetag := range fileTags { tag := tags[index] if filetag.TagId != tag.Id { test.Fatal("File '%v' is tagged %v but expected %v.", file.Path(), filetag.TagId, tag.Id) } } }
func listTagsForPath(store *storage.Storage, path string, showCount, onePerLine bool) error { log.Infof(2, "%v: retrieving tags.", path) file, err := store.FileByPath(path) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } var tagNames []string if file != nil { fileTags, err := store.FileTagsByFileId(file.Id) if err != nil { return fmt.Errorf("%v: could not retrieve file-tags: %v", path, err) } tagNames, err = lookupTagNames(store, fileTags) if err != nil { return err } } else { _, err := os.Stat(path) if err != nil { switch { case os.IsPermission(err): return fmt.Errorf("%v: permission denied", path) case os.IsNotExist(err): return fmt.Errorf("%v: no such file", path) default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } } if showCount { fmt.Println(len(tagNames)) } else { if onePerLine { for _, tagName := range tagNames { fmt.Println(tagName) } } else { format.Columns(tagNames, terminalWidth()) } } return nil }
func tagPath(store *storage.Storage, path string, tagValuePairs []TagValuePair, recursive bool, fingerprintAlgorithm string) error { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err) } stat, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { stat, err = os.Lstat(path) if err != nil { return err } } else { return err } } log.Infof(2, "%v: checking if file exists", absPath) file, err := store.FileByPath(absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } if file == nil { file, err = addFile(store, absPath, stat.ModTime(), uint(stat.Size()), stat.IsDir(), fingerprintAlgorithm) if err != nil { return fmt.Errorf("%v: could not add file: %v", path, err) } } log.Infof(2, "%v: applying tags.", file.Path()) for _, tagValuePair := range tagValuePairs { if _, err = store.AddFileTag(file.Id, tagValuePair.TagId, tagValuePair.ValueId); err != nil { return fmt.Errorf("%v: could not apply tags: %v", file.Path(), err) } } if recursive && stat.IsDir() { if err = tagRecursively(store, path, tagValuePairs, fingerprintAlgorithm); err != nil { return err } } return nil }
func (command *TagCommand) addFile(store *storage.Storage, path string, modTime time.Time, size uint, isDir bool) (*database.File, error) { if command.verbose { log.Infof("%v: adding file.", path) } fingerprint, err := fingerprint.Create(path) if err != nil { return nil, fmt.Errorf("%v: could not create fingerprint: %v", path, err) } file, err := store.AddFile(path, fingerprint, modTime, int64(size), isDir) if err != nil { return nil, fmt.Errorf("%v: could not add file to database: %v", path, err) } return file, nil }
func addFile(store *storage.Storage, path string, modTime time.Time, size uint, isDir bool, fingerprintAlgorithm string) (*entities.File, error) { log.Infof(2, "%v: creating fingerprint", path) fingerprint, err := fingerprint.Create(path, fingerprintAlgorithm) if err != nil { return nil, fmt.Errorf("%v: could not create fingerprint: %v", path, err) } log.Infof(2, "%v: adding file.", path) file, err := store.AddFile(path, fingerprint, modTime, int64(size), isDir) if err != nil { return nil, fmt.Errorf("%v: could not add file to database: %v", path, err) } return file, nil }
func getOrCreateTag(store *storage.Storage, tagName string) (*entities.Tag, error) { tag, err := store.TagByName(tagName) if err != nil { return nil, fmt.Errorf("could not look up tag '%v': %v", tagName, err) } if tag == nil { tag, err = store.AddTag(tagName) if err != nil { return nil, fmt.Errorf("could not create tag '%v': %v", tagName, err) } log.Warnf("New tag '%v'.", tagName) } return tag, nil }
func listValuesForTags(store *storage.Storage, tagNames []string, showCount, onePerLine bool) error { wereErrors := false for _, tagName := range tagNames { tag, err := store.TagByName(tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { log.Warnf("no such tag, '%v'.", tagName) wereErrors = true continue } log.Infof(2, "retrieving values for tag '%v'.", tagName) values, err := store.ValuesByTag(tag.Id) if err != nil { return fmt.Errorf("could not retrieve values for tag '%v': %v", tagName, err) } if showCount { fmt.Printf("%v: %v\n", tagName, len(values)) } else { if onePerLine { fmt.Println(tagName) for _, value := range values { fmt.Println(value.Name) } fmt.Println() } else { valueNames := make([]string, len(values)) for index, value := range values { valueNames[index] = value.Name } fmt.Printf("%v: %v\n", tagName, strings.Join(valueNames, " ")) } } } if wereErrors { return blankError } return nil }
func (command RepairCommand) repairMoved(store *storage.Storage, missing databaseFileMap, untagged fileInfoMap) error { if command.verbose { log.Info("repairing moved files") } moved := make([]string, 0, 10) for path, dbFile := range missing { if command.verbose { log.Infof("%v: searching for new location", path) } for candidatePath, stat := range untagged { if stat.Size() == dbFile.Size { fingerprint, err := fingerprint.Create(candidatePath) if err != nil { return fmt.Errorf("%v: could not create fingerprint: %v", path, err) } if fingerprint == dbFile.Fingerprint { log.Infof("%v: moved to %v", path, candidatePath) moved = append(moved, path) if !command.pretend { _, err := store.UpdateFile(dbFile.Id, candidatePath, dbFile.Fingerprint, stat.ModTime(), dbFile.Size, dbFile.IsDir) if err != nil { return fmt.Errorf("%v: could not update file in database: %v", path, err) } } delete(untagged, candidatePath) break } } } } for _, path := range moved { delete(missing, path) } return nil }
func (command TagCommand) tagPath(store *storage.Storage, path string, tagIds []uint) error { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err) } stat, err := os.Stat(path) if err != nil { switch { case os.IsPermission(err): return fmt.Errorf("%v: permisison denied", path) case os.IsNotExist(err): return fmt.Errorf("%v: no such file", path) default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } file, err := store.FileByPath(absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } if file == nil { file, err = command.addFile(store, absPath, stat.ModTime(), uint(stat.Size()), stat.IsDir()) if err != nil { return fmt.Errorf("%v: could not add file: %v", path, err) } } if command.verbose { log.Infof("%v: applying tags.", file.Path()) } if err = store.AddFileTags(file.Id, tagIds); err != nil { return fmt.Errorf("%v: could not apply tags: %v", file.Path(), err) } if command.recursive && stat.IsDir() { if err = command.tagRecursively(store, path, tagIds); err != nil { return err } } return nil }
func (command UntagCommand) lookupTagIds(store *storage.Storage, names []string) ([]uint, error) { tags, err := store.TagsByNames(names) if err != nil { return nil, fmt.Errorf("could not retrieve tags: %v", err) } for _, name := range names { if !tags.Any(func(tag *database.Tag) bool { return tag.Name == name }) { return nil, fmt.Errorf("no such tag '%v'", name) } } tagIds := make([]uint, len(tags)) for index, tag := range tags { tagIds[index] = tag.Id } return tagIds, nil }
func (command RepairCommand) repairMissing(store *storage.Storage, missing databaseFileMap) error { for path, dbFile := range missing { if command.force && !command.pretend { if err := store.RemoveFileTagsByFileId(dbFile.Id); err != nil { return fmt.Errorf("%v: could not delete file-tags: %v", path, err) } if err := store.RemoveFile(dbFile.Id); err != nil { return fmt.Errorf("%v: could not delete file: %v", path, err) } log.Infof("%v: removed", path) } else { log.Infof("%v: missing", path) } } return nil }
func (command UntagCommand) untagPath(store *storage.Storage, path string, tagIds []uint) error { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err) } file, err := store.FileByPath(absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } if file == nil { return fmt.Errorf("%v: file is not tagged.", path) } for _, tagId := range tagIds { if command.verbose { log.Infof("%v: unapplying tag #%v.", file.Path(), tagId) } if err := store.RemoveFileTag(file.Id, tagId); err != nil { return fmt.Errorf("%v: could not remove tag #%v: %v", file.Path(), tagId, err) } } if err := command.removeUntaggedFile(store, file); err != nil { return err } if command.recursive { childFiles, err := store.FilesByDirectory(file.Path()) if err != nil { return fmt.Errorf("%v: could not retrieve files for directory: %v", file.Path()) } for _, childFile := range childFiles { for _, tagId := range tagIds { if command.verbose { log.Infof("%v: unapplying tag #%v.", childFile.Path(), tagId) } if err := store.RemoveFileTag(childFile.Id, tagId); err != nil { return fmt.Errorf("%v: could not remove tag #%v: %v", childFile.Path(), tagId, err) } } if err := command.removeUntaggedFile(store, childFile); err != nil { return err } } } return nil }
func repairDatabase(store *storage.Storage, pretend, force bool, fingerprintAlgorithm string) error { log.Infof(2, "retrieving all files from the database.") files, err := store.Files() if err != nil { return fmt.Errorf("could not retrieve paths from storage: %v", err) } paths := make([]string, len(files)) for index := 0; index < len(files); index++ { paths[index] = files[index].Path() } err = repairFiles(store, paths, pretend, force, fingerprintAlgorithm) if err != nil { return err } return nil }
func (command TagsCommand) listTagsForPaths(store *storage.Storage, paths []string) error { for _, path := range paths { if command.verbose { log.Infof("%v: retrieving tags.", path) } var tags, err = store.TagsForPath(path) if err != nil { log.Warn(err.Error()) continue } if command.count { log.Print(path + ": " + strconv.Itoa(len(tags))) } else { log.Print(path + ": " + tagLine(tags)) } } return nil }
func (command RepairCommand) repairDatabase(store *storage.Storage) error { if command.verbose { log.Info("retrieving all files from the database.") } files, err := store.Files() if err != nil { return fmt.Errorf("could not retrieve paths from storage: %v", err) } paths := make([]string, len(files)) for index := 0; index < len(files); index++ { paths[index] = files[index].Path() } err = command.checkFiles(store, paths) if err != nil { return err } return nil }
func (command TagsCommand) listTagsForWorkingDirectory(store *storage.Storage) error { file, err := os.Open(".") if err != nil { return fmt.Errorf("could not open working directory: %v", err) } defer file.Close() dirNames, err := file.Readdirnames(0) if err != nil { return fmt.Errorf("could not list working directory contents: %v", err) } sort.Strings(dirNames) for _, dirName := range dirNames { if command.verbose { log.Infof("%v: retrieving tags.", dirName) } var tags, err = store.TagsForPath(dirName) if err != nil { log.Warn(err.Error()) continue } if len(tags) == 0 { continue } if command.count { log.Print(dirName + ": " + strconv.Itoa(len(tags))) } else { log.Print(dirName + ": " + tagLine(tags)) } } return nil }
func (command UntagCommand) removeUntaggedFile(store *storage.Storage, file *database.File) error { if command.verbose { log.Infof("%v: identifying whether file is tagged.", file.Path()) } filetagCount, err := store.FileTagCountByFileId(file.Id) if err != nil { return fmt.Errorf("%v: could not get tag count: %v", file.Path(), err) } if filetagCount == 0 { if command.verbose { log.Infof("%v: removing untagged file.", file.Path()) } err = store.RemoveFile(file.Id) if err != nil { return fmt.Errorf("%v: could not remove file: %v", file.Path(), err) } } return nil }
func (command UntagCommand) untagPathAll(store *storage.Storage, path string) error { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err) } file, err := store.FileByPath(absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } if file == nil { return fmt.Errorf("%v: file is not tagged.", path) } if command.verbose { log.Infof("%v: removing all tags.", file.Path()) } if err := store.RemoveFileTagsByFileId(file.Id); err != nil { return fmt.Errorf("%v: could not remove file's tags: %v", file.Path(), err) } if err := command.removeUntaggedFile(store, file); err != nil { return err } if command.recursive { childFiles, err := store.FilesByDirectory(file.Path()) if err != nil { return fmt.Errorf("%v: could not retrieve files for directory: %v", file.Path()) } for _, childFile := range childFiles { if err := store.RemoveFileTagsByFileId(childFile.Id); err != nil { return fmt.Errorf("%v: could not remove file's tags: %v", childFile.Path(), err) } if err := command.removeUntaggedFile(store, childFile); err != nil { return err } } } return nil }
func (command ImplyCommand) listImplications(store *storage.Storage) error { if command.verbose { log.Infof("retrieving tag implications.") } implications, err := store.Implications() if err != nil { return fmt.Errorf("could not retrieve implications: %v", err) } width := 0 for _, implication := range implications { length := len(implication.ImplyingTag.Name) if length > width { width = length } } previousImplyingTagName := "" for _, implication := range implications { if implication.ImplyingTag.Name != previousImplyingTagName { if previousImplyingTagName != "" { fmt.Println() } previousImplyingTagName = implication.ImplyingTag.Name fmt.Printf("%*v -> %v", width, implication.ImplyingTag.Name, implication.ImpliedTag.Name) } else { fmt.Printf(", %v", implication.ImpliedTag.Name) } } fmt.Println() return nil }
func enumerateDatabasePaths(store *storage.Storage, paths []string) (databaseFileMap, error) { dbFiles := make(databaseFileMap, 100) for _, path := range paths { file, err := store.FileByPath(path) if err != nil { return nil, fmt.Errorf("%v: could not retrieve file from database: %v", path, err) } if file != nil { dbFiles[file.Path()] = *file } files, err := store.FilesByDirectory(path) if err != nil { return nil, fmt.Errorf("%v: could not retrieve files from database: %v", path, err) } for _, file = range files { dbFiles[file.Path()] = *file } } return dbFiles, nil }
func (command TagCommand) lookupTagIds(store *storage.Storage, names []string) ([]uint, error) { tags, err := store.TagsByNames(names) if err != nil { return nil, fmt.Errorf("could not retrieve tags %v: %v", names, err) } for _, name := range names { if !tags.Any(func(tag *database.Tag) bool { return tag.Name == name }) { log.Infof("New tag '%v'.", name) tag, err := store.AddTag(name) if err != nil { return nil, fmt.Errorf("could not add tag '%v': %v", name, err) } tags = append(tags, tag) } } if command.verbose { log.Infof("retrieving tag implications") } tagIds := make([]uint, len(tags)) for index, tag := range tags { tagIds[index] = tag.Id } implications, err := store.ImplicationsForTags(tagIds...) if err != nil { return nil, fmt.Errorf("could not retrieve implied tags: %v", err) } for _, implication := range implications { if !contains(tagIds, implication.ImpliedTag.Id) { log.Infof("tag '%v' is implied.", implication.ImpliedTag.Name) tagIds = append(tagIds, implication.ImpliedTag.Id) } } return tagIds, nil }