Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Path restrictions #5

Merged
merged 3 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading