diff --git a/handler/fs/path.go b/handler/fs/path.go new file mode 100644 index 0000000..4dbd39c --- /dev/null +++ b/handler/fs/path.go @@ -0,0 +1,94 @@ +package fs + +import ( + "errors" + "fmt" + "io" + "io/fs" + "os" + "path" + "path/filepath" +) + +var ( + // ErrNoCurrentDir returned on creating the content file before the current + // directory was created. + ErrNoCurrentDir = errors.New("current dir was not created") + + // ErrInvalidDest returned when provided save path is not a valid directory. + ErrInvalidDest = errors.New("invalid destination path") +) + +type PathResolver struct { + // Dest contains a base directory for all folders that will be created. + // It should be a valid absolute or relative path and must already exist. + Dest string + + absDest string + currentDest string +} + +// NewPathResolver returns new *PathResolver along with resolving provided dest +// value. If an incorrect dest value is provided an error will be returned. +func NewPathResolver(dest string) (*PathResolver, error) { + absDest, err := filepath.Abs(dest) + if err != nil { + return nil, ErrInvalidDest + } + + stat, err := os.Stat(absDest) + if err != nil || !stat.IsDir() { + return nil, ErrInvalidDest + } + + return &PathResolver{ + Dest: dest, + absDest: absDest, + }, nil +} + +// CreateFolder creates a new dir with the provided name within PathResolver.Dest dir. +// Created dir will be marked as current dir. If dir already exists the creation +// step will be skipped. +func (p *PathResolver) CreateFolder(name string) error { + err := os.Chdir(p.absDest) + if err != nil { + return fmt.Errorf("cannot resolve path for folder %s: %w", name, err) + } + + p.currentDest = path.Join(p.absDest, name) + + err = os.Mkdir(name, fs.ModePerm) + if err != nil { + if os.IsExist(err) { + return nil + } + return fmt.Errorf("cannot create folder %s: %w", p.currentDest, err) + } + + return nil +} + +// CreateFile creates a new file with the corresponding name and returns its +// io.WriteCloser. If FSResolver.currentDest wasn't created before, then an error +// will be returned. +func (p *PathResolver) CreateFile(name string) (io.WriteCloser, error) { + if p.currentDest == "" { + return nil, ErrNoCurrentDir + } + + filePath := path.Join(p.currentDest, name) + + f, err := os.Create(filePath) + if err != nil { + return nil, fmt.Errorf("cannot create file %s: %w", filePath, err) + } + + return f, nil +} + +// Remove removes file or dir by its name. If the provided path wasn't found the +// deletion step will be skipped. +func (p *PathResolver) Remove(name string) { + _ = os.Remove(name) +} diff --git a/handler/fs/path_test.go b/handler/fs/path_test.go new file mode 100644 index 0000000..c2ffb33 --- /dev/null +++ b/handler/fs/path_test.go @@ -0,0 +1,82 @@ +//go:build unit +// +build unit + +package fs_test + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + "reactor-crw/handler/fs" +) + +func TestNewPathResolver(t *testing.T) { + paths := []struct { + name string + msg string + errExpected bool + }{ + {" ", "Expected an error during resolving empty path", true}, + {"invalid_path", "Expected an error during resolving non-existent dir", true}, + {os.TempDir(), "Wasn't expected an error", false}, + } + + for _, path := range paths { + _, err := fs.NewPathResolver(path.name) + if path.errExpected { + require.Errorf(t, err, path.msg) + } + + if !path.errExpected { + require.NoError(t, err, path.msg) + } + } +} + +func TestPathResolver_CreateFolder(t *testing.T) { + p, _ := fs.NewPathResolver(os.TempDir()) + + folders := []struct { + name string + errExpected bool + }{ + {name: "test"}, + {name: "_123_test"}, + {name: "109483___"}, + } + + for _, folder := range folders { + err := p.CreateFolder(folder.name) + if !folder.errExpected { + require.NoError(t, err, "Wasn't expected an error during creating %s", folder.name) + } + + if folder.errExpected { + require.Error(t, err, "Expected an error during creating %s", folder.name) + } + + p.Remove(folder.name) + } + + p = &fs.PathResolver{} + err := p.CreateFolder("test") + require.Error(t, err, "Expected an error during creating folder") +} + +func TestPathResolver_CreateFile(t *testing.T) { + p, _ := fs.NewPathResolver(os.TempDir()) + _, err := p.CreateFile("filename") + require.Error(t, err, "Expected an error during file. Folder wasn't created yet.") + + _ = p.CreateFolder("test") + + _, err = p.CreateFile("filename") + require.NoError(t, err, "Wasn't expected an error during creating filename") + + filePath := "./test/filename" + _, err = os.Stat(filePath) + require.NoError(t, err, "File %s wasn't created", filePath) + + p.Remove(filePath) +}