wisski-cloud-distillery/embed/install.go
Tom Wiesing e75dc29de1
wdcli: Update handling of templates
This commit updates the handling of expanding of templates. This is to
prepare embeding templates directly as opposed to embedding a single
resource directory.
2022-09-09 23:20:47 +02:00

118 lines
3.5 KiB
Go

// Package embed contains embedded resources
package embed
import (
"embed"
"io"
"io/fs"
"os"
"path/filepath"
"github.com/pkg/errors"
)
// resourceEmbed contains all the resources required by the WissKI-Distillery package.
//go:embed all:resources
var resourceEmbed embed.FS
// InstallResource install a resource src into dest.
// When it encounters a directory, recursively installs the directory is called.
// For each installation item, onInstallFile is called, unless onInstallFile is nil.
//
// If src points to a file, dst must either be an existing file, or not exist.
// If src points to a directory, dst must either be an existing directory, or not exist.
func InstallResource(dst, src string, onInstallFile func(dst, src string)) error {
return installFile(dst, resourceEmbed, src, onInstallFile)
}
var errExpectedFileButGotDirectory = errors.New("Expected a file, but got a directory")
var errExpectedDirectoryButGotFile = errors.New("Expected a directory, but got a file")
func installFile(dst string, fsys embed.FS, src string, onInstallFile func(dst, src string)) error {
// call the on-install file path
if onInstallFile != nil {
onInstallFile(dst, src)
}
// open the source file!
srcFile, err := fsys.Open(src)
if err != nil {
return errors.Wrapf(err, "Error opening source file %s", src)
}
defer srcFile.Close()
// stat the source file to install
srcStat, srcErr := srcFile.Stat()
if srcErr != nil {
return errors.Wrapf(srcErr, "Error calling stat on source %s", src)
}
// if it is a directory, we should recurse!
if srcStat.IsDir() {
return installDir(dst, srcStat, srcFile, fsys, src, onInstallFile)
}
// determine if we need to create the destination file, or if it already exists
dstStat, dstErr := os.Stat(dst)
switch {
case os.IsNotExist(dstErr):
case dstErr != nil:
return errors.Wrapf(dstErr, "Error calling stat on destination %s", dst)
case dstStat.IsDir():
return errors.Wrapf(errExpectedFileButGotDirectory, "Error processing destination %s", dst)
}
// Open the file
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcStat.Mode())
if err != nil {
return errors.Wrapf(err, "Error opening destination %s", dst)
}
defer dstFile.Close()
// copy over the content
_, err = io.Copy(dstFile, srcFile)
return errors.Wrapf(err, "Error writing to destination %s", dst)
}
func installDir(dst string, srcStat fs.FileInfo, srcFile fs.File, fsys embed.FS, src string, onInstallFile func(dst, src string)) error {
// make sure it is a directory!
dir, ok := srcFile.(fs.ReadDirFile)
if !ok {
return errExpectedDirectoryButGotFile
}
// create the destination
dstStat, dstErr := os.Stat(dst)
switch {
case os.IsNotExist(dstErr):
if err := os.MkdirAll(dst, srcStat.Mode()); err != nil {
return errors.Wrapf(err, "Error creating destination directory %s", dst)
}
case dstErr != nil:
return errors.Wrapf(dstErr, "Error calling stat on destination %s", dst)
case !dstStat.IsDir():
return errors.Wrapf(errExpectedDirectoryButGotFile, "Error opening destination %s", dst)
case dstErr == nil:
}
// read the directory
entries, err := dir.ReadDir(-1)
if err != nil {
return errors.Wrapf(err, "Error reading source directory %s", srcFile)
}
// iterate over all the children
for _, entry := range entries {
if err := func(dst, src string) error {
return installFile(dst, fsys, src, onInstallFile)
}(
filepath.Join(dst, entry.Name()),
filepath.Join(src, entry.Name()),
); err != nil {
return err
}
}
return nil
}