From 1ec2869b6691445985d338cf7d0a58c5911e2bd5 Mon Sep 17 00:00:00 2001 From: ojizero Date: Fri, 13 Dec 2019 14:35:47 +0200 Subject: [PATCH] Initial release --- .github/workflows/push.yml | 31 ++++++++++++++ .github/workflows/release.yml | 20 +++++++++ .gitignore | 25 ++++++++++++ LICENSE | 21 ++++++++++ README.md | 27 ++++++++++++ findup.go | 63 ++++++++++++++++++++++++++++ findup_test.go | 95 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 8 ++++ go.sum | 14 +++++++ 9 files changed, 304 insertions(+) create mode 100644 .github/workflows/push.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 findup.go create mode 100644 findup_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..e2cb270 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,31 @@ +name: Run tests +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + test-package: + name: Test + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.13 + uses: actions/setup-go@v1 + with: + go-version: 1.13 + id: go + + - name: Check out code + uses: actions/checkout@v1 + + - name: Get dependencies + run: go mod download + + - name: Tests + run: go test + + # - name: Version bump + # if: github.ref == "master" + # run: "automatically bump the version here" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..955ebd0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,20 @@ +name: Create new Github release +on: + push: + tags: + - v* +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v1 + + - name: Release new version + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..193bc7b --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Created by https://www.gitignore.io/api/go +# Edit at https://www.gitignore.io/?templates=go + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go Patch ### +/vendor/ +/Godeps/ + +# End of https://www.gitignore.io/api/go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..60e390f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Ameer A. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3402513 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Go Find Up + +> Find a file or directory by walking up parent directories. + +## Usage + +Install latest version + +```sh +go get github.com/ojizero/gofindup +``` + +Import it into your code + +```go +import "github.com/ojizero/gofindup" +``` + +This package only exposes 2 functions, `Findup` and `FindupFrom`, + +```go +// looks the file recursively in parents starting from "./" +gofindup.Findup("some-file-or-directory") + +// looks the file recursively in parents starting from "./some-starting-directory" +gofindup.Findup("some-file-or-directory", "some-starting-directory") +``` diff --git a/findup.go b/findup.go new file mode 100644 index 0000000..c291173 --- /dev/null +++ b/findup.go @@ -0,0 +1,63 @@ +package gofindup + +import ( + "path/filepath" + + "github.com/spf13/afero" +) + +var fs = afero.NewOsFs() + +func findIn(name, dir string, fs afero.Fs) (bool, error) { + files, err := afero.ReadDir(fs, dir) + + if err != nil { + return false, err + } + + for _, f := range files { + if name == f.Name() { + return true, nil + } + } + + return false, nil +} + +func findupFrom(name, dir string, fs afero.Fs) (bool, error) { + for { + found, err := findIn(name, dir, fs) + + if err != nil { + return false, err + } + + if found { + return true, nil + } + + parent := filepath.Dir(dir) + + if parent == dir { + return false, nil + } + + dir = parent + } +} + +func findup(name string, fs afero.Fs) (bool, error) { + return findupFrom(name, ".", fs) +} + +// Recursively find a file by walking up parents in the file tree +// starting from a specific directory. +func FindupFrom(name, dir string) (bool, error) { + return findupFrom(name, dir, fs) +} + +// Recursively find a file by walking up parents in the file tree +// starting from the current working directory. +func Findup(name string) (bool, error) { + return findup(name, fs) +} diff --git a/findup_test.go b/findup_test.go new file mode 100644 index 0000000..da51aa5 --- /dev/null +++ b/findup_test.go @@ -0,0 +1,95 @@ +package gofindup + +import ( + "path/filepath" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" +) + +type given struct { + file string + base string +} + +type expect struct { + found bool + err string +} + +type assertion struct { + given given + expect expect +} + +type assertions []assertion + +type finderFunc func(string, string, afero.Fs) (bool, error) + +var fakefs = afero.NewMemMapFs() + +func init() { + // Build fake file system + + fakefs.MkdirAll("/test", 0755) + + fakefs.MkdirAll("/test/a", 0755) + fakefs.MkdirAll("/test/a/ab", 0755) + afero.WriteFile(fakefs, "/test/a/ab/f.txt", []byte("some mock file"), 0644) + fakefs.MkdirAll("/test/a/ac", 0755) + afero.WriteFile(fakefs, "/test/a/ad.txt", []byte("some mock file"), 0644) + + fakefs.MkdirAll("/test/b", 0755) + fakefs.MkdirAll("/test/b/bc", 0755) + afero.WriteFile(fakefs, "/test/b/bc/f.txt", []byte("some mock file"), 0644) + afero.WriteFile(fakefs, "/test/b/bd.txt", []byte("some mock file"), 0644) + + fakefs.MkdirAll("/test/c", 0755) + fakefs.MkdirAll("/test/c/cd", 0755) + afero.WriteFile(fakefs, "/test/c/cd/f.txt", []byte("some mock file"), 0644) + + afero.WriteFile(fakefs, "/test/t.txt", []byte("some mock file"), 0644) + afero.WriteFile(fakefs, "r.txt", []byte("some mock file"), 0644) +} + +func testAssertions(t *testing.T, as assertions, fn finderFunc) { + for _, a := range as { + found, err := fn(a.given.file, a.given.base, fakefs) + + if a.expect.found { + assert.True(t, found) + } else { + assert.False(t, found) + } + + if a.expect.err == "" { + assert.Nil(t, err) + } else { + assert.EqualError(t, err, a.expect.err) + } + } +} + +func TestFindIn(t *testing.T) { + assertions := assertions{ + {given{"ab", "/test/a"}, expect{true, ""}}, + {given{"ac", "/test/a"}, expect{true, ""}}, + {given{"ae", "/test/a"}, expect{false, ""}}, + {given{"de", "/test/d"}, expect{false, "open " + filepath.Join("/", "test", "d") + ": file does not exist"}}, + } + + testAssertions(t, assertions, findIn) +} + +func TestFindUpFrom(t *testing.T) { + assertions := assertions{ + {given{"ab", "/test/a"}, expect{true, ""}}, + {given{"ab", "/test/a/ac"}, expect{true, ""}}, + {given{"t.txt", "/test/a/ac"}, expect{true, ""}}, + {given{"r.txt", "/test/a/ac"}, expect{true, ""}}, + {given{"r.txt", "/test/d"}, expect{false, "open " + filepath.Join("/", "test", "d") + ": file does not exist"}}, + } + + testAssertions(t, assertions, findupFrom) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..59e95f3 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/ojizero/gofindup + +go 1.13 + +require ( + github.com/spf13/afero v1.2.2 + github.com/stretchr/testify v1.4.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2ec6d36 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=