func (in Index) GetFilePath(f IndexFile) string {
	return filepath.Join(filepath.Dir(in.indexFile), filepath.FromSlash(f.File))
}
GetFilePath attempts to get the path of the destination index file as it is stored on disk
package core
import (
	"encoding/hex"
	"errors"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
	"github.com/BurntSushi/toml"
	"github.com/denormal/go-gitignore"
	"github.com/spf13/viper"
	"github.com/vbauerster/mpb/v4"
	"github.com/vbauerster/mpb/v4/decor"
)
type Index struct {
	HashFormat string      `toml:"hash-format"`
	Files      []IndexFile `toml:"files"`
	indexFile  string
}
type IndexFile struct {
	File           string `toml:"file"`
	Hash           string `toml:"hash,omitempty"`
	HashFormat     string `toml:"hash-format,omitempty"`
	Alias          string `toml:"alias,omitempty"`
	MetaFile       bool   `toml:"metafile,omitempty"`  
	Preserve       bool   `toml:"preserve,omitempty"`  
	fileExistsTemp bool
}
func LoadIndex(indexFile string) (Index, error) {
	var index Index
	if _, err := toml.DecodeFile(indexFile, &index); err != nil {
		return Index{}, err
	}
	index.indexFile = indexFile
	if len(index.HashFormat) == 0 {
		index.HashFormat = "sha256"
	}
	return index, nil
}
func (in *Index) RemoveFile(path string) error {
	relPath, err := filepath.Rel(filepath.Dir(in.indexFile), path)
	if err != nil {
		return err
	}
	i := 0
	for _, file := range in.Files {
		if filepath.Clean(filepath.FromSlash(file.File)) != relPath {
			in.Files[i] = file
			i++
		}
	}
	in.Files = in.Files[:i]
	return nil
}
func (in *Index) resortIndex() {
	sort.SliceStable(in.Files, func(i, j int) bool {
		return in.Files[i].File < in.Files[j].File
	})
}
func (in *Index) updateFileHashGiven(path, format, hash string, mod bool) error {
	found := false
	relPath, err := filepath.Rel(filepath.Dir(in.indexFile), path)
	if err != nil {
		return err
	}
	for k, v := range in.Files {
		if filepath.Clean(filepath.FromSlash(v.File)) == relPath {
			found = true
			in.Files[k].Hash = hash
			if in.HashFormat == format {
				in.Files[k].HashFormat = ""
			} else {
				in.Files[k].HashFormat = format
			}
			in.Files[k].fileExistsTemp = true
			in.Files[k].File = filepath.ToSlash(relPath)
		}
	}
	if !found {
		newFile := IndexFile{
			File:           filepath.ToSlash(relPath),
			Hash:           hash,
			fileExistsTemp: true,
		}
		if in.HashFormat != format {
			newFile.HashFormat = format
		}
		newFile.MetaFile = mod
		in.Files = append(in.Files, newFile)
	}
	return nil
}
func (in *Index) updateFile(path string) error {
	var hashString string
	if viper.GetBool("no-internal-hashes") {
		hashString = ""
	} else {
		f, err := os.Open(path)
		if err != nil {
			return err
		}
		h, err := GetHashImpl("sha256")
		if err != nil {
			_ = f.Close()
			return err
		}
		if _, err := io.Copy(h, f); err != nil {
			_ = f.Close()
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}
		hashString = hex.EncodeToString(h.Sum(nil))
	}
	mod := false
	absFileDir, err := filepath.Abs(filepath.Dir(path))
	if err == nil {
		modsDir := filepath.Join(in.GetPackRoot(), viper.GetString("mods-folder"))
		absModsDir, err := filepath.Abs(modsDir)
		if err == nil {
			if absFileDir == absModsDir && strings.HasSuffix(filepath.Base(path), ".toml") {
				mod = true
			}
		}
	}
	return in.updateFileHashGiven(path, "sha256", hashString, mod)
}
func (in Index) GetPackRoot() string {
	return filepath.Dir(in.indexFile)
}
func (in *Index) Refresh() error {
	pathPF, _ := filepath.Abs(viper.GetString("pack-file"))
	pathIndex, _ := filepath.Abs(in.indexFile)
	packRoot := in.GetPackRoot()
	ignoreExists := true
	pathIgnore, _ := filepath.Abs(filepath.Join(packRoot, ".packwizignore"))
	ignore, err := gitignore.NewFromFile(filepath.Join(packRoot, ".packwizignore"))
	if err != nil {
		ignoreExists = false
	}
	var fileList []string
	err = filepath.Walk(packRoot, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		absPath, _ := filepath.Abs(path)
		if absPath == pathPF || absPath == pathIndex {
			return nil
		}
		if info.IsDir() {
			return nil
		}
		if ignoreExists {
			if absPath == pathIgnore {
				return nil
			}
			if ignore.Ignore(absPath) {
				return nil
			}
		}
		fileList = append(fileList, path)
		return nil
	})
	if err != nil {
		return err
	}
	progressContainer := mpb.New()
	progress := progressContainer.AddBar(int64(len(fileList)),
		mpb.PrependDecorators(
			decor.Name("Refreshing index..."),
			decor.Percentage(decor.WCSyncSpace),
		),
		mpb.AppendDecorators(
			decor.OnComplete(
				decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
			),
		),
	)
	for _, v := range fileList {
		start := time.Now()
		err := in.updateFile(v)
		if err != nil {
			return err
		}
		progress.Increment(time.Since(start))
	}
	progress.SetTotal(int64(len(fileList)), true)  
	progressContainer.Wait()
	i := 0
	for _, file := range in.Files {
		if file.fileExistsTemp {
			in.Files[i] = file
			i++
		}
	}
	in.Files = in.Files[:i]
	in.resortIndex()
	return nil
}
func (in *Index) RefreshFile(path string) error {
	err := in.updateFile(path)
	if err != nil {
		return err
	}
	in.resortIndex()
	return nil
}
func (in Index) Write() error {
	f, err := os.Create(in.indexFile)
	if err != nil {
		return err
	}
	enc := toml.NewEncoder(f)
	enc.Indent = ""
	err = enc.Encode(in)
	if err != nil {
		_ = f.Close()
		return err
	}
	return f.Close()
}
func (in *Index) RefreshFileWithHash(path, format, hash string, mod bool) error {
	if viper.GetBool("no-internal-hashes") {
		hash = ""
	}
	err := in.updateFileHashGiven(path, format, hash, mod)
	if err != nil {
		return err
	}
	in.resortIndex()
	return nil
}
func (in Index) FindMod(modName string) (string, bool) {
	for _, v := range in.Files {
		if v.MetaFile {
			_, file := filepath.Split(v.File)
			fileTrimmed := strings.TrimSuffix(file, ModExtension)
			if fileTrimmed == modName {
				return filepath.Join(filepath.Dir(in.indexFile), filepath.FromSlash(v.File)), true
			}
		}
	}
	return "", false
}
func (in Index) GetAllMods() []string {
	var list []string
	baseDir := filepath.Dir(in.indexFile)
	for _, v := range in.Files {
		if v.MetaFile {
			list = append(list, filepath.Join(baseDir, filepath.FromSlash(v.File)))
		}
	}
	return list
}