From f378bebacebe96e2ecc31e912492f89200b461ce Mon Sep 17 00:00:00 2001 From: Linghua Zhang Date: Mon, 1 Jan 2024 19:40:42 +0900 Subject: [PATCH] fix(export): add error handling for duplicated slugs Close #4 --- internal/cmd/preview.go | 5 +++-- internal/export/export.go | 11 ++++++++--- internal/export/export_test.go | 15 ++++++++++++--- internal/indexer/indexer.go | 17 +++++++++++++---- internal/indexer/indexer_test.go | 15 ++++++++++++++- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/internal/cmd/preview.go b/internal/cmd/preview.go index 4625273..e6334e5 100644 --- a/internal/cmd/preview.go +++ b/internal/cmd/preview.go @@ -33,7 +33,8 @@ func preview(cmd *cobra.Command, args []string) { log.Debug("Creating Preview...") config := config.Shared() - index := indexer.Build(config.GetSectionMetadata(), config.GetExtractOption()) + index, err := indexer.Build(config.GetSectionMetadata(), config.GetExtractOption()) + utils.CheckFatalError(err, "Failed to build index") http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { handleRoot( @@ -62,7 +63,7 @@ func preview(cmd *cobra.Command, args []string) { } log.Info("Server started -> http://localhost:%d", port) - err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil) + err = http.ListenAndServe(fmt.Sprintf(":%d", port), nil) utils.CheckFatalError(err, "Failed to listen the port") } diff --git a/internal/export/export.go b/internal/export/export.go index 4a51b71..e903332 100644 --- a/internal/export/export.go +++ b/internal/export/export.go @@ -25,7 +25,7 @@ type ProgressFunc func(path string) type Context interface { cleanDirectory(outputPath string) error - buildIndex(cfg config.Config) []indexer.Section + buildIndex(cfg config.Config) ([]indexer.Section, error) exportPhotos(sections []indexer.Section, outputPath string, cache cache.Cache, progressFunc ProgressFunc) generateIndexHtml(cfg config.Config, sections []indexer.Section, path string, minimize bool) processOtherFolders(folders []string, outputPath string, minimize bool, messageFunc func(src string, dst string)) @@ -59,7 +59,12 @@ func export(cfg config.Config, outputPath string, minimize bool, cache cache.Cac spinnerMsg("Building index %s", outputPath) photosDirectory := files.OutputPhotosFilePath(outputPath) - section := ctx.buildIndex(cfg) + section, err := ctx.buildIndex(cfg) + if err != nil { + ctx.cleanDirectory(outputPath) + utils.CheckFatalError(err, "Failed t build index.") + } + ctx.exportPhotos(section, photosDirectory, cache, func(path string) { spinnerMsg("%s", path) }) @@ -84,7 +89,7 @@ func (ctx DefaultExportContext) cleanDirectory(outputPath string) error { return files.PruneDirectory(outputPath) } -func (ctx DefaultExportContext) buildIndex(cfg config.Config) []indexer.Section { +func (ctx DefaultExportContext) buildIndex(cfg config.Config) ([]indexer.Section, error) { return indexer.Build(cfg.GetSectionMetadata(), cfg.GetExtractOption()) } diff --git a/internal/export/export_test.go b/internal/export/export_test.go index 32a09f9..db4034d 100644 --- a/internal/export/export_test.go +++ b/internal/export/export_test.go @@ -40,8 +40,17 @@ func (m *MockContext) cleanDirectory(outputPath string) error { return m.Called(outputPath).Error(0) } -func (m *MockContext) buildIndex(cfg config.Config) []indexer.Section { - return m.Called(cfg).Get(0).([]indexer.Section) +func (m *MockContext) buildIndex(cfg config.Config) ([]indexer.Section, error) { + args := m.Called(cfg) + var sections []indexer.Section + var err error + if args.Get(0) != nil { + sections = args.Get(0).([]indexer.Section) + } + if args.Get(1) != nil { + err = args.Get(1).(error) + } + return sections, err } func (m *MockContext) exportPhotos(sections []indexer.Section, outputPath string, cache cache.Cache, progressFunc ProgressFunc) { @@ -104,7 +113,7 @@ func TestExportPhotos(t *testing.T) { mockCtx := new(MockContext) mockCtx.On("cleanDirectory", mock.Anything).Return(nil) - mockCtx.On("buildIndex", mock.Anything).Return(sections) + mockCtx.On("buildIndex", mock.Anything).Return(sections, nil) mockCtx.On("exportPhotos", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return() mockCtx.On("generateIndexHtml", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return() mockCtx.On("processOtherFolders", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return() diff --git a/internal/indexer/indexer.go b/internal/indexer/indexer.go index db4459b..86f8988 100644 --- a/internal/indexer/indexer.go +++ b/internal/indexer/indexer.go @@ -1,6 +1,7 @@ package indexer import ( + "fmt" "html/template" "os" "path/filepath" @@ -28,23 +29,31 @@ type ImageSet struct { OriginalSize ImageSize } -func Build(metadata []config.SectionMetadata, option config.ExtractOption) []Section { +func Build(metadata []config.SectionMetadata, option config.ExtractOption) ([]Section, error) { sections := []Section{} + slugs := map[string]bool{} + for _, val := range metadata { + slug := val.Slug + if slugs[slug] { + return nil, fmt.Errorf("Slug \"%s\" already exists. Slug needs to be unique.", slug) + } + + log.Debug("Extacting section [%s][/%s] %s", val.Title, val.Slug, val.Folder) s := Section{ Title: val.Title, Text: val.Text, - Slug: val.Slug, + Slug: slug, Folder: val.Folder, Ascending: val.Ascending, ImageSets: buildImageSets(val.Folder, val.Ascending, option), } - log.Debug("Extacting section [%s][/%s] %s", val.Title, val.Slug, val.Folder) + slugs[slug] = true sections = append(sections, s) } - return sections + return sections, nil } func buildImageSets(folder string, ascending bool, option config.ExtractOption) []ImageSet { diff --git a/internal/indexer/indexer_test.go b/internal/indexer/indexer_test.go index f518967..34b5899 100644 --- a/internal/indexer/indexer_test.go +++ b/internal/indexer/indexer_test.go @@ -26,7 +26,7 @@ func TestBuild(t *testing.T) { data := []config.SectionMetadata{meta1, meta2} - sections := Build(data, defaultOption) + sections, _ := Build(data, defaultOption) assert.Equal(t, 2, len(sections)) assert.Equal(t, testdata.Collection1["title"], sections[0].Title) @@ -38,6 +38,19 @@ func TestBuild(t *testing.T) { assert.Equal(t, testdata.Collection2FileName3, sections[1].ImageSets[0].FileName) } +func TestBuildDuplicatedSlugs(t *testing.T) { + var meta1 config.SectionMetadata + var meta2 config.SectionMetadata + mapstructure.Decode(testdata.Collection1, &meta1) + mapstructure.Decode(testdata.Collection2, &meta2) + meta2.Slug = meta1.Slug + + data := []config.SectionMetadata{meta1, meta2} + + _, err := Build(data, defaultOption) + assert.NotNil(t, err) +} + func TestBuildImageSets(t *testing.T) { expectedAscendingFileNames := []string{ testdata.Collection1FileName1,