github.com/shohhei1126/hugo@v0.42.2-0.20180623210752-3d5928889ad7/tpl/os/os.go (about)

     1  // Copyright 2017 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 os
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	_os "os"
    20  
    21  	"github.com/gohugoio/hugo/deps"
    22  	"github.com/spf13/afero"
    23  	"github.com/spf13/cast"
    24  )
    25  
    26  // New returns a new instance of the os-namespaced template functions.
    27  func New(deps *deps.Deps) *Namespace {
    28  
    29  	// Since Hugo 0.38 we can have multiple content dirs. This can make it hard to
    30  	// reason about where the file is placed relative to the project root.
    31  	// To make the {{ readFile .Filename }} variant just work, we create a composite
    32  	// filesystem that first checks the work dir fs and then the content fs.
    33  	var rfs afero.Fs
    34  	if deps.Fs != nil {
    35  		rfs = deps.Fs.WorkingDir
    36  		if deps.PathSpec != nil && deps.PathSpec.BaseFs != nil {
    37  			rfs = afero.NewReadOnlyFs(afero.NewCopyOnWriteFs(deps.PathSpec.BaseFs.ContentFs, deps.Fs.WorkingDir))
    38  		}
    39  	}
    40  
    41  	return &Namespace{
    42  		readFileFs: rfs,
    43  		deps:       deps,
    44  	}
    45  }
    46  
    47  // Namespace provides template functions for the "os" namespace.
    48  type Namespace struct {
    49  	readFileFs afero.Fs
    50  	deps       *deps.Deps
    51  }
    52  
    53  // Getenv retrieves the value of the environment variable named by the key.
    54  // It returns the value, which will be empty if the variable is not present.
    55  func (ns *Namespace) Getenv(key interface{}) (string, error) {
    56  	skey, err := cast.ToStringE(key)
    57  	if err != nil {
    58  		return "", nil
    59  	}
    60  
    61  	return _os.Getenv(skey), nil
    62  }
    63  
    64  // readFile reads the file named by filename in the given filesystem
    65  // and returns the contents as a string.
    66  // There is a upper size limit set at 1 megabytes.
    67  func readFile(fs afero.Fs, filename string) (string, error) {
    68  	if filename == "" {
    69  		return "", errors.New("readFile needs a filename")
    70  	}
    71  
    72  	if info, err := fs.Stat(filename); err == nil {
    73  		if info.Size() > 1000000 {
    74  			return "", fmt.Errorf("File %q is too big", filename)
    75  		}
    76  	} else {
    77  		return "", err
    78  	}
    79  	b, err := afero.ReadFile(fs, filename)
    80  
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  
    85  	return string(b), nil
    86  }
    87  
    88  // ReadFile reads the file named by filename relative to the configured WorkingDir.
    89  // It returns the contents as a string.
    90  // There is an upper size limit set at 1 megabytes.
    91  func (ns *Namespace) ReadFile(i interface{}) (string, error) {
    92  	s, err := cast.ToStringE(i)
    93  	if err != nil {
    94  		return "", err
    95  	}
    96  
    97  	return readFile(ns.readFileFs, s)
    98  }
    99  
   100  // ReadDir lists the directory contents relative to the configured WorkingDir.
   101  func (ns *Namespace) ReadDir(i interface{}) ([]_os.FileInfo, error) {
   102  	path, err := cast.ToStringE(i)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	list, err := afero.ReadDir(ns.deps.Fs.WorkingDir, path)
   108  	if err != nil {
   109  		return nil, fmt.Errorf("Failed to read Directory %s with error message %s", path, err)
   110  	}
   111  
   112  	return list, nil
   113  }
   114  
   115  // FileExists checks whether a file exists under the given path.
   116  func (ns *Namespace) FileExists(i interface{}) (bool, error) {
   117  	path, err := cast.ToStringE(i)
   118  	if err != nil {
   119  		return false, err
   120  	}
   121  
   122  	if path == "" {
   123  		return false, errors.New("fileExists needs a path to a file")
   124  	}
   125  
   126  	status, err := afero.Exists(ns.readFileFs, path)
   127  	if err != nil {
   128  		return false, err
   129  	}
   130  
   131  	return status, nil
   132  }