func IsLocalSource(sourceUrl *url.URL) bool {
	return sourceUrl.Scheme == "file"
}
Returns true if the given URL refers to a path on the local file system
package tfsource
import (
	"fmt"
	"io/ioutil"
	"net/url"
	"regexp"
	"strings"
	"github.com/hashicorp/go-getter"
	urlhelper "github.com/hashicorp/go-getter/helper/url"
	"github.com/sirupsen/logrus"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/util"
)
var forcedRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`)
type TerraformSource struct {
	CanonicalSourceURL *url.URL
	DownloadDir string
	WorkingDir string
	VersionFile string
}
func (src *TerraformSource) String() string {
	return fmt.Sprintf("TerraformSource{CanonicalSourceURL = %v, DownloadDir = %v, WorkingDir = %v, VersionFile = %v}", src.CanonicalSourceURL, src.DownloadDir, src.WorkingDir, src.VersionFile)
}
func (terraformSource TerraformSource) EncodeSourceVersion() string {
	return util.EncodeBase64Sha1(terraformSource.CanonicalSourceURL.Query().Encode())
}
func (terraformSource TerraformSource) WriteVersionFile() error {
	version := terraformSource.EncodeSourceVersion()
	return errors.WithStackTrace(ioutil.WriteFile(terraformSource.VersionFile, []byte(version), 0640))
}
func NewTerraformSource(source string, downloadDir string, workingDir string, logger *logrus.Entry) (*TerraformSource, error) {
	canonicalWorkingDir, err := util.CanonicalPath(workingDir, "")
	if err != nil {
		return nil, err
	}
	canonicalSourceUrl, err := toSourceUrl(source, canonicalWorkingDir)
	if err != nil {
		return nil, err
	}
	rootSourceUrl, modulePath, err := splitSourceUrl(canonicalSourceUrl, logger)
	if err != nil {
		return nil, err
	}
	if IsLocalSource(rootSourceUrl) {
		canonicalFilePath, err := util.CanonicalPath(rootSourceUrl.Path, "")
		if err != nil {
			return nil, err
		}
		rootSourceUrl.Path = canonicalFilePath
	}
	rootPath, err := encodeSourceName(rootSourceUrl)
	if err != nil {
		return nil, err
	}
	encodedWorkingDir := util.EncodeBase64Sha1(canonicalWorkingDir)
	updatedDownloadDir := util.JoinPath(downloadDir, encodedWorkingDir, rootPath)
	updatedWorkingDir := util.JoinPath(updatedDownloadDir, modulePath)
	versionFile := util.JoinPath(updatedDownloadDir, ".terragrunt-source-version")
	return &TerraformSource{
		CanonicalSourceURL: rootSourceUrl,
		DownloadDir:        updatedDownloadDir,
		WorkingDir:         updatedWorkingDir,
		VersionFile:        versionFile,
	}, nil
}
func toSourceUrl(source string, workingDir string) (*url.URL, error) {
	rawSourceUrlWithGetter, err := getter.Detect(source, workingDir, getter.Detectors)
	if err != nil {
		return nil, errors.WithStackTrace(err)
	}
	return parseSourceUrl(rawSourceUrlWithGetter)
}
func parseSourceUrl(source string) (*url.URL, error) {
	forcedGetters := []string{}
	forcedGetter, rawSourceUrl := getForcedGetter(source)
	for forcedGetter != "" {
		forcedGetters = append([]string{forcedGetter}, forcedGetters...)
		forcedGetter, rawSourceUrl = getForcedGetter(rawSourceUrl)
	}
	canonicalSourceUrl, err := urlhelper.Parse(rawSourceUrl)
	if err != nil {
		return nil, errors.WithStackTrace(err)
	}
	for _, forcedGetter := range forcedGetters {
		canonicalSourceUrl.Scheme = fmt.Sprintf("%s::%s", forcedGetter, canonicalSourceUrl.Scheme)
	}
	return canonicalSourceUrl, nil
}