Go: Apply go fix modernizations across backend packages

Run `go fix ./...` and keep mechanical modernization updates.

- Replace `interface{}` with `any` in signatures and local types
- Apply formatter/style cleanups from go1.26 tooling
- Keep `omitempty` behavior-preserving simplifications suggested by fix
- No functional feature changes intended

Validation:
- go test ./... -run '^$' -count=1 (Go 1.26.0)
- GOTOOLCHAIN=go1.24.10 go test ./... -run '^$' -count=1

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2026-02-20 03:54:33 +01:00
parent 4653e14b6d
commit a2b7615c93
178 changed files with 558 additions and 687 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ const appDescription = "PhotoPrism® is an AI-Powered Photos App for the Decentr
const appCopyright = "(c) 2018-2025 PhotoPrism UG. All rights reserved."
// Metadata contains build specific information.
var Metadata = map[string]interface{}{
var Metadata = map[string]any{
"Name": appName,
"About": appAbout,
"Edition": appEdition,
+3 -3
View File
@@ -157,12 +157,12 @@ func EmbeddingsMidpoint(embeddings Embeddings) (result Embedding, radius float64
normalizeEmbedding(emb)
for j := 0; j < dim; j++ {
for j := range dim {
result[j] += emb[j]
}
}
for i := 0; i < dim; i++ {
for i := range dim {
result[i] *= invCount
}
@@ -172,7 +172,7 @@ func EmbeddingsMidpoint(embeddings Embeddings) (result Embedding, radius float64
for _, emb := range embeddings {
var dist float64
for i := 0; i < dim; i++ {
for i := range dim {
diff := result[i] - emb[i]
dist += diff * diff
}
+5 -8
View File
@@ -174,10 +174,7 @@ func NewONNXEngine(opts ONNXOptions) (DetectionEngine, error) {
threads := opts.Threads
if threads == 0 {
threads = runtime.NumCPU() / 2
if threads < 1 {
threads = 1
}
threads = max(runtime.NumCPU()/2, 1)
}
if err := sessionOpts.SetIntraOpNumThreads(threads); err != nil {
@@ -560,11 +557,11 @@ func (o *onnxEngine) anchorCenters(height, width, stride, anchors int) []float32
centers := make([]float32, height*width*anchors*2)
idx := 0
for y := 0; y < height; y++ {
for y := range height {
cy := float32(y * stride)
for x := 0; x < width; x++ {
for x := range width {
cx := float32(x * stride)
for a := 0; a < anchors; a++ {
for range anchors {
centers[idx] = cx
centers[idx+1] = cy
idx += 2
@@ -599,7 +596,7 @@ func nonMaxSuppression(boxes []onnxDetection, threshold float32) []onnxDetection
picked := make([]onnxDetection, 0, len(boxes))
suppressed := make([]bool, len(boxes))
for i := 0; i < len(boxes); i++ {
for i := range boxes {
if suppressed[i] {
continue
}
+1 -1
View File
@@ -38,7 +38,7 @@ type Face struct {
Rows int `json:"rows,omitempty"`
Cols int `json:"cols,omitempty"`
Score int `json:"score,omitempty"`
Area Area `json:"face,omitempty"`
Area Area `json:"face"`
Eyes Areas `json:"eyes,omitempty"`
Landmarks Areas `json:"landmarks,omitempty"`
Embeddings Embeddings `json:"embeddings,omitempty"`
+3 -3
View File
@@ -187,12 +187,12 @@ func imageToTensor(img image.Image, resolution int) (tfTensor *tf.Tensor, err er
var tfImage [1][][][3]float32
for j := 0; j < resolution; j++ {
for range resolution {
tfImage[0] = append(tfImage[0], make([][3]float32, resolution))
}
for i := 0; i < resolution; i++ {
for j := 0; j < resolution; j++ {
for i := range resolution {
for j := range resolution {
r, g, b, _ := img.At(i, j).RGBA()
tfImage[0][j][i][0] = convertValue(r)
tfImage[0][j][i][1] = convertValue(g)
+2 -2
View File
@@ -112,8 +112,8 @@ func TestNet(t *testing.T) {
// Dist Matrix
correct := 0
for i := 0; i < len(embeddings); i++ {
for j := 0; j < len(embeddings); j++ {
for i := range embeddings {
for j := range embeddings {
if i >= j {
continue
}
+2 -2
View File
@@ -121,7 +121,7 @@ func (o ResizeOperation) MarshalYAML() (any, error) {
}
// UnmarshalYAML decodes the resize operation from YAML input.
func (o *ResizeOperation) UnmarshalYAML(unmarshal func(interface{}) error) error {
func (o *ResizeOperation) UnmarshalYAML(unmarshal func(any) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
@@ -269,7 +269,7 @@ func (o ColorChannelOrder) MarshalYAML() (any, error) {
}
// UnmarshalYAML decodes the channel order from YAML input.
func (o *ColorChannelOrder) UnmarshalYAML(unmarshal func(interface{}) error) error {
func (o *ColorChannelOrder) UnmarshalYAML(unmarshal func(any) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
+1 -1
View File
@@ -19,7 +19,7 @@ type ApiResponse struct {
Code int `yaml:"Code,omitempty" json:"code,omitempty"`
Error string `yaml:"Error,omitempty" json:"error,omitempty"`
Model *Model `yaml:"Model,omitempty" json:"model,omitempty"`
Result ApiResult `yaml:"Result,omitempty" json:"result,omitempty"`
Result ApiResult `yaml:"Result,omitempty" json:"result"`
}
// Err returns an error if the request has failed.
+1 -1
View File
@@ -47,7 +47,7 @@ type Model struct {
Resolution int `yaml:"Resolution,omitempty" json:"resolution,omitempty"`
TensorFlow *tensorflow.ModelInfo `yaml:"TensorFlow,omitempty" json:"tensorflow,omitempty"`
Options *ModelOptions `yaml:"Options,omitempty" json:"options,omitempty"`
Service Service `yaml:"Service,omitempty" json:"service,omitempty"`
Service Service `yaml:"Service,omitempty" json:"service"`
Path string `yaml:"Path,omitempty" json:"-"`
Disabled bool `yaml:"Disabled,omitempty" json:"disabled,omitempty"`
classifyModel *classify.Model
-1
View File
@@ -40,7 +40,6 @@ func TestFilterModels(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
got := FilterModels(tc.models, tc.when, tc.allow)
if len(got) != len(tc.expected) {
-2
View File
@@ -21,7 +21,6 @@ func TestParseRunType(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
if got := ParseRunType(tc.in); got != tc.out {
@@ -65,7 +64,6 @@ func TestModel_RunType(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.RunType(); got != tc.want {
-1
View File
@@ -434,7 +434,6 @@ func TestModel_IsDefault(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.IsDefault(); got != tc.want {
+1 -1
View File
@@ -33,7 +33,7 @@ func ParseModelTypes(s string) (types ModelTypes) {
s = strings.ToLower(s)
types = make(ModelTypes, 0, strings.Count(s, ","))
for _, t := range strings.Split(s, ",") {
for t := range strings.SplitSeq(s, ",") {
t = strings.TrimSpace(t)
switch t {
case ModelTypeLabels, ModelTypeNsfw, ModelTypeFace, ModelTypeCaption, ModelTypeGenerate:
+2 -2
View File
@@ -12,7 +12,7 @@ type Response struct {
Code int `yaml:"Code,omitempty" json:"code,omitempty"`
Error string `yaml:"Error,omitempty" json:"error,omitempty"`
Model string `yaml:"Model,omitempty" json:"model,omitempty"`
CreatedAt time.Time `yaml:"CreatedAt,omitempty" json:"created_at,omitempty"`
CreatedAt time.Time `yaml:"CreatedAt,omitempty" json:"created_at"`
Response string `yaml:"Response,omitempty" json:"response,omitempty"`
Thinking string `yaml:"Thinking,omitempty" json:"thinking,omitempty"`
Done bool `yaml:"Done,omitempty" json:"done,omitempty"`
@@ -23,7 +23,7 @@ type Response struct {
PromptEvalDuration int `yaml:"PromptEvalDuration,omitempty" json:"prompt_eval_duration,omitempty"`
EvalCount int `yaml:"EvalCount,omitempty" json:"eval_count,omitempty"`
EvalDuration int64 `yaml:"EvalDuration,omitempty" json:"eval_duration,omitempty"`
Result ResultPayload `yaml:"Result,omitempty" json:"result,omitempty"`
Result ResultPayload `yaml:"Result,omitempty" json:"result"`
}
// Err returns an error if the request has failed.
+2 -2
View File
@@ -19,7 +19,7 @@ import (
var brokenVideo []byte
// Abort writes a localized error response and stops further handler processing.
func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
func Abort(c *gin.Context, code int, id i18n.Message, params ...any) {
resp := i18n.NewResponse(code, id, params...)
if code >= 400 {
@@ -32,7 +32,7 @@ func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
}
// Error aborts the request while attaching error details to the response payload.
func Error(c *gin.Context, code int, err error, id i18n.Message, params ...interface{}) {
func Error(c *gin.Context, code int, err error, id i18n.Message, params ...any) {
resp := i18n.NewResponse(code, id, params...)
if err != nil {
+1 -5
View File
@@ -95,11 +95,7 @@ func ClusterListNodes(router *gin.RouterGroup) {
offset = len(items)
}
end := offset + count
if end > len(items) {
end = len(items)
}
end := min(offset+count, len(items))
page := items[offset:end]
+1 -1
View File
@@ -32,7 +32,7 @@ func TestOAuthToken_RateLimit_ClientCredentials(t *testing.T) {
// Invalid client secret repeatedly (from UnknownIP: no headers set)
path := "/api/v1/oauth/token"
for i := 0; i < 3; i++ {
for range 3 {
data := url.Values{
"grant_type": {authn.GrantClientCredentials.String()},
"client_id": {"cs5cpu17n6gj2qo5"},
+1 -1
View File
@@ -24,7 +24,7 @@ func TestCreateSession_RateLimitExceeded(t *testing.T) {
limiter.Login = limiter.NewLimit(rate.Every(24*time.Hour), 3)
limiter.Auth = limiter.NewLimit(rate.Every(24*time.Hour), 3)
for i := 0; i < 3; i++ {
for range 3 {
r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/session", `{"username": "admin", "password": "wrong"}`)
assert.Equal(t, http.StatusUnauthorized, r.Code)
}
+3 -3
View File
@@ -40,7 +40,7 @@ func buildMultipart(files map[string][]byte) (body *bytes.Buffer, contentType st
func buildMultipartTwo(name1 string, data1 []byte, name2 string, data2 []byte) (body *bytes.Buffer, contentType string, err error) {
body = &bytes.Buffer{}
mw := multipart.NewWriter(body)
for _, it := range [][2]interface{}{{name1, data1}, {name2, data2}} {
for _, it := range [][2]any{{name1, data1}, {name2, data2}} {
fw, cerr := mw.CreateFormFile("files", it[0].(string))
if cerr != nil {
return nil, "", cerr
@@ -342,7 +342,7 @@ func TestUploadUserFiles_Multipart_ZipPartialExtraction(t *testing.T) {
var zbuf bytes.Buffer
zw := zip.NewWriter(&zbuf)
for i := 0; i < 20; i++ { // ~20 * 63 KiB ≈ 1.2 MiB
for i := range 20 { // ~20 * 63 KiB ≈ 1.2 MiB
f, _ := zw.Create(fmt.Sprintf("pic%02d.jpg", i+1))
_, _ = f.Write(data)
}
@@ -390,7 +390,7 @@ func TestUploadUserFiles_Multipart_ZipDeepNestingStress(t *testing.T) {
// Build a deeply nested path (20 levels)
deep := ""
for i := 0; i < 20; i++ {
for i := range 20 {
if i == 0 {
deep = "deep"
} else {
+1 -1
View File
@@ -51,7 +51,7 @@ func AppendWebsocketTopics(topics ...string) {
}
// wsSendMessage sends a message to the WebSocket client.
func wsSendMessage(topic string, data interface{}, ws *websocket.Conn, writeMutex *sync.Mutex) {
func wsSendMessage(topic string, data any, ws *websocket.Conn, writeMutex *sync.Mutex) {
if topic == "" || ws == nil || writeMutex == nil {
return
}
+2 -7
View File
@@ -1,6 +1,7 @@
package acl
import (
"slices"
"sort"
"testing"
@@ -172,13 +173,7 @@ func TestResource_Default_String_And_Compare(t *testing.T) {
func TestResourceNames_ContainsCore(t *testing.T) {
want := []Resource{ResourceDefault, ResourcePhotos, ResourceAlbums, ResourceWebDAV, ResourceApi}
for _, w := range want {
found := false
for _, have := range ResourceNames {
if have == w {
found = true
break
}
}
found := slices.Contains(ResourceNames, w)
assert.Truef(t, found, "resource %s not found in ResourceNames", w)
}
}
+6 -10
View File
@@ -31,7 +31,7 @@ type VerifierStatus struct {
CacheETag string `json:"cacheEtag,omitempty"`
KeyIDs []string `json:"keyIds,omitempty"`
KeyCount int `json:"keyCount"`
CacheFetchedAt time.Time `json:"cacheFetchedAt,omitempty"`
CacheFetchedAt time.Time `json:"cacheFetchedAt"`
CacheAgeSeconds int64 `json:"cacheAgeSeconds"`
CacheTTLSeconds int `json:"cacheTtlSeconds"`
CacheStale bool `json:"cacheStale"`
@@ -137,7 +137,7 @@ func (v *Verifier) VerifyToken(ctx context.Context, tokenString string, expected
)
claims := &Claims{}
keyFunc := func(token *gojwt.Token) (interface{}, error) {
keyFunc := func(token *gojwt.Token) (any, error) {
kid, _ := token.Header["kid"].(string)
if kid == "" {
@@ -171,7 +171,7 @@ func (v *Verifier) VerifyToken(ctx context.Context, tokenString string, expected
scopeSet := map[string]struct{}{}
for _, s := range strings.Fields(claims.Scope) {
for s := range strings.FieldsSeq(claims.Scope) {
scopeSet[s] = struct{}{}
}
@@ -235,7 +235,7 @@ func VerifyTokenWithKeys(tokenString string, expected ExpectedClaims, keys []Pub
parser := gojwt.NewParser(options...)
claims := &Claims{}
keyFunc := func(token *gojwt.Token) (interface{}, error) {
keyFunc := func(token *gojwt.Token) (any, error) {
kid, _ := token.Header["kid"].(string)
if kid == "" {
return nil, errors.New("jwt: missing kid header")
@@ -261,7 +261,7 @@ func VerifyTokenWithKeys(tokenString string, expected ExpectedClaims, keys []Pub
if len(expected.Scope) > 0 {
scopeSet := map[string]struct{}{}
for _, s := range strings.Fields(claims.Scope) {
for s := range strings.FieldsSeq(claims.Scope) {
scopeSet[s] = struct{}{}
}
for _, req := range expected.Scope {
@@ -543,11 +543,7 @@ func backoffDuration(attempt int) time.Duration {
attempt = 1
}
base := jwksFetchBaseDelay << (attempt - 1)
if base > jwksFetchMaxDelay {
base = jwksFetchMaxDelay
}
base := min(jwksFetchBaseDelay<<(attempt-1), jwksFetchMaxDelay)
jitterRange := base / 2
+1 -1
View File
@@ -177,7 +177,7 @@ func TestIssuerClampTTL(t *testing.T) {
parsed := &Claims{}
parser := gojwt.NewParser(gojwt.WithValidMethods([]string{gojwt.SigningMethodEdDSA.Alg()}), gojwt.WithoutClaimsValidation())
_, err = parser.ParseWithClaims(token, parsed, func(token *gojwt.Token) (interface{}, error) {
_, err = parser.ParseWithClaims(token, parsed, func(token *gojwt.Token) (any, error) {
key, _ := mgr.ActiveKey()
return key.PublicKey, nil
})
+1 -4
View File
@@ -60,10 +60,7 @@ func clusterNodesListAction(ctx *cli.Context) error {
if count <= 0 || count > 1000 {
count = 100
}
offset := ctx.Int("offset")
if offset < 0 {
offset = 0
}
offset := max(ctx.Int("offset"), 0)
if offset > len(items) {
offset = len(items)
}
+1 -1
View File
@@ -127,7 +127,7 @@ func clusterNodesModAction(ctx *cli.Context) error {
string(acl.ResourceCluster),
"update node", "%s",
}
args := []interface{}{clean.Log(nodeID)}
args := []any{clean.Log(nodeID)}
if changeSummary != "" {
segments = append(segments, "%s")
+1 -1
View File
@@ -194,7 +194,7 @@ func clusterNodesRotateAction(ctx *cli.Context) error {
string(acl.ResourceCluster),
"rotate node", "%s",
}
args := []interface{}{clean.Log(nodeID)}
args := []any{clean.Log(nodeID)}
if detail != "" {
segments = append(segments, "%s")
args = append(args, clean.Log(detail))
+1 -1
View File
@@ -332,7 +332,7 @@ func (e *httpError) Error() string { return fmt.Sprintf("http %d: %s", e.Status,
func postWithBackoff(url, token string, payload []byte, out any) error {
// backoff: 500ms -> max ~8s, 6 attempts with jitter
delay := 500 * time.Millisecond
for attempt := 0; attempt < 6; attempt++ {
for range 6 {
req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(payload))
header.SetAuthorization(req, token)
req.Header.Set(header.ContentType, "application/json")
+1 -1
View File
@@ -104,7 +104,7 @@ func NewTestContext(args []string) *cli.Context {
app.HideHelpCommand = true
app.Action = func(*cli.Context) error { return nil }
app.EnableBashCompletion = false
app.Metadata = map[string]interface{}{
app.Metadata = map[string]any{
"Name": "PhotoPrism",
"About": "PhotoPrism®",
"Edition": "ce",
+1 -4
View File
@@ -74,10 +74,7 @@ func findAction(ctx *cli.Context) error {
rows := make([][]string, 0, len(results))
for _, found := range results {
size := found.FileSize
if size < 0 {
size = 0
}
size := max(found.FileSize, 0)
v := []string{found.FileName, found.FileMime, humanize.Bytes(uint64(size)), found.FileHash} //nolint:gosec // size non-negative after check
rows = append(rows, v)
}
+1 -4
View File
@@ -38,10 +38,7 @@ func showCommandsAction(ctx *cli.Context) error {
includeHidden := ctx.Bool("all")
wantJSON := ctx.Bool("json")
nested := ctx.Bool("nested")
baseHeading := ctx.Int("base-heading")
if baseHeading < 1 {
baseHeading = 1
}
baseHeading := max(ctx.Int("base-heading"), 1)
// Collect the app metadata to be included in the output.
app := catalog.App{}
+1 -1
View File
@@ -40,7 +40,7 @@ func TestShowCommands_JSON_Flat(t *testing.T) {
Name, FullName string
Depth int
} `json:"commands"`
GlobalFlags []map[string]interface{} `json:"global_flags"`
GlobalFlags []map[string]any `json:"global_flags"`
}
if err := json.Unmarshal(b, &v); err != nil {
t.Fatalf("invalid json: %v", err)
+1 -1
View File
@@ -58,7 +58,7 @@ func showConfigAction(ctx *cli.Context) error {
rows, cols := rep.Report(conf)
sections = append(sections, section{Title: rep.Title, Items: report.RowsToObjects(rows, cols)})
}
b, _ := json.Marshal(map[string]interface{}{"sections": sections})
b, _ := json.Marshal(map[string]any{"sections": sections})
fmt.Println(string(b))
return nil
}
-7
View File
@@ -184,13 +184,6 @@ func TestShowConfigOptions_MarkdownSections(t *testing.T) {
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func TestShowFileFormats_JSON(t *testing.T) {
out, err := RunWithTestContext(ShowFileFormatsCommand, []string{"file-formats", "--json"})
+5 -5
View File
@@ -234,10 +234,10 @@ func videoListRow(found search.Photo) []string {
}
// videoListJSONRow renders a search result row for JSON output with canonical column keys.
func videoListJSONRow(found search.Photo) map[string]interface{} {
func videoListJSONRow(found search.Photo) map[string]any {
videoFile, _ := videoPrimaryFile(found)
data := map[string]interface{}{
data := map[string]any{
"video": videoFile.FileName,
"size": videoNonNegativeSize(videoFile.FileSize),
"resolution": fmt.Sprintf("%dx%d", videoFile.FileWidth, videoFile.FileHeight),
@@ -252,16 +252,16 @@ func videoListJSONRow(found search.Photo) map[string]interface{} {
}
// videoListJSON marshals a list of JSON rows using the canonical keys for each column.
func videoListJSON(rows []map[string]interface{}, cols []string) (string, error) {
func videoListJSON(rows []map[string]any, cols []string) (string, error) {
canon := make([]string, len(cols))
for i, col := range cols {
canon[i] = report.CanonKey(col)
}
payload := make([]map[string]interface{}, 0, len(rows))
payload := make([]map[string]any, 0, len(rows))
for _, row := range rows {
item := make(map[string]interface{}, len(canon))
item := make(map[string]any, len(canon))
for _, key := range canon {
item[key] = row[key]
}
+9 -9
View File
@@ -68,10 +68,10 @@ func videoInfoAction(ctx *cli.Context) error {
// videoInfoEntry describes all metadata sections for a single video.
type videoInfoEntry struct {
Index map[string]interface{} `json:"index"`
Exif interface{} `json:"exif,omitempty"`
FFprobe interface{} `json:"ffprobe,omitempty"`
Raw map[string]string `json:"raw,omitempty"`
Index map[string]any `json:"index"`
Exif any `json:"exif,omitempty"`
FFprobe any `json:"ffprobe,omitempty"`
Raw map[string]string `json:"raw,omitempty"`
}
// videoInfoEntryFor collects indexed, ExifTool, and ffprobe metadata for a search result.
@@ -123,8 +123,8 @@ func videoInfoEntryFor(conf *config.Config, found search.Photo, verbose bool) (v
}
// videoIndexSummary builds a concise map of indexed fields for diagnostics.
func videoIndexSummary(found search.Photo, file entity.File) map[string]interface{} {
return map[string]interface{}{
func videoIndexSummary(found search.Photo, file entity.File) map[string]any {
return map[string]any{
"file_name": file.FileName,
"file_root": file.FileRoot,
"file_uid": file.FileUID,
@@ -152,7 +152,7 @@ func videoIndexSummary(found search.Photo, file entity.File) map[string]interfac
}
// videoRunFFprobe executes ffprobe and returns parsed JSON plus raw output.
func videoRunFFprobe(ffprobeBin, filePath string) (interface{}, string, error) {
func videoRunFFprobe(ffprobeBin, filePath string) (any, string, error) {
cmd := exec.Command(ffprobeBin, "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", filePath) //nolint:gosec // args are validated paths
var stdout bytes.Buffer
@@ -169,7 +169,7 @@ func videoRunFFprobe(ffprobeBin, filePath string) (interface{}, string, error) {
return nil, raw, nil
}
var data interface{}
var data any
if err := json.Unmarshal([]byte(raw), &data); err != nil {
return nil, raw, nil
}
@@ -185,7 +185,7 @@ func (v *videoInfoEntry) ensureRaw() {
}
// videoPrettyJSON returns indented JSON for human-readable output.
func videoPrettyJSON(value interface{}) string {
func videoPrettyJSON(value any) string {
data, err := json.MarshalIndent(value, "", " ")
if err != nil {
return ""
+1 -1
View File
@@ -45,7 +45,7 @@ func videoListAction(ctx *cli.Context) error {
cols := videoListColumns()
if format == report.JSON {
rows := make([]map[string]interface{}, 0, len(results))
rows := make([]map[string]any, 0, len(results))
for _, found := range results {
rows = append(rows, videoListJSONRow(found))
}
+2 -10
View File
@@ -27,20 +27,12 @@ func videoSearchResults(query string, count int, offset int) ([]search.Photo, er
Order: sortby.Name,
}
target := count + offset
if target < 0 {
target = 0
}
target := max(count+offset, 0)
collected := make([]search.Photo, 0, target)
index := make(map[string]int, target)
searchOffset := 0
batchSize := count
if batchSize < 200 {
batchSize = 200
}
batchSize := max(count, 200)
needComplete := false
+1 -1
View File
@@ -11,7 +11,7 @@ import (
)
// ApplyCliContext applies the values of the cli context based on the "flag" annotations.
func ApplyCliContext(c interface{}, ctx *cli.Context) error {
func ApplyCliContext(c any, ctx *cli.Context) error {
v := reflect.ValueOf(c).Elem()
// Iterate through all config fields.
-1
View File
@@ -25,7 +25,6 @@ func TestApplyCliContext_Duration(t *testing.T) {
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
+1 -1
View File
@@ -25,7 +25,7 @@ func (f CliFlag) Skip(tags []string) bool {
func (f CliFlag) Fields() reflect.Value {
fields := reflect.ValueOf(f.Flag)
for fields.Kind() == reflect.Ptr {
for fields.Kind() == reflect.Pointer {
fields = reflect.Indirect(fields)
}
+1 -1
View File
@@ -157,7 +157,7 @@ func (f CliFlags) SetHidden(hidden bool, names ...string) (result CliFlags) {
if list.Contains(names, flag.Name()) {
rv := reflect.ValueOf(flag.Flag)
if rv.Kind() == reflect.Ptr {
if rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
+4 -7
View File
@@ -86,7 +86,7 @@ type Config struct {
}
// Values is a shorthand alias for map[string]interface{}.
type Values = map[string]interface{}
type Values = map[string]any
func init() {
TotalMem = memory.TotalMemory()
@@ -747,12 +747,9 @@ func (c *Config) IndexWorkers() int {
}
// NumCPU returns the number of logical CPU cores.
cores := runtime.NumCPU()
// Limit to physical cores to avoid high load on HT capable CPUs.
if cores > cpuid.CPU.PhysicalCores {
cores = cpuid.CPU.PhysicalCores
}
cores := min(
// Limit to physical cores to avoid high load on HT capable CPUs.
runtime.NumCPU(), cpuid.CPU.PhysicalCores)
// Limit number of workers when using SQLite3 to avoid database locking issues.
if c.DatabaseDriver() == SQLite3 && (cores >= 8 && c.options.IndexWorkers <= 0 || c.options.IndexWorkers > 4) {
+1 -4
View File
@@ -126,10 +126,7 @@ func (c *Config) FaceEngineThreads() int {
if c == nil {
return 1
} else if c.options.FaceEngineThreads <= 0 {
threads := runtime.NumCPU() / 2
if threads < 1 {
threads = 1
}
threads := max(runtime.NumCPU()/2, 1)
c.options.FaceEngineThreads = threads
+1 -4
View File
@@ -162,10 +162,7 @@ func TestConfig_FaceEngineRunType(t *testing.T) {
func TestConfig_FaceEngineThreads(t *testing.T) {
c := NewConfig(CliTestContext())
expected := runtime.NumCPU() / 2
if expected < 1 {
expected = 1
}
expected := max(runtime.NumCPU()/2, 1)
assert.Equal(t, expected, c.FaceEngineThreads())
c.options.FaceEngineThreads = 8
+1 -1
View File
@@ -199,7 +199,7 @@ func (c *Config) LegalUrl() string {
func (c *Config) RobotsTxt() ([]byte, error) {
if c.Demo() && c.Public() {
// Allow public demo instances to be indexed.
return []byte(fmt.Sprintf("User-agent: *\nDisallow: /\nAllow: %s/\nAllow: %s/\nAllow: .js\nAllow: .css", LibraryUri, StaticUri)), nil
return fmt.Appendf(nil, "User-agent: *\nDisallow: /\nAllow: %s/\nAllow: %s/\nAllow: .js\nAllow: .css", LibraryUri, StaticUri), nil
} else if c.Public() {
// Do not allow other instances to be indexed when public mode is enabled.
return robotsTxt, nil
+2 -10
View File
@@ -111,22 +111,14 @@ func (c *Config) Usage() Usage {
info.FilesUsedPct = 1
}
info.FilesFreePct = 100 - info.FilesUsedPct
if info.FilesFreePct < 0 {
info.FilesFreePct = 0
}
info.FilesFreePct = max(100-info.FilesUsedPct, 0)
info.UsersActive = query.CountUsers(true, true, nil, []string{"guest"})
info.GuestsActive = query.CountUsers(true, true, []string{"guest"}, nil)
if info.UsersQuota = c.UsersQuota(); info.UsersQuota > 0 {
info.UsersUsedPct = int(math.Floor(info.UsersUsedRatio() * 100))
info.UsersFreePct = 100 - info.UsersUsedPct
if info.UsersFreePct < 0 {
info.UsersFreePct = 0
}
info.UsersFreePct = max(100-info.UsersUsedPct, 0)
} else {
info.UsersUsedPct = 0
info.UsersFreePct = 0
+1 -1
View File
@@ -25,7 +25,7 @@ type Manifest struct {
Scope string `json:"scope"`
StartUrl string `json:"start_url,omitempty"`
Shortcuts Urls `json:"shortcuts"`
Serviceworker Serviceworker `json:"serviceworker,omitempty"`
Serviceworker Serviceworker `json:"serviceworker"`
Permissions list.List `json:"permissions"`
OptionalPermissions list.List `json:"optional_permissions"`
HostPermissions []string `json:"host_permissions"`
+3 -3
View File
@@ -99,7 +99,7 @@ func (Album) TableName() string {
}
// UpdateAlbum updates album attributes directly in the database by UID.
func UpdateAlbum(albumUID string, values interface{}) (err error) {
func UpdateAlbum(albumUID string, values any) (err error) {
if rnd.InvalidUID(albumUID, AlbumUID) {
return fmt.Errorf("album: invalid uid %s", clean.Log(albumUID))
} else if err = Db().Model(Album{}).Where("album_uid = ?", albumUID).UpdateColumns(values).Error; err != nil {
@@ -727,7 +727,7 @@ func (m *Album) SaveForm(f *form.Album) error {
}
// Update sets a new value for a database column.
func (m *Album) Update(attr string, value interface{}) error {
func (m *Album) Update(attr string, value any) error {
if m == nil {
return errors.New("album must not be nil - you may have found a bug")
} else if !m.HasID() {
@@ -738,7 +738,7 @@ func (m *Album) Update(attr string, value interface{}) error {
}
// Updates multiple columns in the database.
func (m *Album) Updates(values interface{}) error {
func (m *Album) Updates(values any) error {
if m == nil {
return errors.New("album must not be nil - you may have found a bug")
} else if !m.HasID() {
+1 -1
View File
@@ -361,7 +361,7 @@ func (m *Client) Disabled() bool {
}
// Updates multiple properties in the database.
func (m *Client) Updates(values interface{}) error {
func (m *Client) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+1 -1
View File
@@ -272,7 +272,7 @@ func (m *Session) Delete() error {
}
// Updates multiple properties in the database.
func (m *Session) Updates(values interface{}) error {
func (m *Session) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+2 -7
View File
@@ -1,6 +1,7 @@
package entity
import (
"slices"
"strings"
)
@@ -86,13 +87,7 @@ func (data SessionData) HasShare(uid string) bool {
return false
}
for _, share := range data.Shares {
if share == uid {
return true
}
}
return false
return slices.Contains(data.Shares, uid)
}
// SharedUIDs returns shared entity UIDs.
+1 -1
View File
@@ -57,7 +57,7 @@ func TestDeleteClientSessions(t *testing.T) {
assert.Equal(t, 0, DeleteClientSessions(&Client{}, authn.MethodDefault, 0))
// Create 10 test client sessions.
for i := 0; i < 10; i++ {
for range 10 {
sess := NewSession(3600, 0)
sess.SetClient(client)
+1 -1
View File
@@ -378,7 +378,7 @@ func (m *User) SaveRelated() *User {
}
// Updates multiple properties in the database.
func (m *User) Updates(values interface{}) error {
func (m *User) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+1 -1
View File
@@ -98,7 +98,7 @@ func (m *UserDetails) Save() error {
}
// Updates multiple properties in the database.
func (m *UserDetails) Updates(values interface{}) error {
func (m *UserDetails) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+1 -1
View File
@@ -78,7 +78,7 @@ func (m *UserSettings) Save() error {
}
// Updates multiple properties in the database.
func (m *UserSettings) Updates(values interface{}) error {
func (m *UserSettings) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+1 -1
View File
@@ -144,7 +144,7 @@ func (m *UserShare) Save() error {
}
// Updates changes multiple record values.
func (m *UserShare) Updates(values interface{}) error {
func (m *UserShare) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+2 -2
View File
@@ -64,7 +64,7 @@ func (m *Details) Save() error {
}
// Update a column in the database.
func (m *Details) Update(attr string, value interface{}) error {
func (m *Details) Update(attr string, value any) error {
if m == nil {
return errors.New("photo details must not be nil - you may have found a bug")
} else if m.PhotoID < 1 {
@@ -75,7 +75,7 @@ func (m *Details) Update(attr string, value interface{}) error {
}
// Updates multiple columns in the database.
func (m *Details) Updates(values interface{}) error {
func (m *Details) Updates(values any) error {
if values == nil {
return nil
} else if m == nil {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
)
// Count returns the number of records for a given a model and key values.
func Count(m interface{}, keys []string, values []interface{}) int {
func Count(m any, keys []string, values []any) int {
if m == nil || len(keys) != len(values) {
log.Debugf("entity: invalid parameters (count records)")
return -1
+1 -1
View File
@@ -6,7 +6,7 @@ import (
)
// Save tries to update an existing record and falls back to insert semantics, retrying on lock errors.
func Save(m interface{}, keyNames ...string) (err error) {
func Save(m any, keyNames ...string) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("save: %s (panic)", r)
+2 -2
View File
@@ -11,7 +11,7 @@ import (
"github.com/photoprism/photoprism/pkg/clean"
)
type Tables map[string]interface{}
type Tables map[string]any
// Entities contains database entities and their table names.
var Entities = Tables{
@@ -105,7 +105,7 @@ func (list Tables) Truncate(db *gorm.DB) {
// Migrate migrates all database tables of registered entities.
func (list Tables) Migrate(db *gorm.DB, opt migrate.Options) {
var name string
var entity interface{}
var entity any
defer func() {
if r := recover(); r != nil {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
)
// Update updates the values of an existing database record.
func Update(m interface{}, keyNames ...string) (err error) {
func Update(m any, keyNames ...string) (err error) {
// Use an unscoped *gorm.DB connection, so that
// soft-deleted database records can also be updated.
db := UnscopedDb()
+6 -11
View File
@@ -3,25 +3,20 @@ package entity
import (
"fmt"
"reflect"
"slices"
)
// Values is a shorthand alias for map[string]interface{}.
type Values = map[string]interface{}
type Values = map[string]any
// Map is retained for backward compatibility.
// TODO: Remove when no longer needed.
type Map = Values
// ModelValues extracts exported struct fields into a Values map, optionally omitting selected names.
func ModelValues(m interface{}, omit ...string) (result Values, omitted []interface{}, err error) {
func ModelValues(m any, omit ...string) (result Values, omitted []any, err error) {
mustOmit := func(name string) bool {
for _, s := range omit {
if name == s {
return true
}
}
return false
return slices.Contains(omit, name)
}
r := reflect.ValueOf(m)
@@ -39,11 +34,11 @@ func ModelValues(m interface{}, omit ...string) (result Values, omitted []interf
t := values.Type()
num := t.NumField()
omitted = make([]interface{}, 0, len(omit))
omitted = make([]any, 0, len(omit))
result = make(Values, num)
// Add exported fields to result.
for i := 0; i < num; i++ {
for i := range num {
field := t.Field(i)
// Skip non-exported fields.
+2 -2
View File
@@ -396,7 +396,7 @@ func (m *Face) Delete() error {
}
// Update a face property in the database.
func (m *Face) Update(attr string, value interface{}) error {
func (m *Face) Update(attr string, value any) error {
if m.ID == "" {
return fmt.Errorf("empty id")
}
@@ -407,7 +407,7 @@ func (m *Face) Update(attr string, value interface{}) error {
}
// Updates face properties in the database.
func (m *Face) Updates(values interface{}) error {
func (m *Face) Updates(values any) error {
if m.ID == "" {
return fmt.Errorf("empty id")
}
+2 -2
View File
@@ -516,12 +516,12 @@ func (m *File) UpdateVideoInfos() error {
}
// Update updates a column in the database.
func (m *File) Update(attr string, value interface{}) error {
func (m *File) Update(attr string, value any) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
}
// Updates multiple columns in the database.
func (m *File) Updates(values interface{}) error {
func (m *File) Updates(values any) error {
return UnscopedDb().Model(m).UpdateColumns(values).Error
}
+6 -6
View File
@@ -49,12 +49,12 @@ func (m *File) MarshalJSON() ([]byte, error) {
Software string `json:",omitempty"`
Error string `json:",omitempty"`
ModTime int64 `json:",omitempty"`
CreatedAt time.Time `json:",omitempty"`
CreatedIn int64 `json:",omitempty"`
UpdatedAt time.Time `json:",omitempty"`
UpdatedIn int64 `json:",omitempty"`
DeletedAt *time.Time `json:",omitempty"`
Markers *Markers `json:",omitempty"`
CreatedAt time.Time
CreatedIn int64 `json:",omitempty"`
UpdatedAt time.Time
UpdatedIn int64 `json:",omitempty"`
DeletedAt *time.Time `json:",omitempty"`
Markers *Markers `json:",omitempty"`
}{
UID: m.FileUID,
PhotoUID: m.PhotoUID,
+2 -2
View File
@@ -45,12 +45,12 @@ func NewFileShare(fileID, accountID uint, remoteName string) *FileShare {
}
// Updates mutates multiple columns on the existing row.
func (m *FileShare) Updates(values interface{}) error {
func (m *FileShare) Updates(values any) error {
return UnscopedDb().Model(m).UpdateColumns(values).Error
}
// Update mutates a single column on the existing row.
func (m *FileShare) Update(attr string, value interface{}) error {
func (m *FileShare) Update(attr string, value any) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
}
+3 -3
View File
@@ -18,7 +18,7 @@ type FileSync struct {
RemoteName string `gorm:"primary_key;auto_increment:false;type:VARBINARY(255)" json:"RemoteName" yaml:"RemoteName,omitempty"`
ServiceID uint `gorm:"primary_key;auto_increment:false" json:"ServiceID" yaml:"ServiceID,omitempty"`
FileID uint `gorm:"index;" json:"FileID" yaml:"FileID,omitempty"`
RemoteDate time.Time `json:"RemoteDate,omitempty" yaml:"RemoteDate,omitempty"`
RemoteDate time.Time `json:"RemoteDate" yaml:"RemoteDate,omitempty"`
RemoteSize int64 `json:"RemoteSize,omitempty" yaml:"RemoteSize,omitempty"`
Status string `gorm:"type:VARBINARY(16);" json:"Status" yaml:"Status,omitempty"`
Error string `gorm:"type:VARBINARY(512);" json:"Error,omitempty" yaml:"Error,omitempty"`
@@ -46,12 +46,12 @@ func NewFileSync(accountID uint, remoteName string) *FileSync {
}
// Updates mutates multiple columns on the existing row.
func (m *FileSync) Updates(values interface{}) error {
func (m *FileSync) Updates(values any) error {
return UnscopedDb().Model(m).UpdateColumns(values).Error
}
// Update mutates a single column on the existing row.
func (m *FileSync) Update(attr string, value interface{}) error {
func (m *FileSync) Update(attr string, value any) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
}
+2 -2
View File
@@ -42,7 +42,7 @@ type Folder struct {
FileCount int `gorm:"-" json:"FileCount" yaml:"-"`
CreatedAt time.Time `json:"-" yaml:"-"`
UpdatedAt time.Time `json:"-" yaml:"-"`
ModifiedAt time.Time `json:"ModifiedAt,omitempty" yaml:"-"`
ModifiedAt time.Time `json:"ModifiedAt" yaml:"-"`
PublishedAt *time.Time `sql:"index" json:"PublishedAt,omitempty" yaml:"PublishedAt,omitempty"`
DeletedAt *time.Time `sql:"index" json:"-" yaml:"DeletedAt,omitempty"`
}
@@ -230,7 +230,7 @@ func FirstOrCreateFolder(m *Folder) *Folder {
}
// Updates selected properties in the database.
func (m *Folder) Updates(values interface{}) error {
func (m *Folder) Updates(values any) error {
return Db().Model(m).Updates(values).Error
}
+2 -2
View File
@@ -37,7 +37,7 @@ func NewKeyword(keyword string) *Keyword {
// Update modifies a single column on an already persisted keyword and relies on
// the standard GORM callback to evict the cached instance afterwards.
func (m *Keyword) Update(attr string, value interface{}) error {
func (m *Keyword) Update(attr string, value any) error {
if m == nil {
return errors.New("keyword must not be nil - you may have found a bug")
} else if !m.HasID() {
@@ -50,7 +50,7 @@ func (m *Keyword) Update(attr string, value interface{}) error {
// Updates applies a set of column changes to an existing keyword while keeping
// the cache consistent via the AfterUpdate hook.
func (m *Keyword) Updates(values interface{}) error {
func (m *Keyword) Updates(values any) error {
if values == nil {
return nil
} else if m == nil {
+2 -2
View File
@@ -201,7 +201,7 @@ func (m *Label) Skip() bool {
}
// Update a label property in the database.
func (m *Label) Update(attr string, value interface{}) error {
func (m *Label) Update(attr string, value any) error {
if m == nil {
return errors.New("label must not be nil - you may have found a bug")
} else if !m.HasID() {
@@ -212,7 +212,7 @@ func (m *Label) Update(attr string, value interface{}) error {
}
// Updates multiple columns in the database.
func (m *Label) Updates(values interface{}) error {
func (m *Label) Updates(values any) error {
if values == nil {
return nil
} else if m == nil {
+2 -2
View File
@@ -142,13 +142,13 @@ func (m *Marker) UpdateFile(file *File) (updated bool) {
}
// Updates multiple columns in the database.
func (m *Marker) Updates(values interface{}) error {
func (m *Marker) Updates(values any) error {
UpdateFaces.Store(true)
return UnscopedDb().Model(m).Updates(values).Error
}
// Update updates a column in the database.
func (m *Marker) Update(attr string, value interface{}) error {
func (m *Marker) Update(attr string, value any) error {
UpdateFaces.Store(true)
return UnscopedDb().Model(m).Update(attr, value).Error
}
-1
View File
@@ -1,5 +1,4 @@
//go:build ignore
// +build ignore
// This generates countries.go by running "go generate"
package main
+1 -1
View File
@@ -34,4 +34,4 @@ import (
var log = event.Log
// Map is a shorthand alias for map[string]interface{}.
type Map = map[string]interface{}
type Map = map[string]any
+1 -1
View File
@@ -105,7 +105,7 @@ func (m *Passcode) Delete() (err error) {
}
// Updates multiple properties in the database.
func (m *Passcode) Updates(values interface{}) error {
func (m *Passcode) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+8 -20
View File
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"path"
"slices"
"strconv"
"strings"
"sync"
@@ -99,8 +100,8 @@ type Photo struct {
Files []File `yaml:"-"`
Labels []PhotoLabel `yaml:"-"`
CreatedBy string `gorm:"type:VARBINARY(42);index" json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"`
CreatedAt time.Time `json:"CreatedAt,omitempty" yaml:"CreatedAt,omitempty"`
UpdatedAt time.Time `json:"UpdatedAt,omitempty" yaml:"UpdatedAt,omitempty"`
CreatedAt time.Time `json:"CreatedAt" yaml:"CreatedAt,omitempty"`
UpdatedAt time.Time `json:"UpdatedAt" yaml:"UpdatedAt,omitempty"`
EditedAt *time.Time `json:"EditedAt,omitempty" yaml:"EditedAt,omitempty"`
PublishedAt *time.Time `sql:"index" json:"PublishedAt,omitempty" yaml:"PublishedAt,omitempty"`
IndexedAt *time.Time `json:"IndexedAt,omitempty" yaml:"-"`
@@ -332,7 +333,7 @@ func (m *Photo) Save() error {
}
// Update a column in the database.
func (m *Photo) Update(attr string, value interface{}) error {
func (m *Photo) Update(attr string, value any) error {
if m == nil {
return errors.New("photo must not be nil - you may have found a bug")
} else if !m.HasID() {
@@ -343,7 +344,7 @@ func (m *Photo) Update(attr string, value interface{}) error {
}
// Updates multiple columns in the database.
func (m *Photo) Updates(values interface{}) error {
func (m *Photo) Updates(values any) error {
if values == nil {
return nil
} else if m == nil {
@@ -411,13 +412,7 @@ func (m *Photo) ResetDuration() {
func (m *Photo) HasMediaType(types ...media.Type) bool {
mediaType := m.MediaType()
for _, t := range types {
if mediaType == t {
return true
}
}
return false
return slices.Contains(types, mediaType)
}
// SetMediaType sets a new media type if its priority is higher than that of the current type.
@@ -944,11 +939,7 @@ func (m *Photo) AddLabels(labels classify.Labels) {
template := NewPhotoLabel(m.ID, labelEntity.ID, classifyLabel.Uncertainty, labelSrc)
template.Topicality = classifyLabel.Topicality
score := 0
if classifyLabel.NSFWConfidence > 0 {
score = classifyLabel.NSFWConfidence
}
score := max(classifyLabel.NSFWConfidence, 0)
if classifyLabel.NSFW && score == 0 {
score = 100
@@ -979,10 +970,7 @@ func (m *Photo) AddLabels(labels classify.Labels) {
}
if classifyLabel.NSFWConfidence > 0 || classifyLabel.NSFW {
nsfwScore := 0
if classifyLabel.NSFWConfidence > 0 {
nsfwScore = classifyLabel.NSFWConfidence
}
nsfwScore := max(classifyLabel.NSFWConfidence, 0)
if classifyLabel.NSFW && nsfwScore == 0 {
nsfwScore = 100
}
+2 -2
View File
@@ -42,7 +42,7 @@ func NewPhotoLabel(photoID, labelID uint, uncertainty int, source string) *Photo
}
// Updates mutates multiple columns in the database and clears cached copies.
func (m *PhotoLabel) Updates(values interface{}) error {
func (m *PhotoLabel) Updates(values any) error {
if m == nil {
return errors.New("photo label must not be nil - you may have found a bug")
} else if !m.HasID() {
@@ -58,7 +58,7 @@ func (m *PhotoLabel) Updates(values interface{}) error {
}
// Update mutates a single column in the database and clears cached copies.
func (m *PhotoLabel) Update(attr string, value interface{}) error {
func (m *PhotoLabel) Update(attr string, value any) error {
if m == nil {
return errors.New("photo label must not be nil - you may have found a bug")
} else if !m.HasID() {
-1
View File
@@ -475,7 +475,6 @@ func TestPhoto_ShouldGenerateCaption(t *testing.T) {
}
for _, tc := range ctx {
tc := tc
t.Run(tc.name, func(t *testing.T) {
result := tc.photo.ShouldGenerateCaption(tc.source, tc.force)
assert.Equal(t, tc.expect, result)
+1 -5
View File
@@ -188,11 +188,7 @@ func PurgeOrphanFaces(faceIds []string, ignored bool) (affected int, err error)
batchSize := BatchSize()
for i := 0; i < len(faceIds); i += batchSize {
j := i + batchSize
if j > len(faceIds) {
j = len(faceIds)
}
j := min(i+batchSize, len(faceIds))
// Next batch.
ids := faceIds[i:j]
+1 -1
View File
@@ -175,7 +175,7 @@ func TestFlagHiddenPhotos(t *testing.T) {
t.Run("SuccessWith1000", func(t *testing.T) {
var checkedTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
// Load 1000 photos that need to be hidden
for i := 0; i < 1000; i++ {
for range 1000 {
newPhoto := entity.Photo{ //JPG, Geo from metadata, indexed
//ID: 1000049,
PhotoUID: rnd.GenerateUID(entity.PhotoUID),
+1 -1
View File
@@ -34,7 +34,7 @@ type Album struct {
LinkCount int `json:"LinkCount"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt time.Time `json:"DeletedAt,omitempty"`
DeletedAt time.Time `json:"DeletedAt"`
}
type AlbumResults []Album
+10 -10
View File
@@ -184,7 +184,7 @@ func AnySlug(col, search, sep string) (where string) {
var wheres []string
var words []string
for _, w := range strings.Split(search, sep) {
for w := range strings.SplitSeq(search, sep) {
w = strings.TrimSpace(w)
words = append(words, txt.Slug(w))
@@ -225,7 +225,7 @@ func AnyInt(col, numbers, sep string, min, max int) (where string) {
var matches []int
var wheres []string
for _, n := range strings.Split(numbers, sep) {
for n := range strings.SplitSeq(numbers, sep) {
i := txt.Int(n)
if i == 0 || i < min || i > max {
@@ -249,19 +249,19 @@ func AnyInt(col, numbers, sep string, min, max int) (where string) {
// OrLike prepares a parameterised OR/LIKE clause for a single column. Star (* )
// wildcards are mapped to SQL percent wildcards before returning the query and
// bind values.
func OrLike(col, s string) (where string, values []interface{}) {
func OrLike(col, s string) (where string, values []any) {
if txt.Empty(col) || txt.Empty(s) {
return "", []interface{}{}
return "", []any{}
}
s = strings.ReplaceAll(s, "*", "%")
s = strings.ReplaceAll(s, "%%", "%")
terms := txt.UnTrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
values = make([]interface{}, len(terms))
values = make([]any, len(terms))
if l := len(terms); l == 0 {
return "", []interface{}{}
return "", []any{}
} else if l == 1 {
values[0] = terms[0]
} else {
@@ -279,9 +279,9 @@ func OrLike(col, s string) (where string, values []interface{}) {
// OrLikeCols behaves like OrLike but fans out the same search terms across
// multiple columns, preserving the order of values so callers can feed them to
// database/sql.
func OrLikeCols(cols []string, s string) (where string, values []interface{}) {
func OrLikeCols(cols []string, s string) (where string, values []any) {
if len(cols) == 0 || txt.Empty(s) {
return "", []interface{}{}
return "", []any{}
}
s = strings.ReplaceAll(s, "*", "%")
@@ -290,10 +290,10 @@ func OrLikeCols(cols []string, s string) (where string, values []interface{}) {
terms := txt.UnTrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
if len(terms) == 0 {
return "", []interface{}{}
return "", []any{}
}
values = make([]interface{}, len(terms)*len(cols))
values = make([]any, len(terms)*len(cols))
for j := range terms {
for i := range cols {
+14 -14
View File
@@ -322,31 +322,31 @@ func TestOrLike(t *testing.T) {
where, values := OrLike("k.keyword", "")
assert.Equal(t, "", where)
assert.Equal(t, []interface{}{}, values)
assert.Equal(t, []any{}, values)
})
t.Run("OneTerm", func(t *testing.T) {
where, values := OrLike("k.keyword", "bar")
assert.Equal(t, "k.keyword LIKE ?", where)
assert.Equal(t, []interface{}{"bar"}, values)
assert.Equal(t, []any{"bar"}, values)
})
t.Run("TwoTerms", func(t *testing.T) {
where, values := OrLike("k.keyword", "foo*%|bar")
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ?", where)
assert.Equal(t, []interface{}{"foo%", "bar"}, values)
assert.Equal(t, []any{"foo%", "bar"}, values)
})
t.Run("OneFilename", func(t *testing.T) {
where, values := OrLike("files.file_name", " 2790/07/27900704_070228_D6D51B6C.jpg")
assert.Equal(t, "files.file_name LIKE ?", where)
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
assert.Equal(t, []any{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
})
t.Run("TwoFilenames", func(t *testing.T) {
where, values := OrLike("files.file_name", "1990*|2790/07/27900704_070228_D6D51B6C.jpg")
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ?", where)
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
assert.Equal(t, []any{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
})
}
@@ -355,55 +355,55 @@ func TestOrLikeCols(t *testing.T) {
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "")
assert.Equal(t, "", where)
assert.Equal(t, []interface{}{}, values)
assert.Equal(t, []any{}, values)
})
t.Run("OneTerm", func(t *testing.T) {
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "bar")
assert.Equal(t, "k.keyword LIKE ? OR p.photo_caption LIKE ?", where)
assert.Equal(t, []interface{}{"bar", "bar"}, values)
assert.Equal(t, []any{"bar", "bar"}, values)
})
t.Run("TwoTerms", func(t *testing.T) {
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "foo*%|bar")
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ? OR p.photo_caption LIKE ? OR p.photo_caption LIKE ?", where)
assert.Equal(t, []interface{}{"foo%", "bar", "foo%", "bar"}, values)
assert.Equal(t, []any{"foo%", "bar", "foo%", "bar"}, values)
})
t.Run("OneTermEscaped", func(t *testing.T) {
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "\\|bar")
assert.Equal(t, "k.keyword LIKE ? OR p.photo_caption LIKE ?", where)
assert.Equal(t, []interface{}{"|bar", "|bar"}, values)
assert.Equal(t, []any{"|bar", "|bar"}, values)
})
t.Run("TwoTermsEscaped", func(t *testing.T) {
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "foo*%|\\|bar")
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ? OR p.photo_caption LIKE ? OR p.photo_caption LIKE ?", where)
assert.Equal(t, []interface{}{"foo%", "|bar", "foo%", "|bar"}, values)
assert.Equal(t, []any{"foo%", "|bar", "foo%", "|bar"}, values)
})
t.Run("OneFilename", func(t *testing.T) {
where, values := OrLikeCols([]string{"files.file_name"}, " 2790/07/27900704_070228_D6D51B6C.jpg")
assert.Equal(t, "files.file_name LIKE ?", where)
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
assert.Equal(t, []any{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
})
t.Run("TwoFilenames", func(t *testing.T) {
where, values := OrLikeCols([]string{"files.file_name", "photos.photo_name"}, "1990*|2790/07/27900704_070228_D6D51B6C.jpg")
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ? OR photos.photo_name LIKE ? OR photos.photo_name LIKE ?", where)
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
assert.Equal(t, []any{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
})
t.Run("OneFilenameEscaped", func(t *testing.T) {
where, values := OrLikeCols([]string{"files.file_name"}, " 2790/07/27900704_070228_D6D\\|51B6C.jpg")
assert.Equal(t, "files.file_name LIKE ?", where)
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
assert.Equal(t, []any{" 2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
})
t.Run("TwoFilenamesEscaped", func(t *testing.T) {
where, values := OrLikeCols([]string{"files.file_name", "photos.photo_name"}, "1990*|2790/07/27900704_070228_D6D\\|51B6C.jpg")
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ? OR photos.photo_name LIKE ? OR photos.photo_name LIKE ?", where)
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D|51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
assert.Equal(t, []any{"1990%", "2790/07/27900704_070228_D6D|51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
})
}
+1 -1
View File
@@ -21,5 +21,5 @@ type Label struct {
ThumbSrc string `json:"ThumbSrc,omitempty"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt time.Time `json:"DeletedAt,omitempty"`
DeletedAt time.Time `json:"DeletedAt"`
}
+2 -2
View File
@@ -104,8 +104,8 @@ type Photo struct {
Merged bool `json:"Merged" select:"-"`
CreatedAt time.Time `json:"CreatedAt" select:"photos.created_at"`
UpdatedAt time.Time `json:"UpdatedAt" select:"photos.updated_at"`
EditedAt time.Time `json:"EditedAt,omitempty" select:"photos.edited_at"`
CheckedAt time.Time `json:"CheckedAt,omitempty" select:"photos.checked_at"`
EditedAt time.Time `json:"EditedAt" select:"photos.edited_at"`
CheckedAt time.Time `json:"CheckedAt" select:"photos.checked_at"`
DeletedAt *time.Time `json:"DeletedAt,omitempty" select:"photos.deleted_at"`
// Additional information from the details table.
+3 -3
View File
@@ -11,15 +11,15 @@ import (
type Cols []string
// SelectString returns the columns for a search result struct as a string.
func SelectString(f interface{}, tags []string) string {
func SelectString(f any, tags []string) string {
return strings.Join(SelectCols(f, tags), ", ")
}
// SelectCols returns the columns for a search result struct.
func SelectCols(f interface{}, tags []string) Cols {
func SelectCols(f any, tags []string) Cols {
v := reflect.ValueOf(f)
if v.Kind() == reflect.Ptr {
if v.Kind() == reflect.Pointer {
v = v.Elem()
}
+2 -2
View File
@@ -214,12 +214,12 @@ func (m *Service) Directories() (result fs.FileInfos, err error) {
}
// Updates multiple columns in the database.
func (m *Service) Updates(values interface{}) error {
func (m *Service) Updates(values any) error {
return UnscopedDb().Model(m).UpdateColumns(values).Error
}
// Update a column in the database.
func (m *Service) Update(attr string, value interface{}) error {
func (m *Service) Update(attr string, value any) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
}
+2 -2
View File
@@ -192,12 +192,12 @@ func (m *Subject) Restore() error {
}
// Update updates an entity value in the database.
func (m *Subject) Update(attr string, value interface{}) error {
func (m *Subject) Update(attr string, value any) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
}
// Updates multiple values in the database.
func (m *Subject) Updates(values interface{}) error {
func (m *Subject) Updates(values any) error {
return UnscopedDb().Model(m).Updates(values).Error
}
+5 -5
View File
@@ -13,7 +13,7 @@ var AuditLog Logger
var AuditPrefix = "audit: "
// Audit optionally reports security-relevant events.
func Audit(level logrus.Level, ev []string, args ...interface{}) {
func Audit(level logrus.Level, ev []string, args ...any) {
// Skip if empty.
if len(ev) == 0 {
return
@@ -41,21 +41,21 @@ func Audit(level logrus.Level, ev []string, args ...interface{}) {
}
// AuditDebug records an audit entry at debug level.
func AuditDebug(ev []string, args ...interface{}) {
func AuditDebug(ev []string, args ...any) {
Audit(logrus.DebugLevel, ev, args...)
}
// AuditInfo records an audit entry at info level.
func AuditInfo(ev []string, args ...interface{}) {
func AuditInfo(ev []string, args ...any) {
Audit(logrus.InfoLevel, ev, args...)
}
// AuditWarn records an audit entry at warning level.
func AuditWarn(ev []string, args ...interface{}) {
func AuditWarn(ev []string, args ...any) {
Audit(logrus.WarnLevel, ev, args...)
}
// AuditErr records an audit entry at error level.
func AuditErr(ev []string, args ...interface{}) {
func AuditErr(ev []string, args ...any) {
Audit(logrus.ErrorLevel, ev, args...)
}
+1 -1
View File
@@ -9,6 +9,6 @@ import (
var MessageSep = " "
// Format formats an audit log event.
func Format(ev []string, args ...interface{}) string {
func Format(ev []string, args ...any) string {
return fmt.Sprintf(strings.Join(ev, MessageSep), args...)
}
+31 -31
View File
@@ -10,32 +10,32 @@ import (
// Logger is a logrus compatible logger interface.
type Logger interface {
WithField(key string, value interface{}) *logrus.Entry
WithField(key string, value any) *logrus.Entry
WithFields(fields logrus.Fields) *logrus.Entry
WithError(err error) *logrus.Entry
WithContext(ctx context.Context) *logrus.Entry
WithTime(t time.Time) *logrus.Entry
Logf(level logrus.Level, format string, args ...interface{})
Tracef(format string, args ...interface{})
Debugf(format string, args ...interface{})
Infof(format string, args ...interface{})
Printf(format string, args ...interface{})
Warnf(format string, args ...interface{})
Warningf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
Panicf(format string, args ...interface{})
Log(level logrus.Level, args ...interface{})
Logf(level logrus.Level, format string, args ...any)
Tracef(format string, args ...any)
Debugf(format string, args ...any)
Infof(format string, args ...any)
Printf(format string, args ...any)
Warnf(format string, args ...any)
Warningf(format string, args ...any)
Errorf(format string, args ...any)
Fatalf(format string, args ...any)
Panicf(format string, args ...any)
Log(level logrus.Level, args ...any)
LogFn(level logrus.Level, fn logrus.LogFunction)
Trace(args ...interface{})
Debug(args ...interface{})
Info(args ...interface{})
Print(args ...interface{})
Warn(args ...interface{})
Warning(args ...interface{})
Error(args ...interface{})
Fatal(args ...interface{})
Panic(args ...interface{})
Trace(args ...any)
Debug(args ...any)
Info(args ...any)
Print(args ...any)
Warn(args ...any)
Warning(args ...any)
Error(args ...any)
Fatal(args ...any)
Panic(args ...any)
TraceFn(fn logrus.LogFunction)
DebugFn(fn logrus.LogFunction)
InfoFn(fn logrus.LogFunction)
@@ -45,16 +45,16 @@ type Logger interface {
ErrorFn(fn logrus.LogFunction)
FatalFn(fn logrus.LogFunction)
PanicFn(fn logrus.LogFunction)
Logln(level logrus.Level, args ...interface{})
Traceln(args ...interface{})
Debugln(args ...interface{})
Infoln(args ...interface{})
Println(args ...interface{})
Warnln(args ...interface{})
Warningln(args ...interface{})
Errorln(args ...interface{})
Fatalln(args ...interface{})
Panicln(args ...interface{})
Logln(level logrus.Level, args ...any)
Traceln(args ...any)
Debugln(args ...any)
Infoln(args ...any)
Println(args ...any)
Warnln(args ...any)
Warningln(args ...any)
Errorln(args ...any)
Fatalln(args ...any)
Panicln(args ...any)
Exit(code int)
SetNoLock()
SetLevel(level logrus.Level)
+4 -4
View File
@@ -39,21 +39,21 @@ func Warn(msg string) {
}
// ErrorMsg publishes a localized error notification.
func ErrorMsg(id i18n.Message, params ...interface{}) {
func ErrorMsg(id i18n.Message, params ...any) {
Error(i18n.Msg(id, params...))
}
// SuccessMsg publishes a localized success notification.
func SuccessMsg(id i18n.Message, params ...interface{}) {
func SuccessMsg(id i18n.Message, params ...any) {
Success(i18n.Msg(id, params...))
}
// InfoMsg publishes a localized informational notification.
func InfoMsg(id i18n.Message, params ...interface{}) {
func InfoMsg(id i18n.Message, params ...any) {
Info(i18n.Msg(id, params...))
}
// WarnMsg publishes a localized warning notification.
func WarnMsg(id i18n.Message, params ...interface{}) {
func WarnMsg(id i18n.Message, params ...any) {
Warn(i18n.Msg(id, params...))
}
+7 -7
View File
@@ -16,7 +16,7 @@ const (
)
// PublishEntities publishes updated entity data.
func PublishEntities(channel, ev string, entities interface{}) {
func PublishEntities(channel, ev string, entities any) {
if channel == "" || ev == "" || entities == nil {
return
}
@@ -29,7 +29,7 @@ func PublishEntities(channel, ev string, entities interface{}) {
}
// PublishUserEntities publishes updated entity data for a user.
func PublishUserEntities(channel, ev string, entities interface{}, userUid string) {
func PublishUserEntities(channel, ev string, entities any, userUid string) {
if userUid == "" {
PublishEntities(channel, ev, entities)
return
@@ -46,26 +46,26 @@ func PublishUserEntities(channel, ev string, entities interface{}, userUid strin
}
// EntitiesUpdated publishes an update notification for the given channel.
func EntitiesUpdated(channel string, entities interface{}) {
func EntitiesUpdated(channel string, entities any) {
PublishEntities(channel, EntityUpdated, entities)
}
// EntitiesCreated publishes a create notification for the given channel.
func EntitiesCreated(channel string, entities interface{}) {
func EntitiesCreated(channel string, entities any) {
PublishEntities(channel, EntityCreated, entities)
}
// EntitiesDeleted publishes a delete notification for the given channel.
func EntitiesDeleted(channel string, entities interface{}) {
func EntitiesDeleted(channel string, entities any) {
PublishEntities(channel, EntityDeleted, entities)
}
// EntitiesArchived publishes an archive notification for the given channel.
func EntitiesArchived(channel string, entities interface{}) {
func EntitiesArchived(channel string, entities any) {
PublishEntities(channel, EntityArchived, entities)
}
// EntitiesRestored publishes a restore notification for the given channel.
func EntitiesRestored(channel string, entities interface{}) {
func EntitiesRestored(channel string, entities any) {
PublishEntities(channel, EntityRestored, entities)
}
+5 -5
View File
@@ -13,7 +13,7 @@ var SystemLog Logger
var SystemPrefix = "system: "
// System writes a system-level log entry and publishes it to the hub.
func System(level logrus.Level, ev []string, args ...interface{}) {
func System(level logrus.Level, ev []string, args ...any) {
if len(ev) == 0 {
return
}
@@ -35,21 +35,21 @@ func System(level logrus.Level, ev []string, args ...interface{}) {
}
// SystemDebug records a system debug message.
func SystemDebug(ev []string, args ...interface{}) {
func SystemDebug(ev []string, args ...any) {
System(logrus.DebugLevel, ev, args...)
}
// SystemInfo records a system info message.
func SystemInfo(ev []string, args ...interface{}) {
func SystemInfo(ev []string, args ...any) {
System(logrus.InfoLevel, ev, args...)
}
// SystemWarn records a system warning.
func SystemWarn(ev []string, args ...interface{}) {
func SystemWarn(ev []string, args ...any) {
System(logrus.WarnLevel, ev, args...)
}
// SystemError records a system error message.
func SystemError(ev []string, args ...interface{}) {
func SystemError(ev []string, args ...any) {
System(logrus.ErrorLevel, ev, args...)
}
+9 -9
View File
@@ -19,7 +19,7 @@ type systemTestLogger struct {
type logEntry struct {
level logrus.Level
args []interface{}
args []any
}
func newSystemTestLogger() *systemTestLogger {
@@ -29,11 +29,11 @@ func newSystemTestLogger() *systemTestLogger {
return &systemTestLogger{Logger: logger}
}
func (l *systemTestLogger) Log(level logrus.Level, args ...interface{}) {
func (l *systemTestLogger) Log(level logrus.Level, args ...any) {
l.mu.Lock()
defer l.mu.Unlock()
entryArgs := append([]interface{}(nil), args...)
entryArgs := append([]any(nil), args...)
l.entries = append(l.entries, logEntry{level: level, args: entryArgs})
}
@@ -59,12 +59,12 @@ func TestSystemLoggingFunctions(t *testing.T) {
tests := []struct {
name string
level logrus.Level
call func(ev []string, args ...interface{})
call func(ev []string, args ...any)
}{
{name: "Info", level: logrus.InfoLevel, call: func(ev []string, args ...interface{}) { SystemInfo(ev, args...) }},
{name: "Warn", level: logrus.WarnLevel, call: func(ev []string, args ...interface{}) { SystemWarn(ev, args...) }},
{name: "Debug", level: logrus.DebugLevel, call: func(ev []string, args ...interface{}) { SystemDebug(ev, args...) }},
{name: "Error", level: logrus.ErrorLevel, call: func(ev []string, args ...interface{}) { SystemError(ev, args...) }},
{name: "Info", level: logrus.InfoLevel, call: func(ev []string, args ...any) { SystemInfo(ev, args...) }},
{name: "Warn", level: logrus.WarnLevel, call: func(ev []string, args ...any) { SystemWarn(ev, args...) }},
{name: "Debug", level: logrus.DebugLevel, call: func(ev []string, args ...any) { SystemDebug(ev, args...) }},
{name: "Error", level: logrus.ErrorLevel, call: func(ev []string, args ...any) { SystemError(ev, args...) }},
}
for _, tt := range tests {
@@ -75,7 +75,7 @@ func TestSystemLoggingFunctions(t *testing.T) {
t.Cleanup(func() { SystemLog = orig })
events := []string{"cleanup %s", "finished"}
args := []interface{}{"cache"}
args := []any{"cache"}
expectedMessage := Format(events, args...)
topic := "system.log." + tt.level.String()
+1 -1
View File
@@ -22,7 +22,7 @@ type Album struct {
}
// NewAlbum creates a new form struct based on the interface values.
func NewAlbum(m interface{}) (*Album, error) {
func NewAlbum(m any) (*Album, error) {
frm := &Album{}
err := deepcopier.Copy(m).To(frm)
+1 -1
View File
@@ -9,7 +9,7 @@ type Face struct {
}
// NewFace copies values from an arbitrary model into a Face form.
func NewFace(m interface{}) (f Face, err error) {
func NewFace(m any) (f Face, err error) {
err = deepcopier.Copy(m).To(&f)
return f, err
+1 -1
View File
@@ -18,7 +18,7 @@ func (f Feedback) Empty() bool {
}
// NewFeedback copies values from an arbitrary model into a Feedback form.
func NewFeedback(m interface{}) (f Feedback, err error) {
func NewFeedback(m any) (f Feedback, err error) {
err = deepcopier.Copy(m).To(&f)
return f, err
+1 -1
View File
@@ -17,7 +17,7 @@ func (f *File) Orientation() int {
}
// NewFile copies values from an arbitrary model into a File form.
func NewFile(m interface{}) (f File, err error) {
func NewFile(m any) (f File, err error) {
err = deepcopier.Copy(m).To(&f)
return f, err
+1 -1
View File
@@ -21,7 +21,7 @@ type Folder struct {
}
// NewFolder copies values from an arbitrary model into a Folder form.
func NewFolder(m interface{}) (f Folder, err error) {
func NewFolder(m any) (f Folder, err error) {
err = deepcopier.Copy(m).To(&f)
return f, err
+2 -2
View File
@@ -9,12 +9,12 @@ import (
)
// Report returns form fields as table rows for reports.
func Report(f interface{}) (rows [][]string, cols []string) {
func Report(f any) (rows [][]string, cols []string) {
cols = []string{"Filter", "Type", "Examples", "Notes"}
v := reflect.ValueOf(f)
if v.Kind() == reflect.Ptr {
if v.Kind() == reflect.Pointer {
v = v.Elem()
}

Some files were not shown because too many files have changed in this diff Show More