Merge branch 'feature/file' into develop
/ docker (push) Successful in 1m11s
Details
/ docker (push) Successful in 1m11s
Details
This commit is contained in:
commit
6080cce45f
|
@ -0,0 +1,102 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
language language.Tag
|
||||||
|
baseFileName string
|
||||||
|
contentBaseName string
|
||||||
|
dir string
|
||||||
|
ext string
|
||||||
|
filename string
|
||||||
|
logicalName string
|
||||||
|
path string
|
||||||
|
translationBaseName string
|
||||||
|
uniqueId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFile(path string) File {
|
||||||
|
out := File{
|
||||||
|
language: language.Tag{},
|
||||||
|
baseFileName: "",
|
||||||
|
contentBaseName: "",
|
||||||
|
dir: filepath.Dir(path) + "/",
|
||||||
|
ext: strings.TrimPrefix(filepath.Ext(path), "."),
|
||||||
|
filename: path,
|
||||||
|
logicalName: filepath.Base(path),
|
||||||
|
path: path,
|
||||||
|
translationBaseName: "",
|
||||||
|
uniqueId: "",
|
||||||
|
}
|
||||||
|
out.path, _ = filepath.Abs(path)
|
||||||
|
out.baseFileName = strings.TrimSuffix(out.logicalName, filepath.Ext(out.logicalName))
|
||||||
|
|
||||||
|
parts := strings.Split(out.baseFileName, ".")
|
||||||
|
out.language = language.Make(parts[len(parts)-1])
|
||||||
|
out.translationBaseName = strings.Join(parts[:len(parts)-1], ".")
|
||||||
|
out.contentBaseName = out.translationBaseName
|
||||||
|
|
||||||
|
switch out.translationBaseName {
|
||||||
|
default:
|
||||||
|
out.contentBaseName = out.translationBaseName
|
||||||
|
case "_index", "index":
|
||||||
|
out.contentBaseName = filepath.Base(out.dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
_, _ = hash.Write([]byte(out.path))
|
||||||
|
out.uniqueId = string(hash.Sum(nil))
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseFileName returns file name without extention.
|
||||||
|
func (f File) BaseFileName() string {
|
||||||
|
return f.baseFileName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) ContentBaseName() string {
|
||||||
|
return f.contentBaseName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dir returns directory path.
|
||||||
|
func (f File) Dir() string {
|
||||||
|
return f.dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ext returns file extention.
|
||||||
|
func (f File) Ext() string {
|
||||||
|
return f.ext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) Filename() string {
|
||||||
|
return f.filename
|
||||||
|
}
|
||||||
|
|
||||||
|
// Language returns language.Tag of current file based on his suffix before
|
||||||
|
// extention.
|
||||||
|
func (f File) Language() language.Tag {
|
||||||
|
return f.language
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) LogicalName() string {
|
||||||
|
return f.logicalName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) Path() string {
|
||||||
|
return f.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) TranslationBaseName() string {
|
||||||
|
return f.translationBaseName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) UniqueID() string {
|
||||||
|
return f.uniqueId
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
package domain_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"source.toby3d.me/toby3d/home/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testRegularFile string = filepath.Join("news", "a.en.md")
|
||||||
|
testLeafFile string = filepath.Join("news", "b", "index.en.md")
|
||||||
|
testBranchFile string = filepath.Join("news", "_index.en.md")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFile_BaseFileName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
input, expect string
|
||||||
|
}{
|
||||||
|
"regular": {testRegularFile, "a.en"},
|
||||||
|
"leaf": {testLeafFile, "index.en"},
|
||||||
|
"branch": {testBranchFile, "_index.en"},
|
||||||
|
} {
|
||||||
|
name, tc := name, tc
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(tc.input).BaseFileName(); actual != tc.expect {
|
||||||
|
t.Errorf("BaseFileName() = '%s', want '%s'", actual, tc.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFile_ContentBaseName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
input, expect string
|
||||||
|
}{
|
||||||
|
"regular": {testRegularFile, "a"},
|
||||||
|
"leaf": {testLeafFile, "b"},
|
||||||
|
"branch": {testBranchFile, "news"},
|
||||||
|
} {
|
||||||
|
name, tc := name, tc
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(tc.input).ContentBaseName(); actual != tc.expect {
|
||||||
|
t.Errorf("ContentBaseName() = '%s', want '%s'", actual, tc.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFile_Dir(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
input, expect string
|
||||||
|
}{
|
||||||
|
"regular": {testRegularFile, "news/"},
|
||||||
|
"leaf": {testLeafFile, "news/b/"},
|
||||||
|
"branch": {testBranchFile, "news/"},
|
||||||
|
} {
|
||||||
|
name, tc := name, tc
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(tc.input).Dir(); actual != tc.expect {
|
||||||
|
t.Errorf("Dir() = '%s', want '%s'", actual, tc.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFile_Ext(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const expect string = "md"
|
||||||
|
|
||||||
|
for name, input := range map[string]string{
|
||||||
|
"regular": testRegularFile,
|
||||||
|
"leaf": testLeafFile,
|
||||||
|
"branch": testBranchFile,
|
||||||
|
} {
|
||||||
|
name, input := name, input
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(input).Ext(); actual != expect {
|
||||||
|
t.Errorf("Ext() = '%s', want '%s'", actual, expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFile_Language(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var expect language.Tag = language.English
|
||||||
|
|
||||||
|
for name, input := range map[string]string{
|
||||||
|
"regular": testRegularFile,
|
||||||
|
"leaf": testLeafFile,
|
||||||
|
"branch": testBranchFile,
|
||||||
|
} {
|
||||||
|
name, input := name, input
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(input).Language(); actual != expect {
|
||||||
|
t.Errorf("Language() = '%s', want '%s'", actual, expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFile_LogicalName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
input, expect string
|
||||||
|
}{
|
||||||
|
"regular": {testRegularFile, "a.en.md"},
|
||||||
|
"leaf": {testLeafFile, "index.en.md"},
|
||||||
|
"branch": {testBranchFile, "_index.en.md"},
|
||||||
|
} {
|
||||||
|
name, tc := name, tc
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(tc.input).LogicalName(); actual != tc.expect {
|
||||||
|
t.Errorf("LogicalName() = '%s', want '%s'", actual, tc.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFile_TranslationBaseName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
input, expect string
|
||||||
|
}{
|
||||||
|
"regular": {testRegularFile, "a"},
|
||||||
|
"leaf": {testLeafFile, "index"},
|
||||||
|
"branch": {testBranchFile, "_index"},
|
||||||
|
} {
|
||||||
|
name, tc := name, tc
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if actual := domain.NewFile(tc.input).TranslationBaseName(); actual != tc.expect {
|
||||||
|
t.Errorf("TranslationBaseName() = '%s', want '%s'", actual, tc.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,9 +5,13 @@ import "golang.org/x/text/language"
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Language language.Tag
|
Language language.Tag
|
||||||
Params map[string]any
|
Params map[string]any
|
||||||
|
File File
|
||||||
Description string
|
Description string
|
||||||
Title string
|
Title string
|
||||||
Content []byte
|
Content []byte
|
||||||
Resources Resources
|
Resources Resources
|
||||||
IsHome bool
|
}
|
||||||
|
|
||||||
|
func (p Page) IsHome() bool {
|
||||||
|
return p.File.dir == "./" && p.File.translationBaseName == "index"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,9 @@ import (
|
||||||
type Site struct {
|
type Site struct {
|
||||||
Language language.Tag
|
Language language.Tag
|
||||||
BaseURL *url.URL
|
BaseURL *url.URL
|
||||||
TimeZone *time.Location
|
|
||||||
Params map[string]any
|
Params map[string]any
|
||||||
|
TimeZone *time.Location
|
||||||
|
File File
|
||||||
Title string
|
Title string
|
||||||
Resources Resources
|
Resources Resources
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/adrg/frontmatter"
|
"github.com/adrg/frontmatter"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
@ -45,11 +44,11 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang language.Tag
|
||||||
ext = "." + base.String() + ext
|
ext = "." + base.String() + ext
|
||||||
}
|
}
|
||||||
|
|
||||||
index := p + ext
|
target := p + ext
|
||||||
|
|
||||||
f, err := repo.dir.Open(index)
|
f, err := repo.dir.Open(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot open '%s' page file: %w", index, err)
|
return nil, fmt.Errorf("cannot open '%s' page file: %w", target, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
@ -61,12 +60,12 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang language.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
return &domain.Page{
|
return &domain.Page{
|
||||||
|
File: domain.NewFile(target),
|
||||||
Language: lang,
|
Language: lang,
|
||||||
Title: data.Title,
|
Title: data.Title,
|
||||||
Content: data.Content,
|
Content: data.Content,
|
||||||
Description: data.Description,
|
Description: data.Description,
|
||||||
Params: data.Params,
|
Params: data.Params,
|
||||||
Resources: make([]*domain.Resource, 0),
|
Resources: make([]*domain.Resource, 0),
|
||||||
IsHome: strings.HasPrefix(index, "index.") && strings.HasSuffix(index, ".md"),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (ucase *pageUseCase) Do(ctx context.Context, lang language.Tag, p string) (
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
out.Resources, _, _ = ucase.statics.Fetch(ctx, path.Dir(targets[i]))
|
out.Resources, _, _ = ucase.statics.Fetch(ctx, out.File.Dir()+"*")
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ func (repo *fileSystemSiteRepository) Get(ctx context.Context, lang language.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
return &domain.Site{
|
return &domain.Site{
|
||||||
|
File: domain.NewFile(target),
|
||||||
Language: lang,
|
Language: lang,
|
||||||
Title: data.Title,
|
Title: data.Title,
|
||||||
BaseURL: data.BaseURL.URL,
|
BaseURL: data.BaseURL.URL,
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (ucase *siteUseCase) Do(ctx context.Context, lang language.Tag) (*domain.Si
|
||||||
return nil, fmt.Errorf("cannot find base site data: %w", err)
|
return nil, fmt.Errorf("cannot find base site data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
out.Resources, _, _ = ucase.statics.Fetch(ctx, ".")
|
out.Resources, _, _ = ucase.statics.Fetch(ctx, "")
|
||||||
|
|
||||||
sub, err := ucase.sites.Get(ctx, lang)
|
sub, err := ucase.sites.Get(ctx, lang)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -12,7 +12,7 @@ type (
|
||||||
Get(ctx context.Context, path string) (*domain.Resource, error)
|
Get(ctx context.Context, path string) (*domain.Resource, error)
|
||||||
|
|
||||||
// Fetch returns all resources from dir recursevly.
|
// Fetch returns all resources from dir recursevly.
|
||||||
Fetch(ctx context.Context, dir string) (domain.Resources, int, error)
|
Fetch(ctx context.Context, pattern string) (domain.Resources, int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
dummyRepository struct{}
|
dummyRepository struct{}
|
||||||
|
@ -26,6 +26,6 @@ func (dummyRepository) Get(ctx context.Context, path string) (*domain.Resource,
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dummyRepository) Fetch(ctx context.Context, dir string) (domain.Resources, int, error) {
|
func (dummyRepository) Fetch(ctx context.Context, pattern string) (domain.Resources, int, error) {
|
||||||
return nil, 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,28 +46,40 @@ func (repo *fileServerStaticRepository) Get(ctx context.Context, p string) (*dom
|
||||||
return domain.NewResource(info.ModTime(), content, p), nil
|
return domain.NewResource(info.ModTime(), content, p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *fileServerStaticRepository) Fetch(ctx context.Context, dir string) (domain.Resources, int, error) {
|
func (repo *fileServerStaticRepository) Fetch(ctx context.Context, pattern string) (domain.Resources, int, error) {
|
||||||
targets := make([]string, 0)
|
var (
|
||||||
if err := fs.WalkDir(repo.root, dir, func(path string, de fs.DirEntry, err error) error {
|
err error
|
||||||
if err != nil {
|
matches []string
|
||||||
return fmt.Errorf("received error while walking through '%s': %w", dir, err)
|
)
|
||||||
}
|
|
||||||
|
if pattern != "" {
|
||||||
|
if matches, err = fs.Glob(repo.root, pattern); err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("cannot match any static by pattern '%s': %w", pattern, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = fs.WalkDir(repo.root, ".", func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("catched error while walk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = append(matches, path)
|
||||||
|
|
||||||
if de.IsDir() {
|
|
||||||
return nil
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("cannot walk through static directories: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
targets = append(targets, path)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("cannot read directory on path '%s': %w", dir, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out := make(domain.Resources, len(targets))
|
out := make(domain.Resources, 0, len(matches))
|
||||||
|
|
||||||
for i := range targets {
|
for i := range matches {
|
||||||
out[i], _ = repo.Get(ctx, targets[i])
|
if r, err := repo.Get(ctx, matches[i]); err == nil {
|
||||||
|
out = append(out, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, len(out), nil
|
return out, len(out), nil
|
||||||
|
|
Loading…
Reference in New Issue