Full application rewrite

This commit is contained in:
2025-01-04 01:49:48 +03:00
parent 70f32b799c
commit 754aecd69a
32 changed files with 1244 additions and 489 deletions

View File

@@ -0,0 +1,53 @@
package metadata
import (
"errors"
"fmt"
"io"
"path/filepath"
"strings"
"time"
)
const timeFormat = "20060102_150405"
var (
ErrBadPrefix = errors.New("bad prefix")
ErrBadNameFormat = errors.New("bad name format")
)
type Default struct {
TimeFormat string
Prefix string
}
func (d *Default) Extract(fp string, _ io.Reader) (Metadata, error) {
format := d.TimeFormat
if format == "" {
format = timeFormat
}
if d.Prefix != "" && !strings.HasPrefix(fp, d.Prefix) {
return Metadata{}, fmt.Errorf("%w: expect a prefix %s, got %s", ErrBadPrefix, d.Prefix, fp)
}
fp = filepath.Base(fp)
leftLimit := len(d.Prefix)
rightLimit := leftLimit + len(format)
if len(fp) < rightLimit {
return Metadata{}, fmt.Errorf("%w: too short", ErrBadNameFormat)
}
created, err := time.Parse(format, fp[leftLimit:rightLimit])
if err != nil {
return Metadata{}, fmt.Errorf("parse time: %w", err)
}
meta := Metadata{
Created: created,
}
return meta, nil
}

View File

@@ -0,0 +1,123 @@
package metadata_test
import (
"errors"
"reflect"
"testing"
"time"
. "github.com/derfenix/photocatalog/internal/metadata"
)
func TestDefault_Extract(t *testing.T) {
t.Parallel()
const timeFormat = "20060102_150405"
tests := []struct {
name string
filePath string
prefix string
timeFormat string
want Metadata
wantErr bool
wantErrType error
}{
{
name: "simple",
filePath: "20241108_160834.jpg",
timeFormat: timeFormat,
want: Metadata{
Created: time.Date(2024, 11, 8, 16, 8, 34, 0, time.UTC),
},
wantErr: false,
},
{
name: "simple in subdirectory",
filePath: "foo/20241108_160834.jpg",
timeFormat: timeFormat,
want: Metadata{
Created: time.Date(2024, 11, 8, 16, 8, 34, 0, time.UTC),
},
wantErr: false,
},
{
name: "simple with suffix",
filePath: "20241108_160834_(1).jpg",
timeFormat: timeFormat,
want: Metadata{
Created: time.Date(2024, 11, 8, 16, 8, 34, 0, time.UTC),
},
wantErr: false,
},
{
name: "prefix",
filePath: "foo_20241108_160834.jpg",
timeFormat: timeFormat,
prefix: "foo_",
want: Metadata{
Created: time.Date(2024, 11, 8, 16, 8, 34, 0, time.UTC),
},
wantErr: false,
},
{
name: "bad prefix",
filePath: "foo_20241108_160834.jpg",
timeFormat: timeFormat,
prefix: "bar_",
want: Metadata{
Created: time.Time{},
},
wantErr: true,
wantErrType: ErrBadPrefix,
},
{
name: "no prefix",
filePath: "20241108_160834.jpg",
timeFormat: timeFormat,
prefix: "bar_",
want: Metadata{
Created: time.Time{},
},
wantErr: true,
wantErrType: ErrBadPrefix,
},
{
name: "unexpected prefix",
filePath: "foo_20241108_160834.jpg",
timeFormat: timeFormat,
prefix: "",
want: Metadata{
Created: time.Time{},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
d := &Default{
TimeFormat: tt.timeFormat,
Prefix: tt.prefix,
}
got, err := d.Extract(tt.filePath, nil)
if (err != nil) != tt.wantErr {
t.Errorf("Extract() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr && tt.wantErrType != nil {
if !errors.Is(err, tt.wantErrType) {
t.Errorf("Extract() errorType = %v, wantErrType %v", err, tt.wantErrType)
}
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Extract() got = %v, want %v", got, tt.want)
}
})
}
}

28
internal/metadata/exif.go Normal file
View File

@@ -0,0 +1,28 @@
package metadata
import (
"fmt"
"io"
"github.com/rwcarlsen/goexif/exif"
)
type Exif struct{}
func (j Exif) Extract(_ string, data io.Reader) (Metadata, error) {
decode, err := exif.Decode(data)
if err != nil {
return Metadata{}, fmt.Errorf("decode exif: %w", err)
}
meta := Metadata{}
created, err := decode.DateTime()
if err != nil {
return Metadata{}, fmt.Errorf("parse datetime: %w", err)
}
meta.Created = created
return meta, nil
}

View File

@@ -0,0 +1,9 @@
package metadata
import (
"time"
)
type Metadata struct {
Created time.Time
}