Skip to content

Commit

Permalink
Merge pull request #5 from vyshnav-vinod/path-restrictions
Browse files Browse the repository at this point in the history
Path restrictions
  • Loading branch information
vyshnav-vinod authored May 24, 2024
2 parents cd76bf5 + 61c9e9f commit e497442
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 46 deletions.
32 changes: 22 additions & 10 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"strings"
"time"
)

Expand Down Expand Up @@ -119,33 +120,42 @@ func (c *Cache) SetPreviousDir() {
}
}

func (c *Cache) GetCacheEntry(entry string) (cacheEntry CacheEntry, ok bool) {
if cache, found := c.contents[entry]; found {
return cache, true
func (c *Cache) GetCacheEntry(entry pathInfo) (cacheEntry CacheEntry, ok bool) {
baseInput := filepath.Base(entry.userInput)
if cache, found := c.contents[baseInput]; found {
if !entry.restrict {
return cache, true
} else {
if strings.Contains(cache.Path, entry.userInput) {
return cache, true
}
}
}
return CacheEntry{}, false
}

func (c *Cache) SetCacheEntry(entry string) {
func (c *Cache) SetCacheEntry(entry pathInfo) {
// Shoudl check if entry is in cache ,if yes just update
// else pop from cache and add new entry
home, _ := os.UserHomeDir()
if !(entry == home) { // Do not add home directory to cache
if cacheEntry, ok := c.GetCacheEntry(filepath.Base(entry)); ok {
if cacheEntry.Path == entry {
c.contents[filepath.Base(entry)] = CacheEntry{
if !((entry.path) == home) {
if cacheEntry, ok := c.GetCacheEntry(entry); ok {
if cacheEntry.Path == entry.path {
c.contents[filepath.Base(entry.path)] = CacheEntry{
Path: cacheEntry.Path,
Frequency: cacheEntry.Frequency + 1,
LastHit: time.Now(),
}
}
// TODO: Else condition
} else {
c.contents[filepath.Base(entry)] = CacheEntry{
Path: entry,
c.contents[filepath.Base(entry.path)] = CacheEntry{
Path: entry.path,
Frequency: 0,
LastHit: time.Now(),
}
}

if len(c.contents) > (c.maxCap + 1) { // +1 to denote the previous dir store
c.popCache()
}
Expand Down Expand Up @@ -190,3 +200,5 @@ func (c *Cache) cleanCache() {
HandleError(err)
}
}

// TODO: Add logging while in DEV
62 changes: 48 additions & 14 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var c Cache

var (
toPopEntryLRU = "entry/to/pop/LRU" // will be popped according to LRU
toPopEntryLFU = "entry/to/popLFU" // Will be popped according to LFU
toPopEntryLFU = "entry/to/pop/LFU" // Will be popped according to LFU

)

Expand Down Expand Up @@ -65,30 +65,64 @@ func Test_validateCache(t *testing.T) {
}
}

// TODO: Make this cleaner
func Test_SetCacheEntry(t *testing.T) {
entry := "random/entry"
for i := 0; i < 2; i++ {
entries := []pathInfo{
{
userInput: "entry",
restrict: false,
path: "file/path/entry",
},
{
userInput: "path/entry",
restrict: true,
path: "file/path/entry",
},
}
for _, entry := range entries {
c.SetCacheEntry(entry)
ce, got := c.GetCacheEntry(filepath.Base(entry))
_, got := c.GetCacheEntry(entry)
if !got {
t.Errorf("SetCacheEntry() failed to add %s to cache", entry)
}
if ce.Frequency != i {
t.Errorf("SetCacheEntry() got frequency = %d, want = %d", ce.Frequency, i)
t.Errorf("SetCacheEntry() failed to add %v to cache", entry)
}
}
c.SetCacheEntry(toPopEntryLRU)
c.SetCacheEntry(toPopEntryLFU)
test_entries_popCache := []pathInfo{
{
userInput: filepath.Base(toPopEntryLRU),
restrict: false,
path: toPopEntryLRU,
},
{
userInput: filepath.Base(toPopEntryLFU),
restrict: false,
path: toPopEntryLFU,
},
}
for _, entry := range test_entries_popCache {
c.SetCacheEntry(entry)
}
}

// Test for popCache is depended on test for SetCacheEntry
func Test_popCache(t *testing.T) {
c.LoadCache()
var popEntries = []string{toPopEntryLRU, toPopEntryLFU}
for _, j := range popEntries {
// fmt.Println(c.contents)
var popEntries = []pathInfo{
{
userInput: filepath.Base(toPopEntryLRU),
restrict: false,
path: toPopEntryLRU,
},
{
userInput: filepath.Base(toPopEntryLFU),
restrict: false,
path: toPopEntryLFU,
},
}
for _, e := range popEntries {
c.popCache()
if entry, ok := c.GetCacheEntry(filepath.Base(j)); ok {
t.Errorf("popCache() failed to pop %v", entry)
if ce, got := c.GetCacheEntry(e); got {
t.Errorf("popEntry() failed to pop %v ", ce)
}
}
}
74 changes: 52 additions & 22 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ var (
timeStart time.Time
)

type pathInfo struct {
// Stores the user input
userInput string
// Restrict is used to denote whether user has specified
// the parent directory of the directory to search as well.
// Eg: User has specified "parent/searchfolder"
// We must only find "searchfolder" which is a child of the
// directory named "parent". Any other folders named
// "searchfolder" will be rejected if their parent directory
// is not "parent"
restrict bool
// Full path (if found)
path string
}

func main() {

// Ignore, This is a build flag for testing purposes
Expand Down Expand Up @@ -77,7 +92,7 @@ func main() {
}
if previousDir {
path, _ := filepath.Abs(c.GetPreviousDir())
os.Exit(success(os.Stdout, path, c))
os.Exit(success(os.Stdout, pathInfo{path: path, restrict: false, userInput: filepath.Base(path)}, c))
}
if path == "" {
flaggy.ShowHelpAndExit("Please provide arguments")
Expand All @@ -91,9 +106,9 @@ func main() {
}
}

func pathfinder(w io.Writer, c *Cache, ignoreDir bool, path string) int {
func pathfinder(w io.Writer, c *Cache, ignoreDir bool, searchPath string) int {

absPath, err := filepath.Abs(path)
absPath, err := filepath.Abs(searchPath)
if err != nil {
HandleError(err)
}
Expand All @@ -105,60 +120,64 @@ func pathfinder(w io.Writer, c *Cache, ignoreDir bool, path string) int {
if _, err := os.Stat(absPath); !os.IsNotExist(err) {
// To support ~, .. , etc
if !ignoreDir {
return success(w, absPath, c)
return success(w, pathInfo{path: absPath, restrict: false, userInput: filepath.Base(absPath)}, c)
} else {
if !strings.Contains(absPath, cwd) { // Check if it is in current directory
return success(w, absPath, c)
return success(w, pathInfo{path: absPath, restrict: false, userInput: filepath.Base(absPath)}, c)
}
}
}

pathInfo := pathInfo{userInput: searchPath}
pathInfo.restrict = !(len(strings.Split(searchPath, "/")) == 1)

// Assume that the path is not an actual path but a search query by the user and it might exist
if cacheEntry, ok := c.GetCacheEntry(filepath.Base(path)); ok {
if cacheEntry, ok := c.GetCacheEntry(pathInfo); ok {
if !ignoreDir {
return success(w, cacheEntry.Path, c)
pathInfo.path = cacheEntry.Path
return success(w, pathInfo, c)
} else {
if !strings.Contains(cacheEntry.Path, cwd) { // Check if it is in current directory
return success(w, cacheEntry.Path, c)
pathInfo.path = cacheEntry.Path
return success(w, pathInfo, c)
}
}
}

var pathReturned string
dirsAlreadyWalked := make(map[string]struct{}) // to ignore walking through already walked directories

// TODO: Goroutines or a new algorithm
if !ignoreDir {
if traverseAndMatchDir(w, cwd, path, &pathReturned, dirsAlreadyWalked, c) {
if traverseAndMatchDir(w, cwd, pathInfo, &pathInfo.path, dirsAlreadyWalked, c) {
// Walk inside working directory
return success(w, pathReturned, c)
return success(w, pathInfo, c)
}
}

dirsAlreadyWalked[cwd] = struct{}{}
if traverseAndMatchDir(w, filepath.Dir(cwd), path, &pathReturned, dirsAlreadyWalked, c) {
if traverseAndMatchDir(w, filepath.Dir(cwd), pathInfo, &pathInfo.path, dirsAlreadyWalked, c) {
// Walk from one directory above
return success(w, pathReturned, c)
return success(w, pathInfo, c)
}

usrHome, err := os.UserHomeDir()
if err != nil {
HandleError(err)
}
dirsAlreadyWalked[filepath.Dir(cwd)] = struct{}{}
if traverseAndMatchDir(w, usrHome, path, &pathReturned, dirsAlreadyWalked, c) {
if traverseAndMatchDir(w, usrHome, pathInfo, &pathInfo.path, dirsAlreadyWalked, c) {
// Walk from $HOME
return success(w, pathReturned, c)
return success(w, pathInfo, c)
}

// pathfinder failed to find the directory and prints
// the path (user input) to stdout for the bash script
// to capture and return as an error msg
fmt.Fprint(w, path)
fmt.Fprint(w, searchPath)
return EXIT_FOLDERNOTFOUND
}

func traverseAndMatchDir(w io.Writer, dirName string, searchDir string, pathReturned *string, dirsAlreadyWalked map[string]struct{}, c *Cache) bool {
func traverseAndMatchDir(w io.Writer, dirName string, searchDir pathInfo, pathReturned *string, dirsAlreadyWalked map[string]struct{}, c *Cache) bool {
if strings.HasPrefix(filepath.Base(dirName), ".") {
return false
}
Expand All @@ -184,9 +203,20 @@ func traverseAndMatchDir(w io.Writer, dirName string, searchDir string, pathRetu
continue
}
if f.IsDir() {
if f.Name() == searchDir {
*pathReturned = path
return true
if f.Name() == filepath.Base(searchDir.userInput) {
if !searchDir.restrict {
*pathReturned = path
return true
} else {
if strings.Contains(path, searchDir.userInput) {
*pathReturned = path
return true
} else {
if traverseAndMatchDir(w, path, searchDir, pathReturned, dirsAlreadyWalked, c) {
return true
}
}
}
} else {
if traverseAndMatchDir(w, path, searchDir, pathReturned, dirsAlreadyWalked, c) {
return true
Expand All @@ -198,9 +228,9 @@ func traverseAndMatchDir(w io.Writer, dirName string, searchDir string, pathRetu
return false
}

func success(w io.Writer, path string, c *Cache) int {
func success(w io.Writer, path pathInfo, c *Cache) int {
// Prints to stdout for bash script to capture
fmt.Fprint(w, path)
fmt.Fprint(w, path.path)
c.SetPreviousDir()
c.SetCacheEntry(path)
return EXIT_SUCCESS
Expand Down

0 comments on commit e497442

Please sign in to comment.