Search: Add Filters for ISO, FNumber and Focal Length Range #3818
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
bac10302da
commit
6a452bcf43
10 changed files with 552 additions and 226 deletions
|
@ -78,12 +78,15 @@ type SearchPhotos struct {
|
|||
Review bool `form:"review" notes:"Finds pictures in review"` // Find photos in review
|
||||
Camera string `form:"camera" example:"camera:canon" notes:"Camera Make/Model Name"` // Camera UID or name
|
||||
Lens string `form:"lens" example:"lens:ef24" notes:"Lens Make/Model Name"` // Lens UID or name
|
||||
Before time.Time `form:"before" time_format:"2006-01-02" notes:"Finds pictures taken before this date"` // Finds images taken before date
|
||||
After time.Time `form:"after" time_format:"2006-01-02" notes:"Finds pictures taken after this date"` // Finds images taken after date
|
||||
Count int `form:"count" binding:"required" serialize:"-"` // Result FILE limit
|
||||
Offset int `form:"offset" serialize:"-"` // Result FILE offset
|
||||
Order string `form:"order" serialize:"-"` // Sort order
|
||||
Merged bool `form:"merged" serialize:"-"` // Merge FILES in response
|
||||
Iso string `form:"iso" example:"iso:200-400" notes:"ISO Range"`
|
||||
F string `form:"f" example:"f:2.8-4.5" notes:"F-Number Range"`
|
||||
Mm string `form:"mm" example:"mm:28-35" notes:"Focal Length Range"`
|
||||
Before time.Time `form:"before" time_format:"2006-01-02" notes:"Finds pictures taken before this date"` // Finds images taken before date
|
||||
After time.Time `form:"after" time_format:"2006-01-02" notes:"Finds pictures taken after this date"` // Finds images taken after date
|
||||
Count int `form:"count" binding:"required" serialize:"-"` // Result FILE limit
|
||||
Offset int `form:"offset" serialize:"-"` // Result FILE offset
|
||||
Order string `form:"order" serialize:"-"` // Sort order
|
||||
Merged bool `form:"merged" serialize:"-"` // Merge FILES in response
|
||||
}
|
||||
|
||||
func (f *SearchPhotos) GetQuery() string {
|
||||
|
|
|
@ -67,6 +67,9 @@ type SearchPhotosGeo struct {
|
|||
Color string `form:"color"`
|
||||
Camera int `form:"camera"`
|
||||
Lens int `form:"lens"`
|
||||
Iso string `form:"iso" example:"iso:200-400" notes:"ISO Range"`
|
||||
F string `form:"f" example:"f:2.8-4.5" notes:"F-Number Range"`
|
||||
Mm string `form:"mm" example:"mm:28-35" notes:"Focal Length Range"`
|
||||
Count int `form:"count" serialize:"-"`
|
||||
Offset int `form:"offset" serialize:"-"`
|
||||
}
|
||||
|
|
|
@ -494,6 +494,21 @@ func searchPhotos(f form.SearchPhotos, sess *entity.Session, resultCols string)
|
|||
s = s.Where("lenses.lens_name LIKE ? OR lenses.lens_model LIKE ? OR lenses.lens_slug LIKE ?", v, v, v)
|
||||
}
|
||||
|
||||
// Filter by ISO Range.
|
||||
if rangeStart, rangeEnd, rangeErr := txt.IntRange(f.Iso, 0, 10000000); rangeErr == nil {
|
||||
s = s.Where("photos.photo_iso >= ? AND photos.photo_iso <= ?", rangeStart, rangeEnd)
|
||||
}
|
||||
|
||||
// Filter by F-Number Range.
|
||||
if rangeStart, rangeEnd, rangeErr := txt.FloatRange(f.F, 0, 10000000); rangeErr == nil {
|
||||
s = s.Where("photos.photo_f_number >= ? AND photos.photo_f_number <= ?", rangeStart-0.01, rangeEnd+0.01)
|
||||
}
|
||||
|
||||
// Filter by Focal Length Range.
|
||||
if rangeStart, rangeEnd, rangeErr := txt.IntRange(f.Mm, 0, 10000000); rangeErr == nil {
|
||||
s = s.Where("photos.photo_focal_length >= ? AND photos.photo_focal_length <= ?", rangeStart, rangeEnd)
|
||||
}
|
||||
|
||||
// Filter by year.
|
||||
if f.Year != "" {
|
||||
s = s.Where(AnyInt("photos.photo_year", f.Year, txt.Or, entity.UnknownYear, txt.YearMax))
|
||||
|
|
|
@ -407,6 +407,21 @@ func UserPhotosGeo(f form.SearchPhotosGeo, sess *entity.Session) (results GeoRes
|
|||
s = s.Where("photos.lens_id = ?", f.Lens)
|
||||
}
|
||||
|
||||
// Filter by ISO Range.
|
||||
if rangeStart, rangeEnd, rangeErr := txt.IntRange(f.Iso, 0, 10000000); rangeErr == nil {
|
||||
s = s.Where("photos.photo_iso >= ? AND photos.photo_iso <= ?", rangeStart, rangeEnd)
|
||||
}
|
||||
|
||||
// Filter by F-Number Range.
|
||||
if rangeStart, rangeEnd, rangeErr := txt.FloatRange(f.F, 0, 10000000); rangeErr == nil {
|
||||
s = s.Where("photos.photo_f_number >= ? AND photos.photo_f_number <= ?", rangeStart-0.01, rangeEnd+0.01)
|
||||
}
|
||||
|
||||
// Filter by Focal Length Range.
|
||||
if rangeStart, rangeEnd, rangeErr := txt.IntRange(f.Mm, 0, 10000000); rangeErr == nil {
|
||||
s = s.Where("photos.photo_focal_length >= ? AND photos.photo_focal_length <= ?", rangeStart, rangeEnd)
|
||||
}
|
||||
|
||||
// Filter by year.
|
||||
if f.Year != "" {
|
||||
s = s.Where(AnyInt("photos.photo_year", f.Year, txt.Or, entity.UnknownYear, txt.YearMax))
|
||||
|
|
96
pkg/txt/float.go
Normal file
96
pkg/txt/float.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package txt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsFloat checks if the string represents a floating point number.
|
||||
func IsFloat(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
for _, r := range s {
|
||||
if r != '.' && r != ',' && (r < '0' || r > '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Float converts a string to a 64-bit floating point number or 0 if invalid.
|
||||
func Float(s string) float64 {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(Numeric(s), 64)
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Float32 converts a string to a 32-bit floating point number or 0 if invalid.
|
||||
func Float32(s string) float32 {
|
||||
return float32(Float(s))
|
||||
}
|
||||
|
||||
// FloatRange parses a string as floating point number range and returns an error if it's not a valid range.
|
||||
func FloatRange(s string, min, max float64) (start float64, end float64, err error) {
|
||||
if s == "" || len(s) > 40 {
|
||||
return start, end, errors.New("invalid range")
|
||||
}
|
||||
|
||||
valid := false
|
||||
|
||||
p := 0
|
||||
v := [][]byte{make([]byte, 0, 20), make([]byte, 0, 20)}
|
||||
|
||||
for i, r := range s {
|
||||
if r == 45 {
|
||||
if i == 0 || p == 1 {
|
||||
v[p] = append(v[p], byte(r))
|
||||
} else if p == 0 {
|
||||
p = 1
|
||||
}
|
||||
}
|
||||
if r == 46 || r >= 48 && r <= 57 {
|
||||
valid = true
|
||||
v[p] = append(v[p], byte(r))
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return start, end, errors.New("invalid range")
|
||||
}
|
||||
|
||||
if p == 0 {
|
||||
start = Float(string(v[0]))
|
||||
end = start
|
||||
} else {
|
||||
start = Float(string(v[0]))
|
||||
end = Float(string(v[1]))
|
||||
}
|
||||
|
||||
if start > max {
|
||||
start = max
|
||||
} else if start < min {
|
||||
start = min
|
||||
}
|
||||
|
||||
if end > max {
|
||||
end = max
|
||||
} else if end < min {
|
||||
end = min
|
||||
}
|
||||
|
||||
return start, end, nil
|
||||
}
|
200
pkg/txt/float_test.go
Normal file
200
pkg/txt/float_test.go
Normal file
|
@ -0,0 +1,200 @@
|
|||
package txt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsFloat(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
assert.False(t, IsFloat(""))
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("0"))
|
||||
})
|
||||
|
||||
t.Run("0.5", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("0.5"))
|
||||
})
|
||||
|
||||
t.Run("0,5", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("0,5"))
|
||||
})
|
||||
|
||||
t.Run("123000.45245", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("123000.45245 "))
|
||||
})
|
||||
|
||||
t.Run("123000.", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("123000. "))
|
||||
})
|
||||
|
||||
t.Run("01:00", func(t *testing.T) {
|
||||
assert.False(t, IsFloat("01:00"))
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
assert.True(t, IsFloat(" 000123"))
|
||||
})
|
||||
|
||||
t.Run("Comma", func(t *testing.T) {
|
||||
assert.True(t, IsFloat(" 123,556\t "))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Float("")
|
||||
assert.Equal(t, 0.0, result)
|
||||
})
|
||||
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := Float(" Screenshot ")
|
||||
assert.Equal(t, 0.0, result)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := Float("0")
|
||||
assert.Equal(t, 0.0, result)
|
||||
})
|
||||
|
||||
t.Run("0.5", func(t *testing.T) {
|
||||
result := Float("0.5")
|
||||
assert.Equal(t, 0.5, result)
|
||||
})
|
||||
|
||||
t.Run("01:00", func(t *testing.T) {
|
||||
result := Float("01:00")
|
||||
assert.Equal(t, 100.0, result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Float(" 000123")
|
||||
assert.Equal(t, 123.0, result)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
result := Float(" 123,556\t ")
|
||||
assert.Equal(t, 123.556, result)
|
||||
})
|
||||
|
||||
t.Run("PositiveFloat", func(t *testing.T) {
|
||||
result := Float("123,000.45245 ")
|
||||
assert.Equal(t, 123000.45245, result)
|
||||
})
|
||||
|
||||
t.Run("NegativeFloat", func(t *testing.T) {
|
||||
result := Float(" - 123,000.45245 ")
|
||||
assert.Equal(t, -123000.45245, result)
|
||||
})
|
||||
|
||||
t.Run("MultipleDots", func(t *testing.T) {
|
||||
result := Float("123.000.45245.44 m")
|
||||
assert.Equal(t, 1230004524544.0, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat32(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Float32("")
|
||||
assert.Equal(t, float32(0), result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Float32(" 000123")
|
||||
assert.Equal(t, float32(123), result)
|
||||
})
|
||||
|
||||
t.Run("LongFloat", func(t *testing.T) {
|
||||
result := Float32("123.87945632786543786547")
|
||||
assert.Equal(t, float32(123.87945632786543786547), result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloatRange(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
start, end, err := FloatRange("", 1, 31)
|
||||
assert.Equal(t, 0.0, start)
|
||||
assert.Equal(t, 0.0, end)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
start, end, err := FloatRange("Screenshot", 1, 31)
|
||||
assert.Equal(t, 0.0, start)
|
||||
assert.Equal(t, 0.0, end)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Day", func(t *testing.T) {
|
||||
start, end, err := FloatRange("5.11-24.64", 1, 31)
|
||||
assert.Equal(t, 5.11, start)
|
||||
assert.Equal(t, 24.64, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
start, end, err := FloatRange("0", 5, 10)
|
||||
assert.Equal(t, 5.0, start)
|
||||
assert.Equal(t, 5.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
start, end, err := FloatRange("000123", 1, 1000)
|
||||
assert.Equal(t, 123.0, start)
|
||||
assert.Equal(t, 123.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
start, end, err := FloatRange(" 123\t ", 1, 1000)
|
||||
assert.Equal(t, 123.0, start)
|
||||
assert.Equal(t, 123.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("PositiveInt", func(t *testing.T) {
|
||||
start, end, err := FloatRange("123", 1, 1000)
|
||||
assert.Equal(t, 123.0, start)
|
||||
assert.Equal(t, 123.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("NegativeInt", func(t *testing.T) {
|
||||
start, end, err := FloatRange("-123", -1000, 1000)
|
||||
assert.Equal(t, -123.0, start)
|
||||
assert.Equal(t, -123.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("ZeroOne", func(t *testing.T) {
|
||||
start, end, err := FloatRange("0-1", -10, 10)
|
||||
assert.Equal(t, 0.0, start)
|
||||
assert.Equal(t, 1.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("NegativeRange", func(t *testing.T) {
|
||||
start, end, err := FloatRange("-99.9--50.005", -100, 1000)
|
||||
assert.Equal(t, -99.9, start)
|
||||
assert.Equal(t, -50.005, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("PositiveRange", func(t *testing.T) {
|
||||
start, end, err := FloatRange("100 - 201", 1, 1000)
|
||||
assert.Equal(t, 100.0, start)
|
||||
assert.Equal(t, 201.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("NegativeToPositive", func(t *testing.T) {
|
||||
start, end, err := FloatRange("-99999-123456563", -1000000, 1000000)
|
||||
assert.Equal(t, -99999.0, start)
|
||||
assert.Equal(t, 1000000.0, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package txt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -45,6 +46,58 @@ func IntVal(s string, min, max, def int) (i int) {
|
|||
return i
|
||||
}
|
||||
|
||||
// IntRange parses a string as integer range and returns an error if it's not a valid range.
|
||||
func IntRange(s string, min, max int) (start int, end int, err error) {
|
||||
if s == "" || len(s) > 40 {
|
||||
return start, end, errors.New("invalid range")
|
||||
}
|
||||
|
||||
valid := false
|
||||
|
||||
p := 0
|
||||
v := [][]byte{make([]byte, 0, 20), make([]byte, 0, 20)}
|
||||
|
||||
for i, r := range s {
|
||||
if r == 45 {
|
||||
if i == 0 || p == 1 {
|
||||
v[p] = append(v[p], byte(r))
|
||||
} else if p == 0 {
|
||||
p = 1
|
||||
}
|
||||
}
|
||||
if r == 46 || r >= 48 && r <= 57 {
|
||||
valid = true
|
||||
v[p] = append(v[p], byte(r))
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return start, end, errors.New("invalid range")
|
||||
}
|
||||
|
||||
if p == 0 {
|
||||
start = Int(string(v[0]))
|
||||
end = start
|
||||
} else {
|
||||
start = Int(string(v[0]))
|
||||
end = Int(string(v[1]))
|
||||
}
|
||||
|
||||
if start > max {
|
||||
start = max
|
||||
} else if start < min {
|
||||
start = min
|
||||
}
|
||||
|
||||
if end > max {
|
||||
end = max
|
||||
} else if end < min {
|
||||
end = min
|
||||
}
|
||||
|
||||
return start, end, nil
|
||||
}
|
||||
|
||||
// UInt converts a string to an unsigned integer or 0 if invalid.
|
||||
func UInt(s string) uint {
|
||||
if s == "" {
|
||||
|
@ -62,6 +115,23 @@ func UInt(s string) uint {
|
|||
return uint(result)
|
||||
}
|
||||
|
||||
// Int64 converts a string to a signed 64-bit integer or 0 if invalid.
|
||||
func Int64(s string) int64 {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
i := strings.SplitN(Numeric(s), ".", 2)
|
||||
|
||||
result, err := strconv.ParseInt(i[0], 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// IsUInt tests if a string represents an unsigned integer.
|
||||
func IsUInt(s string) bool {
|
||||
if s == "" {
|
||||
|
|
|
@ -80,23 +80,90 @@ func TestIntVal(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestIsUInt(t *testing.T) {
|
||||
assert.False(t, IsUInt(""))
|
||||
assert.False(t, IsUInt("12 3"))
|
||||
assert.True(t, IsUInt("123"))
|
||||
}
|
||||
func TestIntRange(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
start, end, err := IntRange("", 1, 31)
|
||||
assert.Equal(t, 0, start)
|
||||
assert.Equal(t, 0, end)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
func TestIsPosInt(t *testing.T) {
|
||||
assert.False(t, IsPosInt(""))
|
||||
assert.False(t, IsPosInt("12 3"))
|
||||
assert.True(t, IsPosInt("123"))
|
||||
assert.False(t, IsPosInt(" "))
|
||||
assert.False(t, IsPosInt("-1"))
|
||||
assert.False(t, IsPosInt("0"))
|
||||
assert.False(t, IsPosInt("0.1"))
|
||||
assert.False(t, IsPosInt("0,1"))
|
||||
assert.True(t, IsPosInt("1"))
|
||||
assert.True(t, IsPosInt("99943546356"))
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
start, end, err := IntRange("Screenshot", 1, 31)
|
||||
assert.Equal(t, 0, start)
|
||||
assert.Equal(t, 0, end)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Day", func(t *testing.T) {
|
||||
start, end, err := IntRange("5-24", 1, 31)
|
||||
assert.Equal(t, 5, start)
|
||||
assert.Equal(t, 24, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
start, end, err := IntRange("0", 5, 10)
|
||||
assert.Equal(t, 5, start)
|
||||
assert.Equal(t, 5, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
start, end, err := IntRange("000123", 1, 1000)
|
||||
assert.Equal(t, 123, start)
|
||||
assert.Equal(t, 123, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
start, end, err := IntRange(" 123\t ", 1, 1000)
|
||||
assert.Equal(t, 123, start)
|
||||
assert.Equal(t, 123, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("PositiveInt", func(t *testing.T) {
|
||||
start, end, err := IntRange("123", 1, 1000)
|
||||
assert.Equal(t, 123, start)
|
||||
assert.Equal(t, 123, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("NegativeInt", func(t *testing.T) {
|
||||
start, end, err := IntRange("-123", -1000, 1000)
|
||||
assert.Equal(t, -123, start)
|
||||
assert.Equal(t, -123, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("ZeroOne", func(t *testing.T) {
|
||||
start, end, err := IntRange("0-1", -10, 10)
|
||||
assert.Equal(t, 0, start)
|
||||
assert.Equal(t, 1, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("NegativeRange", func(t *testing.T) {
|
||||
start, end, err := IntRange("-100--50", -100, 1000)
|
||||
assert.Equal(t, -100, start)
|
||||
assert.Equal(t, -50, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("PositiveRange", func(t *testing.T) {
|
||||
start, end, err := IntRange("100 - 201", 1, 1000)
|
||||
assert.Equal(t, 100, start)
|
||||
assert.Equal(t, 201, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("NegativeToPositive", func(t *testing.T) {
|
||||
start, end, err := IntRange("-99999-123456563", -1000000, 1000000)
|
||||
assert.Equal(t, -99999, start)
|
||||
assert.Equal(t, 1000000, end)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUInt(t *testing.T) {
|
||||
|
@ -130,3 +197,64 @@ func TestUInt(t *testing.T) {
|
|||
assert.Equal(t, uint(0), result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Int64("")
|
||||
assert.Equal(t, int64(0), result)
|
||||
})
|
||||
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := Int64(" Screenshot ")
|
||||
assert.Equal(t, int64(0), result)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := Int64("0")
|
||||
assert.Equal(t, int64(0), result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Int64(" 000123")
|
||||
assert.Equal(t, int64(123), result)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
result := Int64(" 123,556\t ")
|
||||
assert.Equal(t, int64(123), result)
|
||||
})
|
||||
|
||||
t.Run("PositiveFloat", func(t *testing.T) {
|
||||
result := Int64("123,000.45245 ")
|
||||
assert.Equal(t, int64(123000), result)
|
||||
})
|
||||
|
||||
t.Run("NegativeFloat", func(t *testing.T) {
|
||||
result := Int64(" - 123,000.45245 ")
|
||||
assert.Equal(t, int64(-123000), result)
|
||||
})
|
||||
|
||||
t.Run("MultipleDots", func(t *testing.T) {
|
||||
result := Int64("123.000.45245.44 m")
|
||||
assert.Equal(t, int64(1230004524544), result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsUInt(t *testing.T) {
|
||||
assert.False(t, IsUInt(""))
|
||||
assert.False(t, IsUInt("12 3"))
|
||||
assert.True(t, IsUInt("123"))
|
||||
}
|
||||
|
||||
func TestIsPosInt(t *testing.T) {
|
||||
assert.False(t, IsPosInt(""))
|
||||
assert.False(t, IsPosInt("12 3"))
|
||||
assert.True(t, IsPosInt("123"))
|
||||
assert.False(t, IsPosInt(" "))
|
||||
assert.False(t, IsPosInt("-1"))
|
||||
assert.False(t, IsPosInt("0"))
|
||||
assert.False(t, IsPosInt("0.1"))
|
||||
assert.False(t, IsPosInt("0,1"))
|
||||
assert.True(t, IsPosInt("1"))
|
||||
assert.True(t, IsPosInt("99943546356"))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package txt
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -32,57 +31,3 @@ func Numeric(s string) string {
|
|||
|
||||
return s
|
||||
}
|
||||
|
||||
// IsFloat checks if the string represents a floating point number.
|
||||
func IsFloat(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
for _, r := range s {
|
||||
if r != '.' && r != ',' && (r < '0' || r > '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Float converts a string to a 64-bit floating point number or 0 if invalid.
|
||||
func Float(s string) float64 {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(Numeric(s), 64)
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Float32 converts a string to a 32-bit floating point number or 0 if invalid.
|
||||
func Float32(s string) float32 {
|
||||
return float32(Float(s))
|
||||
}
|
||||
|
||||
// Int64 converts a string to a signed 64-bit integer or 0 if invalid.
|
||||
func Int64(s string) int64 {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
i := strings.SplitN(Numeric(s), ".", 2)
|
||||
|
||||
result, err := strconv.ParseInt(i[0], 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -57,152 +57,3 @@ func TestNumeric(t *testing.T) {
|
|||
assert.Equal(t, "1230004524544", result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsFloat(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
assert.False(t, IsFloat(""))
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("0"))
|
||||
})
|
||||
|
||||
t.Run("0.5", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("0.5"))
|
||||
})
|
||||
|
||||
t.Run("0,5", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("0,5"))
|
||||
})
|
||||
|
||||
t.Run("123000.45245", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("123000.45245 "))
|
||||
})
|
||||
|
||||
t.Run("123000.", func(t *testing.T) {
|
||||
assert.True(t, IsFloat("123000. "))
|
||||
})
|
||||
|
||||
t.Run("01:00", func(t *testing.T) {
|
||||
assert.False(t, IsFloat("01:00"))
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
assert.True(t, IsFloat(" 000123"))
|
||||
})
|
||||
|
||||
t.Run("Comma", func(t *testing.T) {
|
||||
assert.True(t, IsFloat(" 123,556\t "))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Float("")
|
||||
assert.Equal(t, 0.0, result)
|
||||
})
|
||||
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := Float(" Screenshot ")
|
||||
assert.Equal(t, 0.0, result)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := Float("0")
|
||||
assert.Equal(t, 0.0, result)
|
||||
})
|
||||
|
||||
t.Run("0.5", func(t *testing.T) {
|
||||
result := Float("0.5")
|
||||
assert.Equal(t, 0.5, result)
|
||||
})
|
||||
|
||||
t.Run("01:00", func(t *testing.T) {
|
||||
result := Float("01:00")
|
||||
assert.Equal(t, 100.0, result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Float(" 000123")
|
||||
assert.Equal(t, 123.0, result)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
result := Float(" 123,556\t ")
|
||||
assert.Equal(t, 123.556, result)
|
||||
})
|
||||
|
||||
t.Run("PositiveFloat", func(t *testing.T) {
|
||||
result := Float("123,000.45245 ")
|
||||
assert.Equal(t, 123000.45245, result)
|
||||
})
|
||||
|
||||
t.Run("NegativeFloat", func(t *testing.T) {
|
||||
result := Float(" - 123,000.45245 ")
|
||||
assert.Equal(t, -123000.45245, result)
|
||||
})
|
||||
|
||||
t.Run("MultipleDots", func(t *testing.T) {
|
||||
result := Float("123.000.45245.44 m")
|
||||
assert.Equal(t, 1230004524544.0, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat32(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Float32("")
|
||||
assert.Equal(t, float32(0), result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Float32(" 000123")
|
||||
assert.Equal(t, float32(123), result)
|
||||
})
|
||||
|
||||
t.Run("LongFloat", func(t *testing.T) {
|
||||
result := Float32("123.87945632786543786547")
|
||||
assert.Equal(t, float32(123.87945632786543786547), result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Int64("")
|
||||
assert.Equal(t, int64(0), result)
|
||||
})
|
||||
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := Int64(" Screenshot ")
|
||||
assert.Equal(t, int64(0), result)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := Int64("0")
|
||||
assert.Equal(t, int64(0), result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Int64(" 000123")
|
||||
assert.Equal(t, int64(123), result)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
result := Int64(" 123,556\t ")
|
||||
assert.Equal(t, int64(123), result)
|
||||
})
|
||||
|
||||
t.Run("PositiveFloat", func(t *testing.T) {
|
||||
result := Int64("123,000.45245 ")
|
||||
assert.Equal(t, int64(123000), result)
|
||||
})
|
||||
|
||||
t.Run("NegativeFloat", func(t *testing.T) {
|
||||
result := Int64(" - 123,000.45245 ")
|
||||
assert.Equal(t, int64(-123000), result)
|
||||
})
|
||||
|
||||
t.Run("MultipleDots", func(t *testing.T) {
|
||||
result := Int64("123.000.45245.44 m")
|
||||
assert.Equal(t, int64(1230004524544), result)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue