From 560becfd32dd7355547938f3c6229060dd395aab Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Thu, 7 Mar 2024 00:54:35 +0100 Subject: chore(repo): renamed gonf subfolder to something more traditional in go land --- pkg/files.go | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 pkg/files.go (limited to 'pkg/files.go') diff --git a/pkg/files.go b/pkg/files.go new file mode 100644 index 0000000..35420d4 --- /dev/null +++ b/pkg/files.go @@ -0,0 +1,151 @@ +package gonf + +import ( + "bytes" + "crypto/sha256" + "errors" + "io" + "io/fs" + "log/slog" + "os" +) + +// ----- Globals --------------------------------------------------------------- +var files []*FilePromise + +// ----- Init ------------------------------------------------------------------ +func init() { + files = make([]*FilePromise, 0) +} + +// ----- Public ---------------------------------------------------------------- +type FilePromise struct { + chain []Promise + contents Value + err error + filename Value + permissions *Permissions + status Status +} + +func File(filename any) *FilePromise { + return &FilePromise{ + chain: nil, + contents: nil, + err: nil, + filename: interfaceToTemplateValue(filename), + permissions: nil, + status: PROMISED, + } +} + +func (f *FilePromise) Contents(contents any) *FilePromise { + f.contents = interfaceToValue(contents) + return f +} + +func (f *FilePromise) Permissions(p *Permissions) *FilePromise { + f.permissions = p + return f +} + +func (f *FilePromise) Template(contents any) *FilePromise { + f.contents = interfaceToTemplateValue(contents) + return f +} + +// We want to satisfy the Promise interface +func (f *FilePromise) IfRepaired(p ...Promise) Promise { + f.chain = p + return f +} + +func (f *FilePromise) Promise() Promise { + files = append(files, f) + return f +} + +func (f *FilePromise) Resolve() { + filename := f.filename.String() + if f.contents != nil { + var sumFile []byte + sumFile, f.err = sha256sumOfFile(filename) + if f.err != nil { + if !errors.Is(f.err, fs.ErrNotExist) { + slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err) + f.status = BROKEN + return + } + } + contents := f.contents.Bytes() + sumContents := sha256sum(contents) + if !bytes.Equal(sumFile, sumContents) { + if f.err = writeFile(filename, contents); f.err != nil { + f.status = BROKEN + slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err) + return + } + f.status = REPAIRED + } + } + if f.permissions != nil { + var status Status + status, f.err = f.permissions.resolve(filename) + if f.status == PROMISED || status == BROKEN { + f.status = status + } + if f.err != nil { + slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err) + return + } + } + if f.status == REPAIRED { + slog.Info("file", "filename", f.filename, "status", f.status) + for _, p := range f.chain { + p.Resolve() + } + } else { + f.status = KEPT + slog.Debug("file", "filename", f.filename, "status", f.status) + } +} + +// ----- Internal -------------------------------------------------------------- +func resolveFiles() (status Status) { + status = KEPT + for _, f := range files { + if f.status == PROMISED { + f.Resolve() + switch f.status { + case BROKEN: + return BROKEN + case REPAIRED: + status = REPAIRED + } + } + } + return +} + +func sha256sumOfFile(filename string) ([]byte, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + return nil, err + } + return h.Sum(nil), nil +} + +func writeFile(filename string, contents []byte) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(contents) + return err +} -- cgit v1.2.3