🚚 Renamed 'page' module back to 'entry'
This commit is contained in:
parent
b046f13098
commit
4026fb9192
|
@ -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")
|
|
@ -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
|
||||
}
|
|
@ -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) {
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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")
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue