fs: improve JSON Unmarshalling for Duration

Enhanced the UnmarshalJSON method for the Duration type to correctly
handle the special string 'off' and ensure large integers are parsed
accurately without floating-point rounding errors. This resolves
issues with setting and removing the MinAge filter through the rclone
rc command.

Fixes #3783

Co-authored-by: Kyle Reynolds <kyle.reynolds@bridgerphotonics.com>
This commit is contained in:
Kyle Reynolds 2024-03-13 12:08:59 -06:00 committed by GitHub
parent 00fb847662
commit 7803b4ed6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 3 deletions

View File

@ -1,6 +1,7 @@
package fs package fs
import ( import (
"encoding/json"
"fmt" "fmt"
"math" "math"
"strconv" "strconv"
@ -231,10 +232,26 @@ func (d Duration) Type() string {
// UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON // UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON
func (d *Duration) UnmarshalJSON(in []byte) error { func (d *Duration) UnmarshalJSON(in []byte) error {
return UnmarshalJSONFlag(in, d, func(i int64) error { // Check if the input is a string value.
*d = Duration(i) if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' {
strVal := string(in[1 : len(in)-1]) // Remove the quotes
// Attempt to parse the string as a duration.
parsedDuration, err := ParseDuration(strVal)
if err != nil {
return err
}
*d = Duration(parsedDuration)
return nil return nil
}) }
// Handle numeric values.
var i int64
err := json.Unmarshal(in, &i)
if err != nil {
return err
}
*d = Duration(i)
return nil
} }
// Scan implements the fmt.Scanner interface // Scan implements the fmt.Scanner interface

View File

@ -214,3 +214,32 @@ func TestParseUnmarshalJSON(t *testing.T) {
assert.Equal(t, Duration(test.want), duration, test.in) assert.Equal(t, Duration(test.want), duration, test.in)
} }
} }
func TestUnmarshalJSON(t *testing.T) {
tests := []struct {
name string
input string
want Duration
wantErr bool
}{
{"off string", `"off"`, DurationOff, false},
{"max int64", `9223372036854775807`, DurationOff, false},
{"duration string", `"1h"`, Duration(time.Hour), false},
{"invalid string", `"invalid"`, 0, true},
{"negative int", `-1`, Duration(-1), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var d Duration
err := json.Unmarshal([]byte(tt.input), &d)
if (err != nil) != tt.wantErr {
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if d != tt.want {
t.Errorf("UnmarshalJSON() got = %v, want %v", d, tt.want)
}
})
}
}