path.go (5445B)
1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // Package path provides template functions for manipulating paths. 15 package path 16 17 import ( 18 "fmt" 19 _path "path" 20 "path/filepath" 21 "strings" 22 23 "github.com/gohugoio/hugo/deps" 24 "github.com/spf13/cast" 25 ) 26 27 // New returns a new instance of the path-namespaced template functions. 28 func New(deps *deps.Deps) *Namespace { 29 return &Namespace{ 30 deps: deps, 31 } 32 } 33 34 // Namespace provides template functions for the "os" namespace. 35 type Namespace struct { 36 deps *deps.Deps 37 } 38 39 // DirFile holds the result from path.Split. 40 type DirFile struct { 41 Dir string 42 File string 43 } 44 45 // Used in test. 46 func (df DirFile) String() string { 47 return fmt.Sprintf("%s|%s", df.Dir, df.File) 48 } 49 50 // Ext returns the file name extension used by path. 51 // The extension is the suffix beginning at the final dot 52 // in the final slash-separated element of path; 53 // it is empty if there is no dot. 54 // The input path is passed into filepath.ToSlash converting any Windows slashes 55 // to forward slashes. 56 func (ns *Namespace) Ext(path any) (string, error) { 57 spath, err := cast.ToStringE(path) 58 if err != nil { 59 return "", err 60 } 61 spath = filepath.ToSlash(spath) 62 return _path.Ext(spath), nil 63 } 64 65 // Dir returns all but the last element of path, typically the path's directory. 66 // After dropping the final element using Split, the path is Cleaned and trailing 67 // slashes are removed. 68 // If the path is empty, Dir returns ".". 69 // If the path consists entirely of slashes followed by non-slash bytes, Dir 70 // returns a single slash. In any other case, the returned path does not end in a 71 // slash. 72 // The input path is passed into filepath.ToSlash converting any Windows slashes 73 // to forward slashes. 74 func (ns *Namespace) Dir(path any) (string, error) { 75 spath, err := cast.ToStringE(path) 76 if err != nil { 77 return "", err 78 } 79 spath = filepath.ToSlash(spath) 80 return _path.Dir(spath), nil 81 } 82 83 // Base returns the last element of path. 84 // Trailing slashes are removed before extracting the last element. 85 // If the path is empty, Base returns ".". 86 // If the path consists entirely of slashes, Base returns "/". 87 // The input path is passed into filepath.ToSlash converting any Windows slashes 88 // to forward slashes. 89 func (ns *Namespace) Base(path any) (string, error) { 90 spath, err := cast.ToStringE(path) 91 if err != nil { 92 return "", err 93 } 94 spath = filepath.ToSlash(spath) 95 return _path.Base(spath), nil 96 } 97 98 // BaseName returns the last element of path, removing the extension if present. 99 // Trailing slashes are removed before extracting the last element. 100 // If the path is empty, Base returns ".". 101 // If the path consists entirely of slashes, Base returns "/". 102 // The input path is passed into filepath.ToSlash converting any Windows slashes 103 // to forward slashes. 104 func (ns *Namespace) BaseName(path any) (string, error) { 105 spath, err := cast.ToStringE(path) 106 if err != nil { 107 return "", err 108 } 109 spath = filepath.ToSlash(spath) 110 return strings.TrimSuffix(_path.Base(spath), _path.Ext(spath)), nil 111 } 112 113 // Split splits path immediately following the final slash, 114 // separating it into a directory and file name component. 115 // If there is no slash in path, Split returns an empty dir and 116 // file set to path. 117 // The input path is passed into filepath.ToSlash converting any Windows slashes 118 // to forward slashes. 119 // The returned values have the property that path = dir+file. 120 func (ns *Namespace) Split(path any) (DirFile, error) { 121 spath, err := cast.ToStringE(path) 122 if err != nil { 123 return DirFile{}, err 124 } 125 spath = filepath.ToSlash(spath) 126 dir, file := _path.Split(spath) 127 128 return DirFile{Dir: dir, File: file}, nil 129 } 130 131 // Join joins any number of path elements into a single path, adding a 132 // separating slash if necessary. All the input 133 // path elements are passed into filepath.ToSlash converting any Windows slashes 134 // to forward slashes. 135 // The result is Cleaned; in particular, 136 // all empty strings are ignored. 137 func (ns *Namespace) Join(elements ...any) (string, error) { 138 var pathElements []string 139 for _, elem := range elements { 140 switch v := elem.(type) { 141 case []string: 142 for _, e := range v { 143 pathElements = append(pathElements, filepath.ToSlash(e)) 144 } 145 case []any: 146 for _, e := range v { 147 elemStr, err := cast.ToStringE(e) 148 if err != nil { 149 return "", err 150 } 151 pathElements = append(pathElements, filepath.ToSlash(elemStr)) 152 } 153 default: 154 elemStr, err := cast.ToStringE(elem) 155 if err != nil { 156 return "", err 157 } 158 pathElements = append(pathElements, filepath.ToSlash(elemStr)) 159 } 160 } 161 return _path.Join(pathElements...), nil 162 } 163 164 // Clean replaces the separators used with standard slashes and then 165 // extraneous slashes are removed. 166 func (ns *Namespace) Clean(path any) (string, error) { 167 spath, err := cast.ToStringE(path) 168 169 if err != nil { 170 return "", err 171 } 172 spath = filepath.ToSlash(spath) 173 return _path.Clean(spath), nil 174 }