🚚 Renamed 'page' module back to 'entry'

This commit is contained in:
Maxim Lebedev 2024-02-03 20:35:22 +06:00
parent b046f13098
commit 4026fb9192
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
10 changed files with 227 additions and 153 deletions

View File

@ -0,0 +1,18 @@
package entry
import (
"context"
"errors"
"source.toby3d.me/toby3d/home/internal/domain"
)
type Repository interface {
Get(ctx context.Context, lang domain.Language, path string) (*domain.Page, error)
// Stat checks for the existence of a page on the specified path without
// parsing its contents.
Stat(ctx context.Context, lang domain.Language, path string) (bool, error)
}
var ErrNotExist error = errors.New("entry not exists")

View File

@ -2,6 +2,7 @@ package fs
import (
"context"
"errors"
"fmt"
"io/fs"
@ -9,7 +10,7 @@ import (
"gopkg.in/yaml.v3"
"source.toby3d.me/toby3d/home/internal/domain"
"source.toby3d.me/toby3d/home/internal/page"
"source.toby3d.me/toby3d/home/internal/entry"
)
type (
@ -29,7 +30,7 @@ var FrontMatterFormats = []*frontmatter.Format{
frontmatter.NewFormat(`---`, `---`, yaml.Unmarshal),
}
func NewFileSystemPageRepository(dir fs.FS) page.Repository {
func NewFileSystemPageRepository(dir fs.FS) entry.Repository {
return &fileSystemPageRepository{
dir: dir,
}
@ -45,7 +46,7 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Langu
f, err := repo.dir.Open(target)
if err != nil {
return nil, fmt.Errorf("cannot open '%s' page file: %w", target, err)
return nil, fmt.Errorf("cannot open '%s' entry file: %w", target, err)
}
defer f.Close()
@ -53,7 +54,7 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Langu
Params: make(map[string]any),
}
if data.Content, err = frontmatter.Parse(f, data, FrontMatterFormats...); err != nil {
return nil, fmt.Errorf("cannot parse page content as FrontMatter: %w", err)
return nil, fmt.Errorf("cannot parse entry content as FrontMatter: %w", err)
}
return &domain.Page{
@ -67,3 +68,14 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Langu
Translations: make([]*domain.Page, 0),
}, nil
}
func (repo *fileSystemPageRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) {
ext := ".md"
if l != domain.LanguageUnd {
ext = "." + l.Lang() + ext
}
_, err := fs.Stat(repo.dir, p+ext)
return errors.Is(err, fs.ErrExist), nil
}

View File

@ -10,7 +10,7 @@ import (
"github.com/google/go-cmp/cmp"
"source.toby3d.me/toby3d/home/internal/domain"
repository "source.toby3d.me/toby3d/home/internal/page/repository/fs"
repository "source.toby3d.me/toby3d/home/internal/entry/repository/fs"
)
func TestGet(t *testing.T) {

View File

@ -0,0 +1,41 @@
package memory
import (
"context"
"errors"
"fmt"
"io/fs"
"sync"
"testing/fstest"
"source.toby3d.me/toby3d/home/internal/domain"
"source.toby3d.me/toby3d/home/internal/entry"
)
type memoryEntryRepository struct {
mutex *sync.RWMutex
files fstest.MapFS
}
func (repo *memoryEntryRepository) Get(_ context.Context, _ domain.Language, p string) (*domain.Page, error) {
f, err := repo.files.Open(p)
if err != nil {
return nil, fmt.Errorf("cannot get entry from memory: %w", err)
}
defer f.Close()
return nil, nil
}
func (repo *memoryEntryRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) {
_, err := fs.Stat(repo.files, p)
return errors.Is(err, fs.ErrExist), nil
}
func NewMemoryEntryRepository(files fstest.MapFS) entry.Repository {
return &memoryEntryRepository{
mutex: new(sync.RWMutex),
files: files,
}
}

View File

@ -1,8 +1,7 @@
package page
package entry
import (
"context"
"errors"
"source.toby3d.me/toby3d/home/internal/domain"
)
@ -10,5 +9,3 @@ import (
type UseCase interface {
Do(ctx context.Context, lang domain.Language, path string) (*domain.Page, error)
}
var ErrNotExist error = errors.New("page not exists")

View File

@ -0,0 +1,80 @@
package usecase
import (
"context"
"fmt"
"path"
"strings"
"source.toby3d.me/toby3d/home/internal/domain"
"source.toby3d.me/toby3d/home/internal/entry"
"source.toby3d.me/toby3d/home/internal/resource"
"source.toby3d.me/toby3d/home/internal/urlutil"
)
type entryUseCase struct {
entries entry.Repository
resources resource.Repository
}
func NewEntryUseCase(entries entry.Repository, resources resource.Repository) entry.UseCase {
return &entryUseCase{
entries: entries,
resources: resources,
}
}
func (ucase *entryUseCase) Do(ctx context.Context, lang domain.Language, p string) (*domain.Page, error) {
targets := make([]string, 0)
hasExt := path.Ext(p) != ""
head, tail := urlutil.ShiftPath(p)
if tail == "/" {
if head = strings.TrimSuffix(head, path.Ext(head)); head == "" {
head = "index"
}
targets = append(targets, head)
}
if head != "index" {
tail = strings.TrimSuffix(tail, path.Ext(tail))
if !strings.HasSuffix(tail, "/index") {
if hasExt {
targets = append([]string{path.Join(head, tail, "index")}, targets...)
} else {
targets = append(targets, path.Join(head, tail, "index"))
}
} else {
targets = append(targets, path.Join(head, tail))
}
}
for i := len(targets) - 1; 0 <= i; i-- {
result, err := ucase.entries.Get(ctx, lang, targets[i])
if err != nil {
continue
}
if result.Resources, _, err = ucase.resources.Fetch(ctx, result.File.Dir()+"*"); err != nil {
return result, nil
}
for _, res := range result.Resources.GetType(domain.ResourceTypePage) {
if res.File.TranslationBaseName() != result.File.TranslationBaseName() {
continue
}
translation, err := ucase.entries.Get(ctx, res.File.Language, targets[i])
if err != nil {
continue
}
result.Translations = append(result.Translations, translation)
}
return result, nil
}
return nil, fmt.Errorf("cannot find page on path '%s': %w", p, entry.ErrNotExist)
}

View File

@ -0,0 +1,70 @@
package usecase_test
import (
"context"
"path/filepath"
"testing"
"testing/fstest"
"github.com/google/go-cmp/cmp"
"source.toby3d.me/toby3d/home/internal/domain"
entryfsrepo "source.toby3d.me/toby3d/home/internal/entry/repository/fs"
"source.toby3d.me/toby3d/home/internal/entry/usecase"
resourcedummyrepo "source.toby3d.me/toby3d/home/internal/resource/repository/dummy"
)
func TestDo(t *testing.T) {
t.Parallel()
pages := entryfsrepo.NewFileSystemPageRepository(fstest.MapFS{
filepath.Join("both", "index.md"): &fstest.MapFile{Data: []byte(`both/index.md`)},
filepath.Join("both.md"): &fstest.MapFile{Data: []byte(`both.md`)},
filepath.Join("file.md"): &fstest.MapFile{Data: []byte(`file.md`)},
filepath.Join("folder", "index.md"): &fstest.MapFile{Data: []byte(`folder/index.md`)},
filepath.Join("foo", "bar", "index.md"): &fstest.MapFile{Data: []byte(`foo/bar/index.md`)},
filepath.Join("foo", "bar.md"): &fstest.MapFile{Data: []byte(`foo/bar.md`)},
filepath.Join("index.md"): &fstest.MapFile{Data: []byte(`index.md`)},
})
ucase := usecase.NewEntryUseCase(pages, resourcedummyrepo.NewDummyResourceRepository())
for name, tc := range map[string]struct {
input string
expect []byte
}{
"root": {"/", []byte(`index.md`)},
"index": {"/index", []byte(`index.md`)},
"index-ext": {"/index.html", []byte(`index.md`)},
"file": {"/file", []byte(`file.md`)},
"file-slash": {"/file/", []byte(`file.md`)},
"file-ext": {"/file.html", []byte(`file.md`)},
"both-ext": {"/both.html", []byte(`both.md`)},
"folder": {"/folder", []byte(`folder/index.md`)},
"folder-slash": {"/folder/", []byte(`folder/index.md`)},
"folder-index": {"/folder/index", []byte(`folder/index.md`)},
"folder-ext": {"/folder/index.html", []byte(`folder/index.md`)},
"both": {"/both", []byte(`both/index.md`)},
"both-slash": {"/both/", []byte(`both/index.md`)},
"both-index": {"/both/index", []byte(`both/index.md`)},
"both-index-ext": {"/both/index.html", []byte(`both/index.md`)},
"sub-folder-index": {"/foo/bar/index", []byte(`foo/bar/index.md`)},
"sub-folder-ext": {"/foo/bar/index.html", []byte(`foo/bar/index.md`)},
"sub-folder-slash": {"/foo/bar/", []byte(`foo/bar/index.md`)},
} {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
actual, err := ucase.Do(context.Background(), domain.LanguageUnd, tc.input)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(string(actual.Content), string(tc.expect)); diff != "" {
t.Error(diff)
}
})
}
}

View File

@ -1,11 +0,0 @@
package page
import (
"context"
"source.toby3d.me/toby3d/home/internal/domain"
)
type Repository interface {
Get(ctx context.Context, lang domain.Language, path string) (*domain.Page, error)
}

View File

@ -1,71 +0,0 @@
package usecase
import (
"context"
"fmt"
"path"
"source.toby3d.me/toby3d/home/internal/domain"
"source.toby3d.me/toby3d/home/internal/page"
"source.toby3d.me/toby3d/home/internal/resource"
"source.toby3d.me/toby3d/home/internal/urlutil"
)
type pageUseCase struct {
pages page.Repository
resources resource.Repository
}
func NewPageUseCase(pages page.Repository, resources resource.Repository) page.UseCase {
return &pageUseCase{
pages: pages,
resources: resources,
}
}
func (ucase *pageUseCase) Do(ctx context.Context, lang domain.Language, p string) (*domain.Page, error) {
ext := path.Ext(p)
if ext == ".html" {
p = p[:len(p)-len(ext)]
}
hasTrailingSlash := p[len(p)-1] == '/'
head, tail := urlutil.ShiftPath(p)
targets := []string{path.Join(head, tail)}
if tail == "/" {
if hasTrailingSlash || ext == "" {
targets = append([]string{path.Join(head, "index")}, targets...)
}
targets = append(targets, head)
}
for i := range targets {
out, err := ucase.pages.Get(ctx, lang, targets[i])
if err != nil {
continue
}
if out.Resources, _, err = ucase.resources.Fetch(ctx, out.File.Dir()+"*"); err != nil {
return out, nil
}
for _, res := range out.Resources.GetType(domain.ResourceTypePage) {
if res.File.TranslationBaseName() != out.File.TranslationBaseName() {
continue
}
translation, err := ucase.pages.Get(ctx, res.File.Language, targets[i])
if err != nil {
continue
}
out.Translations = append(out.Translations, translation)
}
return out, nil
}
return nil, fmt.Errorf("cannot find page on path '%s': %w", p, page.ErrNotExist)
}

View File

@ -1,62 +0,0 @@
package usecase_test
import (
"context"
"path/filepath"
"testing"
"testing/fstest"
"github.com/google/go-cmp/cmp"
"source.toby3d.me/toby3d/home/internal/domain"
pagefsrepo "source.toby3d.me/toby3d/home/internal/page/repository/fs"
"source.toby3d.me/toby3d/home/internal/page/usecase"
"source.toby3d.me/toby3d/home/internal/resource"
)
func TestDo(t *testing.T) {
t.Parallel()
pages := pagefsrepo.NewFileSystemPageRepository(fstest.MapFS{
filepath.Join("both", "index.md"): &fstest.MapFile{Data: []byte(`both/index.md`)},
filepath.Join("folder", "index.md"): &fstest.MapFile{Data: []byte(`folder/index.md`)},
filepath.Join("both.md"): &fstest.MapFile{Data: []byte(`both.md`)},
filepath.Join("file.md"): &fstest.MapFile{Data: []byte(`file.md`)},
filepath.Join("index.md"): &fstest.MapFile{Data: []byte(`index.md`)},
})
ucase := usecase.NewPageUseCase(pages, resource.NewDummyRepository())
for name, tc := range map[string]struct {
input string
expect []byte
}{
"index": {input: "/", expect: []byte(`index.md`)},
"index-ext": {input: "/index.html", expect: []byte(`index.md`)},
"file": {input: "/file", expect: []byte(`file.md`)},
"file-slash": {input: "/file/", expect: []byte(`file.md`)},
"file-ext": {input: "/file.html", expect: []byte(`file.md`)},
"folder": {input: "/folder", expect: []byte(`folder/index.md`)},
"folder-slash": {input: "/folder/", expect: []byte(`folder/index.md`)},
"folder-index": {input: "/folder/index.html", expect: []byte(`folder/index.md`)},
"both": {input: "/both", expect: []byte(`both/index.md`)},
"both-slash": {input: "/both/", expect: []byte(`both/index.md`)},
"both-ext": {input: "/both.html", expect: []byte(`both.md`)},
"both-index": {input: "/both/index.html", expect: []byte(`both/index.md`)},
} {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
actual, err := ucase.Do(context.Background(), domain.LanguageUnd, tc.input)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(string(actual.Content), string(tc.expect)); diff != "" {
t.Error(diff)
}
})
}
}