diff --git a/internal/page/repository/fs/fs_page.go b/internal/page/repository/fs/fs_page.go index 6eed6bc..a5a247e 100644 --- a/internal/page/repository/fs/fs_page.go +++ b/internal/page/repository/fs/fs_page.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io/fs" - "path/filepath" "github.com/adrg/frontmatter" "golang.org/x/text/language" @@ -38,12 +37,12 @@ func NewFileSystemPageRepository(rootDir fs.FS) page.Repository { } func (repo *fileSystemPageRepository) Get(ctx context.Context, lang language.Tag, path string) (*domain.Page, error) { - target := "index." + lang.String() + ".md" - if lang == language.Und { - target = "index.md" + ext := ".md" + if lang != language.Und { + ext = "." + lang.String() + ext } - target = filepath.Join(path, target) + target := path + ext f, err := repo.dir.Open(target) if err != nil { diff --git a/internal/page/repository/fs/fs_page_test.go b/internal/page/repository/fs/fs_page_test.go index 80cf865..610f814 100644 --- a/internal/page/repository/fs/fs_page_test.go +++ b/internal/page/repository/fs/fs_page_test.go @@ -18,38 +18,38 @@ func TestGet(t *testing.T) { t.Parallel() testData := fstest.MapFS{ - filepath.Join("index.ru.md"): &fstest.MapFile{ - Data: []byte("---\ntitle: страница\n---\nпривет, мир!\n"), - }, - filepath.Join("dir", "index.en.md"): &fstest.MapFile{ - Data: []byte("---\ntitle: page\n---\nhello, world!\n"), - }, + 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("index.md"): &fstest.MapFile{Data: []byte("index.md")}, } repo := repository.NewFileSystemPageRepository(testData) for name, tc := range map[string]struct { - lang language.Tag expect *domain.Page - path string + input string }{ - "file": { - lang: language.English, - path: path.Join("dir"), - expect: &domain.Page{ - Language: language.English, - Title: "page", - Content: []byte("hello, world!\n"), - }, + "index": { + input: path.Join("index"), + expect: &domain.Page{Content: []byte("index.md")}, }, - "dir": { - lang: language.Russian, - path: path.Join(""), - expect: &domain.Page{ - Language: language.Russian, - Title: "страница", - Content: []byte("привет, мир!\n"), - }, + "file": { + input: path.Join("file"), + expect: &domain.Page{Content: []byte("file.md")}, + }, + "folder": { + input: path.Join("folder", "index"), + expect: &domain.Page{Content: []byte("folder/index.md")}, + }, + "both-file": { + input: path.Join("both"), + expect: &domain.Page{Content: []byte("both.md")}, + }, + "both-folder": { + input: path.Join("both", "index"), + expect: &domain.Page{Content: []byte("both/index.md")}, }, } { name, tc := name, tc @@ -57,7 +57,7 @@ func TestGet(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - out, err := repo.Get(context.Background(), tc.lang, tc.path) + out, err := repo.Get(context.Background(), language.Und, tc.input) if err != nil { t.Fatal(err) } diff --git a/internal/page/usecase.go b/internal/page/usecase.go index 6499980..9b8c1b1 100644 --- a/internal/page/usecase.go +++ b/internal/page/usecase.go @@ -2,6 +2,7 @@ package page import ( "context" + "errors" "golang.org/x/text/language" @@ -11,3 +12,5 @@ import ( type UseCase interface { Do(ctx context.Context, lang language.Tag, path string) (*domain.Page, error) } + +var ErrNotExist error = errors.New("page not exists") diff --git a/internal/page/usecase/page_ucase.go b/internal/page/usecase/page_ucase.go index 80f9ba1..fda05b9 100644 --- a/internal/page/usecase/page_ucase.go +++ b/internal/page/usecase/page_ucase.go @@ -3,11 +3,13 @@ package usecase import ( "context" "fmt" + "path" "golang.org/x/text/language" "source.toby3d.me/toby3d/home/internal/domain" "source.toby3d.me/toby3d/home/internal/page" + "source.toby3d.me/toby3d/home/internal/urlutil" ) type pageUseCase struct { @@ -20,11 +22,32 @@ func NewPageUseCase(pages page.Repository) page.UseCase { } } -func (ucase *pageUseCase) Do(ctx context.Context, lang language.Tag, path string) (*domain.Page, error) { - page, err := ucase.pages.Get(ctx, lang, path) - if err != nil { - return nil, fmt.Errorf("cannot find '%s' page: %w", path, err) +func (ucase *pageUseCase) Do(ctx context.Context, lang language.Tag, p string) (*domain.Page, error) { + ext := path.Ext(p) + if ext == ".html" { + p = p[:len(p)-len(ext)] } - return page, nil + 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 + } + + return out, nil + } + + return nil, fmt.Errorf("cannot find page on path '%s': %w", p, page.ErrNotExist) } diff --git a/internal/page/usecase/page_ucase_test.go b/internal/page/usecase/page_ucase_test.go new file mode 100644 index 0000000..146ad47 --- /dev/null +++ b/internal/page/usecase/page_ucase_test.go @@ -0,0 +1,59 @@ +package usecase_test + +import ( + "context" + "path/filepath" + "testing" + "testing/fstest" + + "github.com/google/go-cmp/cmp" + "golang.org/x/text/language" + + pagefsrepo "source.toby3d.me/toby3d/home/internal/page/repository/fs" + "source.toby3d.me/toby3d/home/internal/page/usecase" +) + +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) + + for name, tc := range map[string]struct { + input string + expect []byte + }{ + "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(), language.Und, tc.input) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(string(actual.Content), string(tc.expect)); diff != "" { + t.Error(diff) + } + }) + } +} diff --git a/internal/site/repository/fs/fs_site.go b/internal/site/repository/fs/fs_site.go index 174ec24..63baf0e 100644 --- a/internal/site/repository/fs/fs_site.go +++ b/internal/site/repository/fs/fs_site.go @@ -37,11 +37,13 @@ func NewFileSystemSiteRepository(rootDir fs.FS) site.Repository { } func (repo *fileSystemSiteRepository) Get(ctx context.Context, lang language.Tag) (*domain.Site, error) { - target := "index." + lang.String() + ".md" - if lang == language.Und { - target = "index.md" + ext := ".md" + if lang != language.Und { + ext = "." + lang.String() + ext } + target := "index" + ext + f, err := repo.dir.Open(target) if err != nil { return nil, fmt.Errorf("cannot open '%s' site file: %w", target, err)