Skip to content

Commit

Permalink
feat(nef): add ensure on fs (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
plastikfan committed Oct 20, 2024
1 parent f96c2d6 commit 8b094dc
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ tasks:
cmds:
- go test ./collections

tt:
tn:
cmds:
- go test

Expand Down
47 changes: 47 additions & 0 deletions file-systems.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
)

// 🔥 An important note about using standard golang file systems (io.fs/fs.FS)
Expand Down Expand Up @@ -254,6 +255,48 @@ func (f *makeDirAllFS) MakeDirAll(name string, perm os.FileMode) error {
return os.MkdirAll(path, perm)
}

// EnsurePathAt ensures that the specified path exists (including any non
// existing intermediate directories). Given a path and a default filename,
// the specified path is created in the following manner:
// - If the path denotes a file (path does not end is a directory separator), then
// the parent folder is created if it doesn't exist on the file-system provided.
// - If the path denotes a directory, then that directory is created.
//
// The returned string represents the file, so if the path specified was a
// directory path, then the defaultFilename provided is joined to the path
// and returned, otherwise the original path is returned un-modified.
// Note: filepath.Join does not preserve a trailing separator, therefore to make sure
// a path is interpreted as a directory and not a file, then the separator has
// to be appended manually onto the end of the path.
func (f *makeDirAllFS) Ensure(as PathAs,
) (at string, err error) {
if !fs.ValidPath(as.Name) {
return "", NewInvalidPathError(as.Name)
}

var (
file string
)

if f.FileExists(as.Name) {
_, file = filepath.Split(as.Name)

return file, nil
}

if f.DirectoryExists(as.Name) {
return as.Default, nil
}

if as.AsFile {
directory, file := SplitParent(as.Name)

return file, f.MakeDirAll(directory, as.Perm)
}

return as.Default, f.MakeDirAll(as.Name, as.Perm)
}

// 🎯 removeFS

type removeFS struct {
Expand Down Expand Up @@ -501,3 +544,7 @@ func compose(root string) *entities {
reader: reader,
}
}

func join(segments ...string) string {
return strings.Join(segments, "/")
}
207 changes: 207 additions & 0 deletions fs-ensure-at_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package nef_test

import (
"errors"
"fmt"
"os"
"path/filepath"
"testing/fstest"

. "github.com/onsi/ginkgo/v2" //nolint:revive // ok
. "github.com/onsi/gomega" //nolint:revive // ok

nef "github.com/snivilised/nefilim"
lab "github.com/snivilised/nefilim/internal/laboratory"
)

var _ = Describe("Ensure", Ordered, func() {
var (
mocks *nef.ResolveMocks
mapFS *makeDirMapFS
root string
fS nef.MakeDirFS
)

BeforeAll(func() {
root = Repo("test")
})

BeforeEach(func() {
mocks = &nef.ResolveMocks{
HomeFunc: func() (string, error) {
return filepath.Join(string(filepath.Separator), "home", "prodigy"), nil
},
AbsFunc: func(_ string) (string, error) { // no-op
return "", errors.New("not required for these tests")
},
}

mapFS = &makeDirMapFS{
mapFS: fstest.MapFS{
filepath.Join("home", "prodigy"): &fstest.MapFile{
Mode: os.ModeDir,
},
},
}
scratch(root)

fS = nef.NewMakeDirFS(nef.At{
Root: root,
})
})

DescribeTable("local-fs",
func(entry fsTE[nef.MakeDirFS]) {
if entry.arrange != nil {
entry.arrange(entry, fS)
}
entry.action(entry, fS)
},
func(entry fsTE[nef.MakeDirFS]) string {
return fmt.Sprintf("🧪 ===> given: '%v', %v should: '%v'",
entry.given, entry.op, entry.should,
)
},

Entry(nil, fsTE[nef.MakeDirFS]{
given: "path exists as file",
should: "return filename of path",
op: "Ensure",
require: lab.Static.FS.Scratch,
target: lab.Static.FS.Ensure.Log.File,
arrange: func(entry fsTE[nef.MakeDirFS], _ nef.MakeDirFS) {
directory := lab.Static.FS.Ensure.Default.Directory
Expect(require(root, directory, entry.target)).To(Succeed())
},
action: func(entry fsTE[nef.MakeDirFS], fS nef.MakeDirFS) {
result, err := fS.Ensure(
nef.PathAs{
Name: entry.target,
Default: lab.Static.FS.Ensure.Default.File,
Perm: lab.Perms.Dir,
},
)
Expect(err).To(Succeed())
_, file := filepath.Split(lab.Static.FS.Ensure.Log.File)
Expect(result).To(Equal(file))
},
}),

Entry(nil, fsTE[nef.MakeDirFS]{
given: "path exists as directory",
should: "return default",
op: "Ensure",
require: lab.Static.FS.Scratch,
target: lab.Static.FS.Ensure.Log.Directory,
arrange: func(entry fsTE[nef.MakeDirFS], _ nef.MakeDirFS) {
Expect(require(root, entry.target)).To(Succeed())
},
action: func(entry fsTE[nef.MakeDirFS], fS nef.MakeDirFS) {
_, file := filepath.Split(lab.Static.FS.Ensure.Default.File)
result, err := fS.Ensure(
nef.PathAs{
Name: entry.target,
Default: file,
Perm: lab.Perms.Dir,
},
)
Expect(err).To(Succeed())
Expect(result).To(Equal(file))
},
}),

Entry(nil, fsTE[nef.MakeDirFS]{
given: "file does not exist",
should: "create parent directory and return file",
op: "Ensure",
require: lab.Static.FS.Scratch,
target: lab.Static.FS.Ensure.Log.File,
from: lab.Static.FS.Ensure.Home,
arrange: func(entry fsTE[nef.MakeDirFS], _ nef.MakeDirFS) {
parent := Join(entry.require, entry.from)
Expect(require(root, parent)).To(Succeed())
},
action: func(entry fsTE[nef.MakeDirFS], fS nef.MakeDirFS) {
_, file := filepath.Split(lab.Static.FS.Ensure.Default.File)
result, err := fS.Ensure(
nef.PathAs{
Name: entry.target,
Default: file,
Perm: lab.Perms.Dir,
AsFile: true,
},
)
Expect(err).To(Succeed())
ensureAt := lab.Static.FS.Ensure.Default.Directory
Expect(AsDirectory(ensureAt)).To(ExistInFS(fS))
_, file = filepath.Split(entry.target)
Expect(result).To(Equal(file))
},
}),

Entry(nil, fsTE[nef.MakeDirFS]{
given: "directory does not exist",
should: "create directory and return default",
op: "Ensure",
require: lab.Static.FS.Scratch,
target: lab.Static.FS.Ensure.Log.Directory,
from: lab.Static.FS.Ensure.Home,
arrange: func(entry fsTE[nef.MakeDirFS], _ nef.MakeDirFS) {
parent := Join(entry.require, entry.from)
Expect(require(root, parent)).To(Succeed())
},
action: func(entry fsTE[nef.MakeDirFS], fS nef.MakeDirFS) {
_, file := filepath.Split(lab.Static.FS.Ensure.Default.File)
result, err := fS.Ensure(
nef.PathAs{
Name: entry.target,
Default: file,
Perm: lab.Perms.Dir,
},
)
Expect(err).To(Succeed())
ensureAt := lab.Static.FS.Ensure.Default.Directory
Expect(AsDirectory(ensureAt)).To(ExistInFS(fS))
Expect(result).To(Equal(file))
},
}),
)

DescribeTable("with mapFS",
func(entry *ensureTE) {
home, _ := mocks.HomeFunc()
location := TrimRoot(filepath.Join(home, entry.relative))

if entry.directory {
location += string(filepath.Separator)
}

actual, err := nef.EnsurePathAt(location, lab.Static.FS.Ensure.Default.File, lab.Perms.File, mapFS)
directory, _ := filepath.Split(actual)
directory = filepath.Clean(directory)
expected := TrimRoot(Path(home, entry.expected))

Expect(err).Error().To(BeNil())
Expect(actual).To(Equal(expected))
Expect(AsDirectory(TrimRoot(directory))).To(ExistInFS(mapFS))
},
func(entry *ensureTE) string {
return fmt.Sprintf("🧪 ===> given: '%v', should: '%v'", entry.given, entry.should)
},

XEntry(nil, &ensureTE{
given: "path is file",
should: "create parent directory and return specified file path",
relative: filepath.Join("logs", "test.log"), // home/logs/test.log
expected: "logs/test.log",
}),

XEntry(nil, &ensureTE{
given: "path is directory",
should: "create parent directory and return default file path",
relative: "logs/",
directory: true,
expected: "logs/default-test.log",
}),
)
})
18 changes: 18 additions & 0 deletions internal/laboratory/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ type (
Destination string
}

Ensure struct {
Home string
Default Pair
Log Pair
}

Pair struct {
File string
Directory string
Expand Down Expand Up @@ -45,6 +51,7 @@ type (
StaticFs struct {
Copy Copy
Create Create
Ensure Ensure
Existing Pair
MakeDir MakeDir
Move Move
Expand Down Expand Up @@ -78,6 +85,17 @@ var (
Create: Create{
Destination: "scratch/pictures-of-you.CREATE.txt",
},
Ensure: Ensure{
Home: "home/marina",
Default: Pair{
File: "scratch/home/marina/logs/default-test.log",
Directory: "scratch/home/marina/logs",
},
Log: Pair{
File: "scratch/home/marina/logs/test.log",
Directory: "scratch/home/marina/logs",
},
},
Existing: Pair{
File: "data/fS/paradise-lost.txt",
Directory: "data/fS",
Expand Down
8 changes: 8 additions & 0 deletions make-dir-map-fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"path/filepath"
"strings"
"testing/fstest"

nef "github.com/snivilised/nefilim"
)

type (
Expand Down Expand Up @@ -74,3 +76,9 @@ func (f *makeDirMapFS) MakeDirAll(path string, perm os.FileMode) error {

return nil
}

func (f *makeDirMapFS) Ensure(as nef.PathAs,
) (at string, err error) {
_ = as
panic("NOT-IMPL: makeDirMapFS.Ensure")
}
20 changes: 20 additions & 0 deletions nefilim-defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,31 @@ type (
ReadFileFS
}

// PathAs used with Ensure to define how to ensure that a path exists
// at the location specified
PathAs struct {
Name string
Default string
Perm os.FileMode
AsFile bool
}

// MakeDirFS is a file system with a MkDirAll method.
MakeDirFS interface {
ExistsInFS
MakeDir(name string, perm os.FileMode) error
MakeDirAll(name string, perm os.FileMode) error
// Ensure makes sure that a path exists (PathAs.Name). If the path exists
// as a file then no directories need to be created and this file name
// (PathAs.Name) is returned. If the path exists as a directory, then again
// no directories are created, but the default (PathAs.Default) is returned.
//
// If the path does not exist, then 1 of 2 things can happen. If PathAs.AsFile
// is set to true, then the parent of the path is created, and file portion
// of the path is returned. When PathAs.AsFile is not set, ie the path
// provided is to be interpreted as a directory, then this directory is
// created and the default is returned.
Ensure(as PathAs) (string, error)
}

// MoveFS
Expand Down
4 changes: 4 additions & 0 deletions nefilim-suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ func Repo(relative string) string {
return Path(repo, relative)
}

func Join(segments ...string) string {
return strings.Join(segments, "/")
}

func Normalise(p string) string {
return strings.ReplaceAll(p, "/", string(filepath.Separator))
}
Expand Down

0 comments on commit 8b094dc

Please sign in to comment.