diff --git a/args.go b/args.go index 17ab2c6..1a50385 100644 --- a/args.go +++ b/args.go @@ -29,14 +29,15 @@ type Args struct { // last argument if it represents a file name being written. // in case that it is not, we fall back to the current directory. func (a Args) Directory() string { - if info, err := os.Stat(a.Last); err == nil && info.IsDir() { - return fixPathForm(a.Last, a.Last) + path := fixPathForm(a.Last, true, a.Last) + if info, err := os.Stat(path); err == nil && info.IsDir() { + return fixPathForm(path, true, path) } - dir := filepath.Dir(a.Last) + dir := filepath.Dir(path) if info, err := os.Stat(dir); err != nil || !info.IsDir() { return "./" } - return fixPathForm(a.Last, dir) + return fixPathForm(path, true, dir) } func newArgs(line string) Args { diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..28069ea --- /dev/null +++ b/glide.lock @@ -0,0 +1,10 @@ +hash: 4a4bf605b36984489638d6807399e2bc08f16680aa8e4ae42ad1625316bfa9ac +updated: 2018-11-24T13:16:25.513580171+05:30 +imports: +- name: github.com/hashicorp/errwrap + version: 8a6fb523712970c966eefc6b39ed2c5e74880354 +- name: github.com/hashicorp/go-multierror + version: 886a7fbe3eb1c874d46f623bfa70af45f425b3d1 +- name: github.com/mitchellh/go-homedir + version: ae18d6b8b3205b561c79e8e5f69bff09736185f4 +testImports: [] diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..9548931 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,3 @@ +package: github.com/posener/complete +import: +- package: github.com/mitchellh/go-homedir diff --git a/predict_files.go b/predict_files.go index c8adf7e..9795b1d 100644 --- a/predict_files.go +++ b/predict_files.go @@ -39,7 +39,7 @@ func files(pattern string, allowFiles bool) PredictFunc { } // only try deeper, if the one item is a directory - if stat, err := os.Stat(prediction[0]); err != nil || !stat.IsDir() { + if stat, err := os.Stat(fixPathForm(prediction[0], true, prediction[0])); err != nil || !stat.IsDir() { return } @@ -67,7 +67,7 @@ func PredictFilesSet(files []string) PredictFunc { return func(a Args) (prediction []string) { // add all matching files to prediction for _, f := range files { - f = fixPathForm(a.Last, f) + f = fixPathForm(a.Last, false, f) // test matching of file to the argument if match.File(f, a.Last) { diff --git a/predict_test.go b/predict_test.go index 24df78d..9f9834d 100644 --- a/predict_test.go +++ b/predict_test.go @@ -1,6 +1,7 @@ package complete import ( + "os" "sort" "strings" "testing" @@ -11,10 +12,13 @@ func TestPredicate(t *testing.T) { initTests() tests := []struct { - name string - p Predictor - argList []string - want []string + name string + p Predictor + argList []string + want []string + prepEnv func() (string, map[string]string, error) + cleanEnv func(dirTreeBase string) + checkEqual func(dirTreeMappings map[string]string, got []string) bool }{ { name: "set", @@ -110,6 +114,44 @@ func TestPredicate(t *testing.T) { argList: []string{"./dir", "./dir/", "./di"}, want: []string{"./dir/", "./dir/foo", "./dir/bar"}, }, + { + name: "predict anything in home directory with `~` prefix", + p: PredictFiles("*"), + argList: []string{"~/foo"}, + want: []string{"~/foo", "~/foo/foo.md", "~/foo/foo-dir"}, + prepEnv: func() (string, map[string]string, error) { + basePath, dirTreeMappings, err := CreateDirTree( + `~`, + "foo", + []FileProperties{ + FileProperties{ + FilePath: "foo.md", + FileParent: "", + FileType: RegularFile, + ModificationType: CREATE, + }, + FileProperties{ + FilePath: "foo-dir", + FileParent: "", + FileType: Directory, + ModificationType: CREATE, + }, + }, + ) + return basePath, dirTreeMappings, err + }, + cleanEnv: func(dirTreeBase string) { + os.RemoveAll(dirTreeBase) + }, + checkEqual: func(dirTreeMappings map[string]string, got []string) bool { + want := []string{dirTreeMappings["foo"], dirTreeMappings["foo/foo.md"], dirTreeMappings["foo/-dir"]} + sort.Strings(got) + sort.Strings(want) + gotStr := strings.Join(got, ",") + wantStr := strings.Join(want, ",") + return gotStr == wantStr + }, + }, { name: "root directories", p: PredictDirs("*"), @@ -142,8 +184,21 @@ func TestPredicate(t *testing.T) { }, } + var basePath string + var err error + var dirTreeMappings map[string]string + for _, tt := range tests { + if tt.prepEnv != nil { + if basePath, dirTreeMappings, err = tt.prepEnv(); err != nil { + t.Errorf("error setting up env. Error %v", err) + } + } + if tt.cleanEnv != nil { + defer tt.cleanEnv(basePath) + } + // no args in argList, means an empty argument if len(tt.argList) == 0 { tt.argList = append(tt.argList, "") @@ -157,6 +212,10 @@ func TestPredicate(t *testing.T) { sort.Strings(matches) sort.Strings(tt.want) + if tt.checkEqual != nil { + tt.checkEqual(dirTreeMappings, matches) + return + } got := strings.Join(matches, ",") want := strings.Join(tt.want, ",") diff --git a/test_utils.go b/test_utils.go new file mode 100644 index 0000000..7b8a2bb --- /dev/null +++ b/test_utils.go @@ -0,0 +1,184 @@ +package complete + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +// TempMkdir creates a temporary directory +func TempMkdir(parentDir string, newDirPrefix string) (string, error) { + parentDir = filepath.FromSlash(parentDir) + dir, err := ioutil.TempDir(parentDir, newDirPrefix) + if err != nil { + return "", fmt.Errorf("failed to create dir with prefix %s in directory %s. Error %v", newDirPrefix, parentDir, err) + } + return dir, nil +} + +// TempMkFile creates a temporary file. +func TempMkFile(dir string, fileName string) (string, error) { + dir = filepath.FromSlash(dir) + f, err := ioutil.TempFile(dir, fileName) + if err != nil { + return "", fmt.Errorf("failed to create test file %s in dir %s. Error %v", fileName, dir, err) + } + if err := f.Close(); err != nil { + return "", err + } + return f.Name(), nil +} + +// FileType custom type to indicate type of file +type FileType int + +const ( + // RegularFile enum to represent regular file + RegularFile FileType = 0 + // Directory enum to represent directory + Directory FileType = 1 +) + +// ModificationType custom type to indicate file modification type +type ModificationType string + +const ( + // UPDATE enum representing update operation on a file + UPDATE ModificationType = "update" + // CREATE enum representing create operation for a file/folder + CREATE ModificationType = "create" + // DELETE enum representing delete operation for a file/folder + DELETE ModificationType = "delete" + // APPEND enum representing append operation on a file + APPEND ModificationType = "append" +) + +// FileProperties to contain meta-data of a file like, file/folder name, file/folder parent dir, file type and desired file modification type +type FileProperties struct { + FilePath string + FileParent string + FileType FileType + ModificationType ModificationType +} + +// SimulateFileModifications mock function to simulate requested file/folder operation +// Parameters: +// basePath: The parent directory for file/folder involved in desired file operation +// fileModification: Meta-data of file/folder +// Returns: +// path to file/folder involved in the operation +// error if any or nil +func SimulateFileModifications(basePath string, fileModification FileProperties) (string, error) { + // Files/folders intended to be directly under basepath will be indicated by fileModification.FileParent set to empty string + if fileModification.FileParent != "" { + // If fileModification.FileParent is not empty, use it to generate file/folder absolute path + basePath = filepath.Join(basePath, fileModification.FileParent) + } + + switch fileModification.ModificationType { + case CREATE: + if fileModification.FileType == Directory { + filePath, err := TempMkdir(basePath, fileModification.FilePath) + // t.Logf("In simulateFileModifications, Attempting to create folder %s in %s. Error : %v", fileModification.filePath, basePath, err) + return filePath, err + } else if fileModification.FileType == RegularFile { + folderPath, err := TempMkFile(basePath, fileModification.FilePath) + // t.Logf("In simulateFileModifications, Attempting to create file %s in %s", fileModification.filePath, basePath) + return folderPath, err + } + case DELETE: + if fileModification.FileType == Directory { + return filepath.Join(basePath, fileModification.FilePath), os.RemoveAll(filepath.Join(basePath, fileModification.FilePath)) + } else if fileModification.FileType == RegularFile { + return filepath.Join(basePath, fileModification.FilePath), os.Remove(filepath.Join(basePath, fileModification.FilePath)) + } + case UPDATE: + if fileModification.FileType == Directory { + return "", fmt.Errorf("Updating directory %s is not supported", fileModification.FilePath) + } else if fileModification.FileType == RegularFile { + f, err := os.Open(filepath.Join(basePath, fileModification.FilePath)) + if err != nil { + return "", err + } + if _, err := f.WriteString("Hello from Odo"); err != nil { + return "", err + } + if err := f.Sync(); err != nil { + return "", err + } + if err := f.Close(); err != nil { + return "", err + } + return filepath.Join(basePath, fileModification.FilePath), nil + } + case APPEND: + if fileModification.FileType == RegularFile { + err := ioutil.WriteFile(filepath.Join(basePath, fileModification.FilePath), []byte("// Check watch command"), os.ModeAppend) + if err != nil { + return "", err + } + return filepath.Join(basePath, fileModification.FilePath), nil + } else { + return "", fmt.Errorf("Append not supported for file of type %v", fileModification.FileType) + } + default: + return "", fmt.Errorf("Unsupported file operation %s", fileModification.ModificationType) + } + return "", nil +} + +// CreateDirTree sets up a mock directory tree +// Parameters: +// srcParentPath: The base path where src/dir tree is expected to be rooted +// srcName: Name of the source directory +// requiredFilePaths: list of required sources, their description like whether regularfile/directory, parent directory path of source and desired modification type like update/create/delete/append +// Returns: +// absolute base path of source code +// directory structure containing mappings from desired relative paths to their respective absolute path. +// error if any +func CreateDirTree(srcParentPath string, srcName string, requiredFilePaths []FileProperties) (string, map[string]string, error) { + + if srcParentPath == `~` { + srcParentPath = fixPathForm(srcParentPath, true, srcParentPath) + } + // This is required because ioutil#TempFile and ioutil#TempFolder creates paths with random numeric suffixes. + // So, to be able to refer to the file/folder at any later point in time the created paths returned by ioutil#TempFile or ioutil#TempFolder will need to be saved. + dirTreeMappings := make(map[string]string) + + // Create temporary directory for mock component source code + srcPath, err := TempMkdir(srcParentPath, srcName) + if err != nil { + return "", dirTreeMappings, fmt.Errorf("failed to create dir %s under %s. Error: %v", srcName, srcParentPath, err) + } + dirTreeMappings[srcName] = srcPath + + // For each of the passed(desired) files/folders under component source + for _, fileProperties := range requiredFilePaths { + + // get relative path using file parent and file name passed + relativePath := filepath.Join(fileProperties.FileParent, fileProperties.FilePath) + + // get its absolute path using the mappings preserved from previous creates + if realParentPath, ok := dirTreeMappings[fileProperties.FileParent]; ok { + // real path for the intended file operation is obtained from previously maintained directory tree mappings by joining parent path and file name + realPath := filepath.Join(realParentPath, fileProperties.FilePath) + // Preserve the new paths for further reference + fileProperties.FilePath = filepath.Base(realPath) + fileProperties.FileParent, _ = filepath.Rel(srcPath, filepath.Dir(realPath)) + } + + // Perform mock operation as requested by the parameter + newPath, err := SimulateFileModifications(srcPath, fileProperties) + dirTreeMappings[relativePath] = newPath + if err != nil { + return "", dirTreeMappings, fmt.Errorf("unable to setup test env. Error %v", err) + } + + fileProperties.FilePath = filepath.Base(newPath) + fileProperties.FileParent = filepath.Dir(newPath) + } + + // Return base source path and directory tree mappings + return srcPath, dirTreeMappings, nil +} diff --git a/utils.go b/utils.go index 58b8b79..5d2609e 100644 --- a/utils.go +++ b/utils.go @@ -4,16 +4,37 @@ import ( "os" "path/filepath" "strings" + + homedir "github.com/mitchellh/go-homedir" ) -// fixPathForm changes a file name to a relative name -func fixPathForm(last string, file string) string { +// fixPathForm changes a file name to a relative name in accordance with isExpandLast which if true, +// will have the form of absolute path for cases like `~` which are not recognized by anyone other than +// the shell +func fixPathForm(last string, isExpandLast bool, file string) string { // get wording directory for relative name workDir, err := os.Getwd() if err != nil { return file } + if strings.Contains(last, "..") { + return fixDirPath(file) + } + + if strings.Contains(last, "~") { + if isExpandLast { + path, err := homedir.Expand(last) + if err != nil { + path = last + } + return fixDirPath(path) + } else { + // else here although not required, is added here to stress the orthogonality + // nature of this block with the block above + return unResolveHome(last, file) + } + } abs, err := filepath.Abs(file) if err != nil { return file @@ -37,8 +58,43 @@ func fixPathForm(last string, file string) string { return fixDirPath(rel) } +func unResolveHome(last string, path string) string { + if strings.Contains(last, "~") { + + // Resolve `~` to complete resolvable path + lastEq, _ := homedir.Expand(last) + // Get parent equivalent for cases of partially complete last + lastParentEq, _ := homedir.Expand(filepath.Dir(lastEq)) + + /* + The following might be the possible cases to unresolve absolute path to path completion of path with `~`: + * Case when the last as entered in terminal is partial + ** This is when, parent dir of incompletely entered last, is same as parent dir of possible path + Because, possible path is in this case expected to have contents after resolving the incomplete last on terminal + * Case when the last as entered in terminal is complete + ** Resolved form of complete last is equal to path + This is possible because we include the same directory also as one of the possible auto-completion solutions + ** Resolved form of complete last is equal to parent of resolved auto-completion path + This happens because normally, except for the last itself in the auto-complete suggestions, + all other paths are expected to be only 1 step ahead of last at any given auto-completion step + + According to the cases above, we replace the maximum possible path out of the obtained from passed potential + auto-completion path with the maximum available form of last so that we retain `~` in the suggestions provided + */ + if fixDirPath(lastEq) == fixDirPath(path) { + path = strings.Replace(path, fixDirPath(path), fixDirPath(last), 1) + } else if fixDirPath(lastEq) == fixDirPath(filepath.Dir(path)) { + path = strings.Replace(path, fixDirPath(lastEq), fixDirPath(last), 1) + } else if fixDirPath(lastParentEq) == fixDirPath(filepath.Dir(path)) { + path = strings.Replace(path, fixDirPath(filepath.Dir(lastEq)), fixDirPath(filepath.Dir(last)), 1) + } + } + return path +} + func fixDirPath(path string) string { - info, err := os.Stat(path) + tmpPath, _ := homedir.Expand(path) + info, err := os.Stat(tmpPath) if err == nil && info.IsDir() && !strings.HasSuffix(path, "/") { path += "/" } diff --git a/vendor/github.com/hashicorp/errwrap/LICENSE b/vendor/github.com/hashicorp/errwrap/LICENSE new file mode 100644 index 0000000..c33dcc7 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/errwrap/README.md b/vendor/github.com/hashicorp/errwrap/README.md new file mode 100644 index 0000000..444df08 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/README.md @@ -0,0 +1,89 @@ +# errwrap + +`errwrap` is a package for Go that formalizes the pattern of wrapping errors +and checking if an error contains another error. + +There is a common pattern in Go of taking a returned `error` value and +then wrapping it (such as with `fmt.Errorf`) before returning it. The problem +with this pattern is that you completely lose the original `error` structure. + +Arguably the _correct_ approach is that you should make a custom structure +implementing the `error` interface, and have the original error as a field +on that structure, such [as this example](http://golang.org/pkg/os/#PathError). +This is a good approach, but you have to know the entire chain of possible +rewrapping that happens, when you might just care about one. + +`errwrap` formalizes this pattern (it doesn't matter what approach you use +above) by giving a single interface for wrapping errors, checking if a specific +error is wrapped, and extracting that error. + +## Installation and Docs + +Install using `go get github.com/hashicorp/errwrap`. + +Full documentation is available at +http://godoc.org/github.com/hashicorp/errwrap + +## Usage + +#### Basic Usage + +Below is a very basic example of its usage: + +```go +// A function that always returns an error, but wraps it, like a real +// function might. +func tryOpen() error { + _, err := os.Open("/i/dont/exist") + if err != nil { + return errwrap.Wrapf("Doesn't exist: {{err}}", err) + } + + return nil +} + +func main() { + err := tryOpen() + + // We can use the Contains helpers to check if an error contains + // another error. It is safe to do this with a nil error, or with + // an error that doesn't even use the errwrap package. + if errwrap.Contains(err, "does not exist") { + // Do something + } + if errwrap.ContainsType(err, new(os.PathError)) { + // Do something + } + + // Or we can use the associated `Get` functions to just extract + // a specific error. This would return nil if that specific error doesn't + // exist. + perr := errwrap.GetType(err, new(os.PathError)) +} +``` + +#### Custom Types + +If you're already making custom types that properly wrap errors, then +you can get all the functionality of `errwraps.Contains` and such by +implementing the `Wrapper` interface with just one function. Example: + +```go +type AppError { + Code ErrorCode + Err error +} + +func (e *AppError) WrappedErrors() []error { + return []error{e.Err} +} +``` + +Now this works: + +```go +err := &AppError{Err: fmt.Errorf("an error")} +if errwrap.ContainsType(err, fmt.Errorf("")) { + // This will work! +} +``` diff --git a/vendor/github.com/hashicorp/errwrap/errwrap.go b/vendor/github.com/hashicorp/errwrap/errwrap.go new file mode 100644 index 0000000..a733bef --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/errwrap.go @@ -0,0 +1,169 @@ +// Package errwrap implements methods to formalize error wrapping in Go. +// +// All of the top-level functions that take an `error` are built to be able +// to take any error, not just wrapped errors. This allows you to use errwrap +// without having to type-check and type-cast everywhere. +package errwrap + +import ( + "errors" + "reflect" + "strings" +) + +// WalkFunc is the callback called for Walk. +type WalkFunc func(error) + +// Wrapper is an interface that can be implemented by custom types to +// have all the Contains, Get, etc. functions in errwrap work. +// +// When Walk reaches a Wrapper, it will call the callback for every +// wrapped error in addition to the wrapper itself. Since all the top-level +// functions in errwrap use Walk, this means that all those functions work +// with your custom type. +type Wrapper interface { + WrappedErrors() []error +} + +// Wrap defines that outer wraps inner, returning an error type that +// can be cleanly used with the other methods in this package, such as +// Contains, GetAll, etc. +// +// This function won't modify the error message at all (the outer message +// will be used). +func Wrap(outer, inner error) error { + return &wrappedError{ + Outer: outer, + Inner: inner, + } +} + +// Wrapf wraps an error with a formatting message. This is similar to using +// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap +// errors, you should replace it with this. +// +// format is the format of the error message. The string '{{err}}' will +// be replaced with the original error message. +func Wrapf(format string, err error) error { + outerMsg := "" + if err != nil { + outerMsg = err.Error() + } + + outer := errors.New(strings.Replace( + format, "{{err}}", outerMsg, -1)) + + return Wrap(outer, err) +} + +// Contains checks if the given error contains an error with the +// message msg. If err is not a wrapped error, this will always return +// false unless the error itself happens to match this msg. +func Contains(err error, msg string) bool { + return len(GetAll(err, msg)) > 0 +} + +// ContainsType checks if the given error contains an error with +// the same concrete type as v. If err is not a wrapped error, this will +// check the err itself. +func ContainsType(err error, v interface{}) bool { + return len(GetAllType(err, v)) > 0 +} + +// Get is the same as GetAll but returns the deepest matching error. +func Get(err error, msg string) error { + es := GetAll(err, msg) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetType is the same as GetAllType but returns the deepest matching error. +func GetType(err error, v interface{}) error { + es := GetAllType(err, v) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetAll gets all the errors that might be wrapped in err with the +// given message. The order of the errors is such that the outermost +// matching error (the most recent wrap) is index zero, and so on. +func GetAll(err error, msg string) []error { + var result []error + + Walk(err, func(err error) { + if err.Error() == msg { + result = append(result, err) + } + }) + + return result +} + +// GetAllType gets all the errors that are the same type as v. +// +// The order of the return value is the same as described in GetAll. +func GetAllType(err error, v interface{}) []error { + var result []error + + var search string + if v != nil { + search = reflect.TypeOf(v).String() + } + Walk(err, func(err error) { + var needle string + if err != nil { + needle = reflect.TypeOf(err).String() + } + + if needle == search { + result = append(result, err) + } + }) + + return result +} + +// Walk walks all the wrapped errors in err and calls the callback. If +// err isn't a wrapped error, this will be called once for err. If err +// is a wrapped error, the callback will be called for both the wrapper +// that implements error as well as the wrapped error itself. +func Walk(err error, cb WalkFunc) { + if err == nil { + return + } + + switch e := err.(type) { + case *wrappedError: + cb(e.Outer) + Walk(e.Inner, cb) + case Wrapper: + cb(err) + + for _, err := range e.WrappedErrors() { + Walk(err, cb) + } + default: + cb(err) + } +} + +// wrappedError is an implementation of error that has both the +// outer and inner errors. +type wrappedError struct { + Outer error + Inner error +} + +func (w *wrappedError) Error() string { + return w.Outer.Error() +} + +func (w *wrappedError) WrappedErrors() []error { + return []error{w.Outer, w.Inner} +} diff --git a/vendor/github.com/hashicorp/errwrap/errwrap_test.go b/vendor/github.com/hashicorp/errwrap/errwrap_test.go new file mode 100644 index 0000000..5ae5f8e --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/errwrap_test.go @@ -0,0 +1,94 @@ +package errwrap + +import ( + "fmt" + "testing" +) + +func TestWrappedError_impl(t *testing.T) { + var _ error = new(wrappedError) +} + +func TestGetAll(t *testing.T) { + cases := []struct { + Err error + Msg string + Len int + }{ + {}, + { + fmt.Errorf("foo"), + "foo", + 1, + }, + { + fmt.Errorf("bar"), + "foo", + 0, + }, + { + Wrapf("bar", fmt.Errorf("foo")), + "foo", + 1, + }, + { + Wrapf("{{err}}", fmt.Errorf("foo")), + "foo", + 2, + }, + { + Wrapf("bar", Wrapf("baz", fmt.Errorf("foo"))), + "foo", + 1, + }, + } + + for i, tc := range cases { + actual := GetAll(tc.Err, tc.Msg) + if len(actual) != tc.Len { + t.Fatalf("%d: bad: %#v", i, actual) + } + for _, v := range actual { + if v.Error() != tc.Msg { + t.Fatalf("%d: bad: %#v", i, actual) + } + } + } +} + +func TestGetAllType(t *testing.T) { + cases := []struct { + Err error + Type interface{} + Len int + }{ + {}, + { + fmt.Errorf("foo"), + "foo", + 0, + }, + { + fmt.Errorf("bar"), + fmt.Errorf("foo"), + 1, + }, + { + Wrapf("bar", fmt.Errorf("foo")), + fmt.Errorf("baz"), + 2, + }, + { + Wrapf("bar", Wrapf("baz", fmt.Errorf("foo"))), + Wrapf("", nil), + 0, + }, + } + + for i, tc := range cases { + actual := GetAllType(tc.Err, tc.Type) + if len(actual) != tc.Len { + t.Fatalf("%d: bad: %#v", i, actual) + } + } +} diff --git a/vendor/github.com/hashicorp/errwrap/go.mod b/vendor/github.com/hashicorp/errwrap/go.mod new file mode 100644 index 0000000..c9b8402 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/go.mod @@ -0,0 +1 @@ +module github.com/hashicorp/errwrap diff --git a/vendor/github.com/hashicorp/go-multierror/.travis.yml b/vendor/github.com/hashicorp/go-multierror/.travis.yml new file mode 100644 index 0000000..304a835 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/.travis.yml @@ -0,0 +1,12 @@ +sudo: false + +language: go + +go: + - 1.x + +branches: + only: + - master + +script: make test testrace diff --git a/vendor/github.com/hashicorp/go-multierror/LICENSE b/vendor/github.com/hashicorp/go-multierror/LICENSE new file mode 100644 index 0000000..82b4de9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/LICENSE @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/go-multierror/Makefile b/vendor/github.com/hashicorp/go-multierror/Makefile new file mode 100644 index 0000000..b97cd6e --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/Makefile @@ -0,0 +1,31 @@ +TEST?=./... + +default: test + +# test runs the test suite and vets the code. +test: generate + @echo "==> Running tests..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS} + +# testrace runs the race checker +testrace: generate + @echo "==> Running tests (race)..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -race ${TESTARGS} + +# updatedeps installs all the dependencies needed to run and build. +updatedeps: + @sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'" + +# generate runs `go generate` to build the dynamically generated source files. +generate: + @echo "==> Generating..." + @find . -type f -name '.DS_Store' -delete + @go list ./... \ + | grep -v "/vendor/" \ + | xargs -n1 go generate + +.PHONY: default test testrace updatedeps generate diff --git a/vendor/github.com/hashicorp/go-multierror/README.md b/vendor/github.com/hashicorp/go-multierror/README.md new file mode 100644 index 0000000..ead5830 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/README.md @@ -0,0 +1,97 @@ +# go-multierror + +[![Build Status](http://img.shields.io/travis/hashicorp/go-multierror.svg?style=flat-square)][travis] +[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs] + +[travis]: https://travis-ci.org/hashicorp/go-multierror +[godocs]: https://godoc.org/github.com/hashicorp/go-multierror + +`go-multierror` is a package for Go that provides a mechanism for +representing a list of `error` values as a single `error`. + +This allows a function in Go to return an `error` that might actually +be a list of errors. If the caller knows this, they can unwrap the +list and access the errors. If the caller doesn't know, the error +formats to a nice human-readable format. + +`go-multierror` implements the +[errwrap](https://github.com/hashicorp/errwrap) interface so that it can +be used with that library, as well. + +## Installation and Docs + +Install using `go get github.com/hashicorp/go-multierror`. + +Full documentation is available at +http://godoc.org/github.com/hashicorp/go-multierror + +## Usage + +go-multierror is easy to use and purposely built to be unobtrusive in +existing Go applications/libraries that may not be aware of it. + +**Building a list of errors** + +The `Append` function is used to create a list of errors. This function +behaves a lot like the Go built-in `append` function: it doesn't matter +if the first argument is nil, a `multierror.Error`, or any other `error`, +the function behaves as you would expect. + +```go +var result error + +if err := step1(); err != nil { + result = multierror.Append(result, err) +} +if err := step2(); err != nil { + result = multierror.Append(result, err) +} + +return result +``` + +**Customizing the formatting of the errors** + +By specifying a custom `ErrorFormat`, you can customize the format +of the `Error() string` function: + +```go +var result *multierror.Error + +// ... accumulate errors here, maybe using Append + +if result != nil { + result.ErrorFormat = func([]error) string { + return "errors!" + } +} +``` + +**Accessing the list of errors** + +`multierror.Error` implements `error` so if the caller doesn't know about +multierror, it will work just fine. But if you're aware a multierror might +be returned, you can use type switches to access the list of errors: + +```go +if err := something(); err != nil { + if merr, ok := err.(*multierror.Error); ok { + // Use merr.Errors + } +} +``` + +**Returning a multierror only if there are errors** + +If you build a `multierror.Error`, you can use the `ErrorOrNil` function +to return an `error` implementation only if there are errors to return: + +```go +var result *multierror.Error + +// ... accumulate errors here + +// Return the `error` only if errors were added to the multierror, otherwise +// return nil since there are no errors. +return result.ErrorOrNil() +``` diff --git a/vendor/github.com/hashicorp/go-multierror/append.go b/vendor/github.com/hashicorp/go-multierror/append.go new file mode 100644 index 0000000..775b6e7 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/append.go @@ -0,0 +1,41 @@ +package multierror + +// Append is a helper function that will append more errors +// onto an Error in order to create a larger multi-error. +// +// If err is not a multierror.Error, then it will be turned into +// one. If any of the errs are multierr.Error, they will be flattened +// one level into err. +func Append(err error, errs ...error) *Error { + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Go through each error and flatten + for _, e := range errs { + switch e := e.(type) { + case *Error: + if e != nil { + err.Errors = append(err.Errors, e.Errors...) + } + default: + if e != nil { + err.Errors = append(err.Errors, e) + } + } + } + + return err + default: + newErrs := make([]error, 0, len(errs)+1) + if err != nil { + newErrs = append(newErrs, err) + } + newErrs = append(newErrs, errs...) + + return Append(&Error{}, newErrs...) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/append_test.go b/vendor/github.com/hashicorp/go-multierror/append_test.go new file mode 100644 index 0000000..58ddafa --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/append_test.go @@ -0,0 +1,82 @@ +package multierror + +import ( + "errors" + "testing" +) + +func TestAppend_Error(t *testing.T) { + original := &Error{ + Errors: []error{errors.New("foo")}, + } + + result := Append(original, errors.New("bar")) + if len(result.Errors) != 2 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } + + original = &Error{} + result = Append(original, errors.New("bar")) + if len(result.Errors) != 1 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } + + // Test when a typed nil is passed + var e *Error + result = Append(e, errors.New("baz")) + if len(result.Errors) != 1 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } + + // Test flattening + original = &Error{ + Errors: []error{errors.New("foo")}, + } + + result = Append(original, Append(nil, errors.New("foo"), errors.New("bar"))) + if len(result.Errors) != 3 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } +} + +func TestAppend_NilError(t *testing.T) { + var err error + result := Append(err, errors.New("bar")) + if len(result.Errors) != 1 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } +} + +func TestAppend_NilErrorArg(t *testing.T) { + var err error + var nilErr *Error + result := Append(err, nilErr) + if len(result.Errors) != 0 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } +} + +func TestAppend_NilErrorIfaceArg(t *testing.T) { + var err error + var nilErr error + result := Append(err, nilErr) + if len(result.Errors) != 0 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } +} + +func TestAppend_NonError(t *testing.T) { + original := errors.New("foo") + result := Append(original, errors.New("bar")) + if len(result.Errors) != 2 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } +} + +func TestAppend_NonError_Error(t *testing.T) { + original := errors.New("foo") + result := Append(original, Append(nil, errors.New("bar"))) + if len(result.Errors) != 2 { + t.Fatalf("wrong len: %d", len(result.Errors)) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/flatten.go b/vendor/github.com/hashicorp/go-multierror/flatten.go new file mode 100644 index 0000000..aab8e9a --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/flatten.go @@ -0,0 +1,26 @@ +package multierror + +// Flatten flattens the given error, merging any *Errors together into +// a single *Error. +func Flatten(err error) error { + // If it isn't an *Error, just return the error as-is + if _, ok := err.(*Error); !ok { + return err + } + + // Otherwise, make the result and flatten away! + flatErr := new(Error) + flatten(err, flatErr) + return flatErr +} + +func flatten(err error, flatErr *Error) { + switch err := err.(type) { + case *Error: + for _, e := range err.Errors { + flatten(e, flatErr) + } + default: + flatErr.Errors = append(flatErr.Errors, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/flatten_test.go b/vendor/github.com/hashicorp/go-multierror/flatten_test.go new file mode 100644 index 0000000..e99c410 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/flatten_test.go @@ -0,0 +1,46 @@ +package multierror + +import ( + "errors" + "fmt" + "reflect" + "testing" +) + +func TestFlatten(t *testing.T) { + original := &Error{ + Errors: []error{ + errors.New("one"), + &Error{ + Errors: []error{ + errors.New("two"), + &Error{ + Errors: []error{ + errors.New("three"), + }, + }, + }, + }, + }, + } + + expected := `3 errors occurred: + * one + * two + * three + +` + actual := fmt.Sprintf("%s", Flatten(original)) + + if expected != actual { + t.Fatalf("expected: %s, got: %s", expected, actual) + } +} + +func TestFlatten_nonError(t *testing.T) { + err := errors.New("foo") + actual := Flatten(err) + if !reflect.DeepEqual(actual, err) { + t.Fatalf("bad: %#v", actual) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/format.go b/vendor/github.com/hashicorp/go-multierror/format.go new file mode 100644 index 0000000..47f13c4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/format.go @@ -0,0 +1,27 @@ +package multierror + +import ( + "fmt" + "strings" +) + +// ErrorFormatFunc is a function callback that is called by Error to +// turn the list of errors into a string. +type ErrorFormatFunc func([]error) string + +// ListFormatFunc is a basic formatter that outputs the number of errors +// that occurred along with a bullet point list of the errors. +func ListFormatFunc(es []error) string { + if len(es) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) + } + + points := make([]string, len(es)) + for i, err := range es { + points[i] = fmt.Sprintf("* %s", err) + } + + return fmt.Sprintf( + "%d errors occurred:\n\t%s\n\n", + len(es), strings.Join(points, "\n\t")) +} diff --git a/vendor/github.com/hashicorp/go-multierror/format_test.go b/vendor/github.com/hashicorp/go-multierror/format_test.go new file mode 100644 index 0000000..2b6da1d --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/format_test.go @@ -0,0 +1,40 @@ +package multierror + +import ( + "errors" + "testing" +) + +func TestListFormatFuncSingle(t *testing.T) { + expected := `1 error occurred: + * foo + +` + + errors := []error{ + errors.New("foo"), + } + + actual := ListFormatFunc(errors) + if actual != expected { + t.Fatalf("bad: %#v", actual) + } +} + +func TestListFormatFuncMultiple(t *testing.T) { + expected := `2 errors occurred: + * foo + * bar + +` + + errors := []error{ + errors.New("foo"), + errors.New("bar"), + } + + actual := ListFormatFunc(errors) + if actual != expected { + t.Fatalf("bad: %#v", actual) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/go.mod b/vendor/github.com/hashicorp/go-multierror/go.mod new file mode 100644 index 0000000..2534331 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/go.mod @@ -0,0 +1,3 @@ +module github.com/hashicorp/go-multierror + +require github.com/hashicorp/errwrap v1.0.0 diff --git a/vendor/github.com/hashicorp/go-multierror/go.sum b/vendor/github.com/hashicorp/go-multierror/go.sum new file mode 100644 index 0000000..85b1f8f --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/go.sum @@ -0,0 +1,4 @@ +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go new file mode 100644 index 0000000..89b1422 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/multierror.go @@ -0,0 +1,51 @@ +package multierror + +import ( + "fmt" +) + +// Error is an error type to track multiple errors. This is used to +// accumulate errors in cases and return them as a single "error". +type Error struct { + Errors []error + ErrorFormat ErrorFormatFunc +} + +func (e *Error) Error() string { + fn := e.ErrorFormat + if fn == nil { + fn = ListFormatFunc + } + + return fn(e.Errors) +} + +// ErrorOrNil returns an error interface if this Error represents +// a list of errors, or returns nil if the list of errors is empty. This +// function is useful at the end of accumulation to make sure that the value +// returned represents the existence of errors. +func (e *Error) ErrorOrNil() error { + if e == nil { + return nil + } + if len(e.Errors) == 0 { + return nil + } + + return e +} + +func (e *Error) GoString() string { + return fmt.Sprintf("*%#v", *e) +} + +// WrappedErrors returns the list of errors that this Error is wrapping. +// It is an implementation of the errwrap.Wrapper interface so that +// multierror.Error can be used with that library. +// +// This method is not safe to be called concurrently and is no different +// than accessing the Errors field directly. It is implemented only to +// satisfy the errwrap.Wrapper interface. +func (e *Error) WrappedErrors() []error { + return e.Errors +} diff --git a/vendor/github.com/hashicorp/go-multierror/multierror_test.go b/vendor/github.com/hashicorp/go-multierror/multierror_test.go new file mode 100644 index 0000000..2949c3b --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/multierror_test.go @@ -0,0 +1,71 @@ +package multierror + +import ( + "errors" + "reflect" + "testing" +) + +func TestError_Impl(t *testing.T) { + var _ error = new(Error) +} + +func TestErrorError_custom(t *testing.T) { + errors := []error{ + errors.New("foo"), + errors.New("bar"), + } + + fn := func(es []error) string { + return "foo" + } + + multi := &Error{Errors: errors, ErrorFormat: fn} + if multi.Error() != "foo" { + t.Fatalf("bad: %s", multi.Error()) + } +} + +func TestErrorError_default(t *testing.T) { + expected := `2 errors occurred: + * foo + * bar + +` + + errors := []error{ + errors.New("foo"), + errors.New("bar"), + } + + multi := &Error{Errors: errors} + if multi.Error() != expected { + t.Fatalf("bad: %s", multi.Error()) + } +} + +func TestErrorErrorOrNil(t *testing.T) { + err := new(Error) + if err.ErrorOrNil() != nil { + t.Fatalf("bad: %#v", err.ErrorOrNil()) + } + + err.Errors = []error{errors.New("foo")} + if v := err.ErrorOrNil(); v == nil { + t.Fatal("should not be nil") + } else if !reflect.DeepEqual(v, err) { + t.Fatalf("bad: %#v", v) + } +} + +func TestErrorWrappedErrors(t *testing.T) { + errors := []error{ + errors.New("foo"), + errors.New("bar"), + } + + multi := &Error{Errors: errors} + if !reflect.DeepEqual(multi.Errors, multi.WrappedErrors()) { + t.Fatalf("bad: %s", multi.WrappedErrors()) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/prefix.go b/vendor/github.com/hashicorp/go-multierror/prefix.go new file mode 100644 index 0000000..5c477ab --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/prefix.go @@ -0,0 +1,37 @@ +package multierror + +import ( + "fmt" + + "github.com/hashicorp/errwrap" +) + +// Prefix is a helper function that will prefix some text +// to the given error. If the error is a multierror.Error, then +// it will be prefixed to each wrapped error. +// +// This is useful to use when appending multiple multierrors +// together in order to give better scoping. +func Prefix(err error, prefix string) error { + if err == nil { + return nil + } + + format := fmt.Sprintf("%s {{err}}", prefix) + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Wrap each of the errors + for i, e := range err.Errors { + err.Errors[i] = errwrap.Wrapf(format, e) + } + + return err + default: + return errwrap.Wrapf(format, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/prefix_test.go b/vendor/github.com/hashicorp/go-multierror/prefix_test.go new file mode 100644 index 0000000..1d4a6f6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/prefix_test.go @@ -0,0 +1,33 @@ +package multierror + +import ( + "errors" + "testing" +) + +func TestPrefix_Error(t *testing.T) { + original := &Error{ + Errors: []error{errors.New("foo")}, + } + + result := Prefix(original, "bar") + if result.(*Error).Errors[0].Error() != "bar foo" { + t.Fatalf("bad: %s", result) + } +} + +func TestPrefix_NilError(t *testing.T) { + var err error + result := Prefix(err, "bar") + if result != nil { + t.Fatalf("bad: %#v", result) + } +} + +func TestPrefix_NonError(t *testing.T) { + original := errors.New("foo") + result := Prefix(original, "bar") + if result.Error() != "bar foo" { + t.Fatalf("bad: %s", result) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/sort.go b/vendor/github.com/hashicorp/go-multierror/sort.go new file mode 100644 index 0000000..fecb14e --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/sort.go @@ -0,0 +1,16 @@ +package multierror + +// Len implements sort.Interface function for length +func (err Error) Len() int { + return len(err.Errors) +} + +// Swap implements sort.Interface function for swapping elements +func (err Error) Swap(i, j int) { + err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] +} + +// Less implements sort.Interface function for determining order +func (err Error) Less(i, j int) bool { + return err.Errors[i].Error() < err.Errors[j].Error() +} diff --git a/vendor/github.com/hashicorp/go-multierror/sort_test.go b/vendor/github.com/hashicorp/go-multierror/sort_test.go new file mode 100644 index 0000000..7fd04e8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/sort_test.go @@ -0,0 +1,52 @@ +package multierror + +import ( + "errors" + "reflect" + "sort" + "testing" +) + +func TestSortSingle(t *testing.T) { + errFoo := errors.New("foo") + + expected := []error{ + errFoo, + } + + err := &Error{ + Errors: []error{ + errFoo, + }, + } + + sort.Sort(err) + if !reflect.DeepEqual(err.Errors, expected) { + t.Fatalf("bad: %#v", err) + } +} + +func TestSortMultiple(t *testing.T) { + errBar := errors.New("bar") + errBaz := errors.New("baz") + errFoo := errors.New("foo") + + expected := []error{ + errBar, + errBaz, + errFoo, + } + + err := &Error{ + Errors: []error{ + errFoo, + errBar, + errBaz, + }, + } + + sort.Sort(err) + if !reflect.DeepEqual(err.Errors, expected) { + t.Fatalf("bad: %#v", err) + } +} diff --git a/vendor/github.com/mitchellh/go-homedir/LICENSE b/vendor/github.com/mitchellh/go-homedir/LICENSE new file mode 100644 index 0000000..f9c841a --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +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/vendor/github.com/mitchellh/go-homedir/README.md b/vendor/github.com/mitchellh/go-homedir/README.md new file mode 100644 index 0000000..d70706d --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/README.md @@ -0,0 +1,14 @@ +# go-homedir + +This is a Go library for detecting the user's home directory without +the use of cgo, so the library can be used in cross-compilation environments. + +Usage is incredibly simple, just call `homedir.Dir()` to get the home directory +for a user, and `homedir.Expand()` to expand the `~` in a path to the home +directory. + +**Why not just use `os/user`?** The built-in `os/user` package requires +cgo on Darwin systems. This means that any Go code that uses that package +cannot cross compile. But 99% of the time the use for `os/user` is just to +retrieve the home directory, which we can do for the current user without +cgo. This library does that, enabling cross-compilation. diff --git a/vendor/github.com/mitchellh/go-homedir/go.mod b/vendor/github.com/mitchellh/go-homedir/go.mod new file mode 100644 index 0000000..7efa09a --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/go.mod @@ -0,0 +1 @@ +module github.com/mitchellh/go-homedir diff --git a/vendor/github.com/mitchellh/go-homedir/homedir.go b/vendor/github.com/mitchellh/go-homedir/homedir.go new file mode 100644 index 0000000..fb87bef --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/homedir.go @@ -0,0 +1,157 @@ +package homedir + +import ( + "bytes" + "errors" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" +) + +// DisableCache will disable caching of the home directory. Caching is enabled +// by default. +var DisableCache bool + +var homedirCache string +var cacheLock sync.RWMutex + +// Dir returns the home directory for the executing user. +// +// This uses an OS-specific method for discovering the home directory. +// An error is returned if a home directory cannot be detected. +func Dir() (string, error) { + if !DisableCache { + cacheLock.RLock() + cached := homedirCache + cacheLock.RUnlock() + if cached != "" { + return cached, nil + } + } + + cacheLock.Lock() + defer cacheLock.Unlock() + + var result string + var err error + if runtime.GOOS == "windows" { + result, err = dirWindows() + } else { + // Unix-like system, so just assume Unix + result, err = dirUnix() + } + + if err != nil { + return "", err + } + homedirCache = result + return result, nil +} + +// Expand expands the path to include the home directory if the path +// is prefixed with `~`. If it isn't prefixed with `~`, the path is +// returned as-is. +func Expand(path string) (string, error) { + if len(path) == 0 { + return path, nil + } + + if path[0] != '~' { + return path, nil + } + + if len(path) > 1 && path[1] != '/' && path[1] != '\\' { + return "", errors.New("cannot expand user-specific home dir") + } + + dir, err := Dir() + if err != nil { + return "", err + } + + return filepath.Join(dir, path[1:]), nil +} + +func dirUnix() (string, error) { + homeEnv := "HOME" + if runtime.GOOS == "plan9" { + // On plan9, env vars are lowercase. + homeEnv = "home" + } + + // First prefer the HOME environmental variable + if home := os.Getenv(homeEnv); home != "" { + return home, nil + } + + var stdout bytes.Buffer + + // If that fails, try OS specific commands + if runtime.GOOS == "darwin" { + cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) + cmd.Stdout = &stdout + if err := cmd.Run(); err == nil { + result := strings.TrimSpace(stdout.String()) + if result != "" { + return result, nil + } + } + } else { + cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + // If the error is ErrNotFound, we ignore it. Otherwise, return it. + if err != exec.ErrNotFound { + return "", err + } + } else { + if passwd := strings.TrimSpace(stdout.String()); passwd != "" { + // username:password:uid:gid:gecos:home:shell + passwdParts := strings.SplitN(passwd, ":", 7) + if len(passwdParts) > 5 { + return passwdParts[5], nil + } + } + } + } + + // If all else fails, try the shell + stdout.Reset() + cmd := exec.Command("sh", "-c", "cd && pwd") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + result := strings.TrimSpace(stdout.String()) + if result == "" { + return "", errors.New("blank output when reading home directory") + } + + return result, nil +} + +func dirWindows() (string, error) { + // First prefer the HOME environmental variable + if home := os.Getenv("HOME"); home != "" { + return home, nil + } + + // Prefer standard environment variable USERPROFILE + if home := os.Getenv("USERPROFILE"); home != "" { + return home, nil + } + + drive := os.Getenv("HOMEDRIVE") + path := os.Getenv("HOMEPATH") + home := drive + path + if drive == "" || path == "" { + return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") + } + + return home, nil +} diff --git a/vendor/github.com/mitchellh/go-homedir/homedir_test.go b/vendor/github.com/mitchellh/go-homedir/homedir_test.go new file mode 100644 index 0000000..cd52127 --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/homedir_test.go @@ -0,0 +1,129 @@ +package homedir + +import ( + "os" + "os/user" + "path/filepath" + "testing" +) + +func patchEnv(key, value string) func() { + bck := os.Getenv(key) + deferFunc := func() { + os.Setenv(key, bck) + } + + if value != "" { + os.Setenv(key, value) + } else { + os.Unsetenv(key) + } + + return deferFunc +} + +func BenchmarkDir(b *testing.B) { + // We do this for any "warmups" + for i := 0; i < 10; i++ { + Dir() + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Dir() + } +} + +func TestDir(t *testing.T) { + u, err := user.Current() + if err != nil { + t.Fatalf("err: %s", err) + } + + dir, err := Dir() + if err != nil { + t.Fatalf("err: %s", err) + } + + if u.HomeDir != dir { + t.Fatalf("%#v != %#v", u.HomeDir, dir) + } + + DisableCache = true + defer func() { DisableCache = false }() + defer patchEnv("HOME", "")() + dir, err = Dir() + if err != nil { + t.Fatalf("err: %s", err) + } + + if u.HomeDir != dir { + t.Fatalf("%#v != %#v", u.HomeDir, dir) + } +} + +func TestExpand(t *testing.T) { + u, err := user.Current() + if err != nil { + t.Fatalf("err: %s", err) + } + + cases := []struct { + Input string + Output string + Err bool + }{ + { + "/foo", + "/foo", + false, + }, + + { + "~/foo", + filepath.Join(u.HomeDir, "foo"), + false, + }, + + { + "", + "", + false, + }, + + { + "~", + u.HomeDir, + false, + }, + + { + "~foo/foo", + "", + true, + }, + } + + for _, tc := range cases { + actual, err := Expand(tc.Input) + if (err != nil) != tc.Err { + t.Fatalf("Input: %#v\n\nErr: %s", tc.Input, err) + } + + if actual != tc.Output { + t.Fatalf("Input: %#v\n\nOutput: %#v", tc.Input, actual) + } + } + + DisableCache = true + defer func() { DisableCache = false }() + defer patchEnv("HOME", "/custom/path/")() + expected := filepath.Join("/", "custom", "path", "foo/bar") + actual, err := Expand("~/foo/bar") + + if err != nil { + t.Errorf("No error is expected, got: %v", err) + } else if actual != expected { + t.Errorf("Expected: %v; actual: %v", expected, actual) + } +}