From b0dce3c29fb9c4bb91594f1152b0fe88f0476852 Mon Sep 17 00:00:00 2001 From: "Gustavo \"Guz\" L de Mello" Date: Thu, 20 Nov 2025 15:07:53 -0300 Subject: [PATCH] feat(editor,storage): storage library to abstract writabble file system --- editor/go.mod | 5 ++ editor/go.sum | 4 ++ editor/storage/local.go | 98 +++++++++++++++++++++++++++++++++++++++ editor/storage/storage.go | 42 +++++++++++++++++ makefile | 20 ++++++++ 5 files changed, 169 insertions(+) create mode 100644 editor/storage/local.go create mode 100644 editor/storage/storage.go diff --git a/editor/go.mod b/editor/go.mod index 3d9f058..c6c9779 100644 --- a/editor/go.mod +++ b/editor/go.mod @@ -1,3 +1,8 @@ module code.capytal.cc/capytal/comicverse/editor go 1.25.2 + +require ( + code.capytal.cc/loreddev/smalltrip v0.0.0-20251113171745-e3813daa807e + code.capytal.cc/loreddev/x v0.0.0-20251113171626-2ce5d71249c1 +) diff --git a/editor/go.sum b/editor/go.sum index e69de29..db44f69 100644 --- a/editor/go.sum +++ b/editor/go.sum @@ -0,0 +1,4 @@ +code.capytal.cc/loreddev/smalltrip v0.0.0-20251113171745-e3813daa807e h1:LdkirHDzhkcnhOBnDN0po84DjHAAkGztjHu/4mfWpSI= +code.capytal.cc/loreddev/smalltrip v0.0.0-20251113171745-e3813daa807e/go.mod h1:jMvSPUj295pTk/ixyxZfwZJE/RQ7DZzvQ3cVoAklkPA= +code.capytal.cc/loreddev/x v0.0.0-20251113171626-2ce5d71249c1 h1:BE0QdvwVVTG/t7nwNO5rrLf1vdAc5axv/1mWd/oAWhw= +code.capytal.cc/loreddev/x v0.0.0-20251113171626-2ce5d71249c1/go.mod h1:p5ZPHzutdbUDfpvNBCjv5ls6rM4YNl2k4ipD5b0aRho= diff --git a/editor/storage/local.go b/editor/storage/local.go new file mode 100644 index 0000000..2bc68d1 --- /dev/null +++ b/editor/storage/local.go @@ -0,0 +1,98 @@ +package storage + +import ( + "errors" + "fmt" + "io" + "io/fs" + "log/slog" + "os" + "path" +) + +func Newlocal( + root *os.Root, + logger *slog.Logger, +) Storage { + return &local{ + log: logger, + root: root, + } +} + +type local struct { + log *slog.Logger + root *os.Root +} + +var _ Storage = (*local)(nil) + +func (files *local) Exists(p string) bool { + if _, err := files.root.Stat(p); err != nil { + return false + } + return true +} + +func (files *local) Open(p string) (fs.File, error) { + log := files.log.With( + slog.String("path", p), + slog.String("root", files.root.Name())) + + log.Debug("Opening file") + defer log.Debug("File opened") + + f, err := files.root.Open(p) + if errors.Is(err, os.ErrNotExist) { + return nil, ErrNotExists + } else if err != nil { + return nil, err + } + + return f, nil +} + +func (files *local) Write(p string, d []byte) (int, error) { + log := files.log.With( + slog.String("path", p), + slog.String("root", files.root.Name())) + + log.Debug("Writing file") + defer log.Debug("File wrote") + + if err := files.root.MkdirAll(path.Dir(p), os.ModePerm); err != nil { + return 0, fmt.Errorf("file.local: failed to create parent directories %q: %w", path.Dir(p), err) + } + + err := files.root.WriteFile(p, d, os.ModePerm) + if err != nil { + return 0, fmt.Errorf("file.local: failed to write file %q: %w", p, err) + } + + return len(d), nil +} + +func (files *local) WriteFrom(p string, r io.Reader) (int64, error) { + log := files.log.With( + slog.String("path", p), + slog.String("root", files.root.Name())) + + log.Debug("Writing file") + defer log.Debug("File wrote") + + if err := files.root.MkdirAll(path.Dir(p), os.ModePerm); err != nil { + return 0, fmt.Errorf("file.local: failed to create parent directories %q: %w", path.Dir(p), err) + } + + f, err := files.root.Create(p) + if err != nil { + return 0, fmt.Errorf("file.local: failed to create file %q: %w", p, err) + } + + n, err := f.ReadFrom(r) + if err != nil { + return 0, fmt.Errorf("file.local: failed to write file %q: %w", p, err) + } + + return n, nil +} diff --git a/editor/storage/storage.go b/editor/storage/storage.go new file mode 100644 index 0000000..44d9d93 --- /dev/null +++ b/editor/storage/storage.go @@ -0,0 +1,42 @@ +package storage + +import ( + "io" + "io/fs" + "os" + "path" +) + +type Storage interface { + Exists(p string) bool + Open(p string) (fs.File, error) + Write(p string, b []byte) (int, error) + WriteFrom(p string, r io.Reader) (int64, error) +} + +type withRoot struct { + root string + Storage +} + +func WithRoot(rootDir string, s Storage) Storage { + return &withRoot{root: rootDir, Storage: s} +} + +func (f *withRoot) Exists(p string) bool { + return f.Storage.Exists(path.Join(f.root, p)) +} + +func (f *withRoot) Open(p string) (fs.File, error) { + return f.Storage.Open(path.Join(f.root, p)) +} + +func (f *withRoot) Write(p string, b []byte) (int, error) { + return f.Storage.Write(path.Join(f.root, p), b) +} + +func (f *withRoot) WriteFrom(p string, r io.Reader) (int64, error) { + return f.Storage.WriteFrom(path.Join(f.root, p), r) +} + +var ErrNotExists = os.ErrNotExist diff --git a/makefile b/makefile index 952586f..5e9b190 100644 --- a/makefile +++ b/makefile @@ -30,6 +30,26 @@ dev: dev/debug: $(MAKE) -j2 debug dev/assets +editor/dev/server: + cd ./editor; go run github.com/joho/godotenv/cmd/godotenv@v1.5.1 \ + go run github.com/air-verse/air@v1.52.2 \ + --build.cmd "go build -o tmp/bin/main ./cmd" \ + --build.bin "tmp/bin/main" \ + --build.exclude_dir "node_modules" \ + --build.include_ext "go" \ + --build.stop_on_error "false" \ + --misc.clean_on_exit true \ + -- -dev -port $(PORT) -hostname 0.0.0.0 + +editor/dev/assets: + cd ./editor; tailwindcss \ + -i ./assets/css/tailwind.css \ + -o ./assets/css/style.css \ + --watch + +editor/dev: + $(MAKE) -j2 editor/dev/assets editor/dev/server + debug: dlv debug -l 127.0.0.1:38697 \ --continue \