func (manifest *fileManifest) Clean() error {
	return manifest.clean(filepath.Join(manifest.ManifestFolder, manifest.ManifestFile))
}
Clean will recursively remove all files specified in the manifest
package util
import (
	"encoding/gob"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"fmt"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/mattn/go-zglob"
)
func FileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}
func FileNotExists(path string) bool {
	_, err := os.Stat(path)
	return os.IsNotExist(err)
}
func CanonicalPath(path string, basePath string) (string, error) {
	if !filepath.IsAbs(path) {
		path = JoinPath(basePath, path)
	}
	absPath, err := filepath.Abs(path)
	if err != nil {
		return "", err
	}
	return CleanPath(absPath), nil
}
func CanonicalPaths(paths []string, basePath string) ([]string, error) {
	canonicalPaths := []string{}
	for _, path := range paths {
		canonicalPath, err := CanonicalPath(path, basePath)
		if err != nil {
			return canonicalPaths, err
		}
		canonicalPaths = append(canonicalPaths, canonicalPath)
	}
	return canonicalPaths, nil
}
func Grep(regex *regexp.Regexp, glob string) (bool, error) {
	matches, err := zglob.Glob(glob)
	if err != nil {
		return false, errors.WithStackTrace(err)
	}
	for _, match := range matches {
		if IsDir(match) {
			continue
		}
		bytes, err := ioutil.ReadFile(match)
		if err != nil {
			return false, errors.WithStackTrace(err)
		}
		if regex.Match(bytes) {
			return true, nil
		}
	}
	return false, nil
}
func IsDir(path string) bool {
	fileInfo, err := os.Stat(path)
	return err == nil && fileInfo.IsDir()
}
func IsFile(path string) bool {
	fileInfo, err := os.Stat(path)
	return err == nil && !fileInfo.IsDir()
}
func GetPathRelativeTo(path string, basePath string) (string, error) {
	if path == "" {
		path = "."
	}
	if basePath == "" {
		basePath = "."
	}
	inputFolderAbs, err := filepath.Abs(basePath)
	if err != nil {
		return "", errors.WithStackTrace(err)
	}
	fileAbs, err := filepath.Abs(path)
	if err != nil {
		return "", errors.WithStackTrace(err)
	}
	relPath, err := filepath.Rel(inputFolderAbs, fileAbs)
	if err != nil {
		return "", errors.WithStackTrace(err)
	}
	return filepath.ToSlash(relPath), nil
}
func ReadFileAsString(path string) (string, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return "", errors.WithStackTraceAndPrefix(err, "Error reading file at path %s", path)
	}
	return string(bytes), nil
}
func CopyFolderContents(source, destination, manifestFile string) error {
	return CopyFolderContentsWithFilter(source, destination, manifestFile, func(path string) bool {
		return !PathContainsHiddenFileOrFolder(path)
	})
}
func CopyFolderContentsWithFilter(source, destination, manifestFile string, filter func(path string) bool) error {
	if err := os.MkdirAll(destination, 0700); err != nil {
		return errors.WithStackTrace(err)
	}
	manifest := newFileManifest(destination, manifestFile)
	if err := manifest.Clean(); err != nil {
		return errors.WithStackTrace(err)
	}
	if err := manifest.Create(); err != nil {
		return errors.WithStackTrace(err)
	}
	defer manifest.Close()
	files, err := filepath.Glob(fmt.Sprintf("%s/*", source))
	if err != nil {
		return errors.WithStackTrace(err)
	}
	for _, file := range files {
		fileRelativePath, err := GetPathRelativeTo(file, source)
		if err != nil {
			return err
		}
		if !filter(fileRelativePath) {
			continue
		}
		dest := filepath.Join(destination, fileRelativePath)
		if IsDir(file) {
			info, err := os.Lstat(file)
			if err != nil {
				return errors.WithStackTrace(err)
			}
			if err := os.MkdirAll(dest, info.Mode()); err != nil {
				return errors.WithStackTrace(err)
			}
			if err := CopyFolderContentsWithFilter(file, dest, manifestFile, filter); err != nil {
				return err
			}
			if err := manifest.AddDirectory(dest); err != nil {
				return err
			}
		} else {
			parentDir := filepath.Dir(dest)
			if err := os.MkdirAll(parentDir, 0700); err != nil {
				return errors.WithStackTrace(err)
			}
			if err := CopyFile(file, dest); err != nil {
				return err
			}
			if err := manifest.AddFile(dest); err != nil {
				return err
			}
		}
	}
	return nil
}
func IsSymLink(path string) bool {
	fileInfo, err := os.Lstat(path)
	return err == nil && fileInfo.Mode()&os.ModeSymlink != 0
}
func PathContainsHiddenFileOrFolder(path string) bool {
	pathParts := strings.Split(path, string(filepath.Separator))
	for _, pathPart := range pathParts {
		if strings.HasPrefix(pathPart, ".") && pathPart != "." && pathPart != ".." {
			return true
		}
	}
	return false
}
func CopyFile(source string, destination string) error {
	contents, err := ioutil.ReadFile(source)
	if err != nil {
		return errors.WithStackTrace(err)
	}
	return WriteFileWithSamePermissions(source, destination, contents)
}
func WriteFileWithSamePermissions(source string, destination string, contents []byte) error {
	fileInfo, err := os.Stat(source)
	if err != nil {
		return errors.WithStackTrace(err)
	}
	return ioutil.WriteFile(destination, contents, fileInfo.Mode())
}
func JoinPath(elem ...string) string {
	return filepath.ToSlash(filepath.Join(elem...))
}
func CleanPath(path string) string {
	return filepath.ToSlash(filepath.Clean(path))
}
func JoinTerraformModulePath(modulesFolder string, path string) string {
	cleanModulesFolder := strings.TrimRight(modulesFolder, `/\`)
	cleanPath := strings.TrimLeft(path, `/\`)
	return fmt.Sprintf("%s//%s", cleanModulesFolder, cleanPath)
}
type fileManifest struct {
	ManifestFolder string  
	ManifestFile   string  
	encoder        *gob.Encoder
	fileHandle     *os.File
}
type fileManifestEntry struct {
	Path  string
	IsDir bool
}