diff --git a/pkg/enum/maps.go b/pkg/enum/bool.go similarity index 52% rename from pkg/enum/maps.go rename to pkg/enum/bool.go index e2c7c2fc1..4ce15e38d 100644 --- a/pkg/enum/maps.go +++ b/pkg/enum/bool.go @@ -1,6 +1,15 @@ package enum -// YesMap map to represent Yes in the following languages Czech, Danish, Dutch, English, French, German, Indonesian, Italian, Polish, Portuguese, Russian, Ukrainian. +// True and False specify boolean string representations. +const ( + True = "true" + False = "false" +) + +// YesMap enumerates lower-case tokens we accept as an affirmative answer across +// supported languages (Czech, Danish, Dutch, English, French, German, +// Indonesian, Italian, Polish, Portuguese, Russian, Ukrainian). Callers should +// trim and lowercase input before performing lookups. var YesMap = map[string]struct{}{ "1": {}, "yes": {}, @@ -12,6 +21,7 @@ var YesMap = map[string]struct{}{ "ja": {}, "oui": {}, "si": {}, + "sí": {}, "tak": {}, "sim": {}, "да": {}, @@ -19,7 +29,9 @@ var YesMap = map[string]struct{}{ "так": {}, } -// NoMap map to represent No in the following languages Czech, Danish, Dutch, English, French, German, Indonesian, Italian, Polish, Portuguese, Russian, Ukrainian. +// NoMap enumerates lower-case tokens we accept as a negative answer across the +// same set of supported languages. Callers should trim and lowercase input +// before performing lookups. var NoMap = map[string]struct{}{ "0": {}, "no": {}, diff --git a/pkg/enum/enum.go b/pkg/enum/enum.go index 614faa210..d0b20a92c 100644 --- a/pkg/enum/enum.go +++ b/pkg/enum/enum.go @@ -1,5 +1,6 @@ /* -Package enum provides declarations of enum. +Package enum centralizes canonical string constants and lookup tables that we +use when interpreting enumerations throughout PhotoPrism. Copyright (c) 2018 - 2025 PhotoPrism UG. All rights reserved. diff --git a/pkg/enum/strings.go b/pkg/enum/strings.go deleted file mode 100644 index e8ddb4660..000000000 --- a/pkg/enum/strings.go +++ /dev/null @@ -1,7 +0,0 @@ -package enum - -// True and False specify boolean string representations. -const ( - True = "true" - False = "false" -) diff --git a/pkg/list/strings.go b/pkg/list/strings.go index 1c68c96e6..b74f86295 100644 --- a/pkg/list/strings.go +++ b/pkg/list/strings.go @@ -1,6 +1,8 @@ package list -import "github.com/photoprism/photoprism/pkg/enum" +import ( + "github.com/photoprism/photoprism/pkg/enum" +) // StringLengthLimit specifies the maximum length of string return values. var StringLengthLimit = 767 diff --git a/pkg/txt/bool.go b/pkg/txt/bool.go deleted file mode 100644 index 46bd6fee7..000000000 --- a/pkg/txt/bool.go +++ /dev/null @@ -1,56 +0,0 @@ -package txt - -import ( - "strings" - - "github.com/photoprism/photoprism/pkg/enum" -) - -// Bool casts a string to bool. -func Bool(s string) bool { - s = strings.TrimSpace(s) - - if s == "" || No(s) { - return false - } - - return true -} - -// Yes tests if a string represents "yes" in the following languages Czech, Danish, Dutch, English, French, German, Indonesian, Italian, Polish, Portuguese, Russian, Ukrainian. -func Yes(s string) (result bool) { - t := strings.ToLower(strings.TrimSpace(s)) - if t == "" { - return false - } else if strings.Contains(t, " ") { - result = false - } else { - - _, result = enum.YesMap[t] - } - return result -} - -// No tests if a string represents "no" in the following languages Czech, Danish, Dutch, English, French, German, Indonesian, Italian, Polish, Portuguese, Russian, Ukrainian. -func No(s string) (result bool) { - t := strings.ToLower(strings.TrimSpace(s)) - if t == "" { - return false - } else if strings.Contains(t, " ") { - result = false - } else { - _, result = enum.NoMap[t] - } - return result -} - -// New tests if a string represents "new". -func New(s string) bool { - if s == "" { - return false - } - - s = strings.ToLower(strings.TrimSpace(s)) - - return s == EnNew -} diff --git a/pkg/txt/const.go b/pkg/txt/const.go new file mode 100644 index 000000000..66cfddcca --- /dev/null +++ b/pkg/txt/const.go @@ -0,0 +1,21 @@ +package txt + +import ( + "github.com/photoprism/photoprism/pkg/enum" +) + +// True and False specify boolean string representations. +const ( + True = enum.True + False = enum.False +) + +// Additional english language strings. +const ( + EnOr = "or" + EnAnd = "and" + EnWith = "with" + EnIn = "in" + EnAt = "at" + EnNew = "new" +) diff --git a/pkg/txt/en.go b/pkg/txt/en.go deleted file mode 100644 index f567baff7..000000000 --- a/pkg/txt/en.go +++ /dev/null @@ -1,10 +0,0 @@ -package txt - -const ( - EnOr = "or" - EnAnd = "and" - EnWith = "with" - EnIn = "in" - EnAt = "at" - EnNew = "new" -) diff --git a/pkg/txt/match.go b/pkg/txt/match.go new file mode 100644 index 000000000..7efa375a4 --- /dev/null +++ b/pkg/txt/match.go @@ -0,0 +1,57 @@ +package txt + +import ( + "strings" + "unicode" + + "github.com/photoprism/photoprism/pkg/enum" +) + +// New tests if a string represents "new". +func New(s string) bool { + if s == "" { + return false + } + + s = strings.ToLower(strings.TrimSpace(s)) + + return s == EnNew +} + +// Bool casts a string to bool by treating any non-empty value that is not a +// known negative token as true. +func Bool(s string) bool { + s = strings.TrimSpace(s) + + if s == "" || No(s) { + return false + } + + return true +} + +// Yes reports whether s matches a supported affirmative token in the languages +// represented by enum.YesMap. +func Yes(s string) bool { + return matchEnumToken(enum.YesMap, s) +} + +// No reports whether s matches a supported negative token in the languages +// represented by enum.NoMap. +func No(s string) bool { + return matchEnumToken(enum.NoMap, s) +} + +// matchEnumToken normalizes s and checks whether it exists in tokens. +func matchEnumToken(tokens map[string]struct{}, s string) bool { + t := strings.ToLower(strings.TrimSpace(s)) + if t == "" { + return false + } + if strings.IndexFunc(t, unicode.IsSpace) >= 0 { + return false + } + + _, ok := tokens[t] + return ok +} diff --git a/pkg/txt/bool_test.go b/pkg/txt/match_test.go similarity index 91% rename from pkg/txt/bool_test.go rename to pkg/txt/match_test.go index 40c0f9bc6..63cdf7e13 100644 --- a/pkg/txt/bool_test.go +++ b/pkg/txt/match_test.go @@ -6,6 +6,30 @@ import ( "github.com/stretchr/testify/assert" ) +func TestNew(t *testing.T) { + t.Run("Empty", func(t *testing.T) { + assert.False(t, New("")) + }) + t.Run("EnNew", func(t *testing.T) { + assert.True(t, New(EnNew)) + }) + t.Run("Spaces", func(t *testing.T) { + assert.True(t, New(" new ")) + }) + t.Run("Uppercase", func(t *testing.T) { + assert.True(t, New("NEW")) + }) + t.Run("Lowercase", func(t *testing.T) { + assert.True(t, New("new")) + }) + t.Run("True", func(t *testing.T) { + assert.True(t, New("New")) + }) + t.Run("False", func(t *testing.T) { + assert.False(t, New("non")) + }) +} + func TestBool(t *testing.T) { t.Run("NotEmpty", func(t *testing.T) { assert.True(t, Bool("Browse your life in pictures")) @@ -34,6 +58,9 @@ func TestBool(t *testing.T) { t.Run("Empty", func(t *testing.T) { assert.False(t, Bool("")) }) + t.Run("UppercaseNo", func(t *testing.T) { + assert.False(t, Bool("NO")) + }) } func TestYes(t *testing.T) { @@ -98,6 +125,12 @@ func TestYes(t *testing.T) { assert.True(t, Yes("да")) assert.True(t, Yes("Да")) }) + t.Run("TabSeparatedPhrase", func(t *testing.T) { + assert.False(t, Yes("yes\tplease")) + }) + t.Run("NonBreakingSpace", func(t *testing.T) { + assert.False(t, Yes("yes\u00a0please")) + }) } func TestNo(t *testing.T) { @@ -181,28 +214,10 @@ func TestNo(t *testing.T) { t.Run("Nein", func(t *testing.T) { assert.True(t, No("nein")) }) -} - -func TestNew(t *testing.T) { - t.Run("Empty", func(t *testing.T) { - assert.False(t, New("")) + t.Run("TabSeparatedPhrase", func(t *testing.T) { + assert.False(t, No("no\tthanks")) }) - t.Run("EnNew", func(t *testing.T) { - assert.True(t, New(EnNew)) - }) - t.Run("Spaces", func(t *testing.T) { - assert.True(t, New(" new ")) - }) - t.Run("Uppercase", func(t *testing.T) { - assert.True(t, New("NEW")) - }) - t.Run("Lowercase", func(t *testing.T) { - assert.True(t, New("new")) - }) - t.Run("True", func(t *testing.T) { - assert.True(t, New("New")) - }) - t.Run("False", func(t *testing.T) { - assert.False(t, New("non")) + t.Run("NonBreakingSpace", func(t *testing.T) { + assert.True(t, No("нет\u00a0")) }) }