github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/tpl/path/path.go (about)

     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  	_path "path"
    19  	"path/filepath"
    20  	"strings"
    21  
    22  	"github.com/gohugoio/hugo/common/paths"
    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  // Ext returns the file name extension used by path.
    40  // The extension is the suffix beginning at the final dot
    41  // in the final slash-separated element of path;
    42  // it is empty if there is no dot.
    43  // The input path is passed into filepath.ToSlash converting any Windows slashes
    44  // to forward slashes.
    45  func (ns *Namespace) Ext(path any) (string, error) {
    46  	spath, err := cast.ToStringE(path)
    47  	if err != nil {
    48  		return "", err
    49  	}
    50  	spath = filepath.ToSlash(spath)
    51  	return _path.Ext(spath), nil
    52  }
    53  
    54  // Dir returns all but the last element of path, typically the path's directory.
    55  // After dropping the final element using Split, the path is Cleaned and trailing
    56  // slashes are removed.
    57  // If the path is empty, Dir returns ".".
    58  // If the path consists entirely of slashes followed by non-slash bytes, Dir
    59  // returns a single slash. In any other case, the returned path does not end in a
    60  // slash.
    61  // The input path is passed into filepath.ToSlash converting any Windows slashes
    62  // to forward slashes.
    63  func (ns *Namespace) Dir(path any) (string, error) {
    64  	spath, err := cast.ToStringE(path)
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  	spath = filepath.ToSlash(spath)
    69  	return _path.Dir(spath), nil
    70  }
    71  
    72  // Base returns the last element of path.
    73  // Trailing slashes are removed before extracting the last element.
    74  // If the path is empty, Base returns ".".
    75  // If the path consists entirely of slashes, Base returns "/".
    76  // The input path is passed into filepath.ToSlash converting any Windows slashes
    77  // to forward slashes.
    78  func (ns *Namespace) Base(path any) (string, error) {
    79  	spath, err := cast.ToStringE(path)
    80  	if err != nil {
    81  		return "", err
    82  	}
    83  	spath = filepath.ToSlash(spath)
    84  	return _path.Base(spath), nil
    85  }
    86  
    87  // BaseName returns the last element of path, removing the extension if present.
    88  // Trailing slashes are removed before extracting the last element.
    89  // If the path is empty, Base returns ".".
    90  // If the path consists entirely of slashes, Base returns "/".
    91  // The input path is passed into filepath.ToSlash converting any Windows slashes
    92  // to forward slashes.
    93  func (ns *Namespace) BaseName(path any) (string, error) {
    94  	spath, err := cast.ToStringE(path)
    95  	if err != nil {
    96  		return "", err
    97  	}
    98  	spath = filepath.ToSlash(spath)
    99  	return strings.TrimSuffix(_path.Base(spath), _path.Ext(spath)), nil
   100  }
   101  
   102  // Split splits path immediately following the final slash,
   103  // separating it into a directory and file name component.
   104  // If there is no slash in path, Split returns an empty dir and
   105  // file set to path.
   106  // The input path is passed into filepath.ToSlash converting any Windows slashes
   107  // to forward slashes.
   108  // The returned values have the property that path = dir+file.
   109  func (ns *Namespace) Split(path any) (paths.DirFile, error) {
   110  	spath, err := cast.ToStringE(path)
   111  	if err != nil {
   112  		return paths.DirFile{}, err
   113  	}
   114  	spath = filepath.ToSlash(spath)
   115  	dir, file := _path.Split(spath)
   116  
   117  	return paths.DirFile{Dir: dir, File: file}, nil
   118  }
   119  
   120  // Join joins any number of path elements into a single path, adding a
   121  // separating slash if necessary. All the input
   122  // path elements are passed into filepath.ToSlash converting any Windows slashes
   123  // to forward slashes.
   124  // The result is Cleaned; in particular,
   125  // all empty strings are ignored.
   126  func (ns *Namespace) Join(elements ...any) (string, error) {
   127  	var pathElements []string
   128  	for _, elem := range elements {
   129  		switch v := elem.(type) {
   130  		case []string:
   131  			for _, e := range v {
   132  				pathElements = append(pathElements, filepath.ToSlash(e))
   133  			}
   134  		case []any:
   135  			for _, e := range v {
   136  				elemStr, err := cast.ToStringE(e)
   137  				if err != nil {
   138  					return "", err
   139  				}
   140  				pathElements = append(pathElements, filepath.ToSlash(elemStr))
   141  			}
   142  		default:
   143  			elemStr, err := cast.ToStringE(elem)
   144  			if err != nil {
   145  				return "", err
   146  			}
   147  			pathElements = append(pathElements, filepath.ToSlash(elemStr))
   148  		}
   149  	}
   150  	return _path.Join(pathElements...), nil
   151  }
   152  
   153  // Clean replaces the separators used with standard slashes and then
   154  // extraneous slashes are removed.
   155  func (ns *Namespace) Clean(path any) (string, error) {
   156  	spath, err := cast.ToStringE(path)
   157  
   158  	if err != nil {
   159  		return "", err
   160  	}
   161  	spath = filepath.ToSlash(spath)
   162  	return _path.Clean(spath), nil
   163  }