diff --git a/docs/manual.md b/docs/manual.md index 116ddb7..15f3f00 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -269,16 +269,21 @@ enbas show --type blocked ### Mute an account -``` -enbas mute --type account --account-name @name@example.social --mute-notifications --mute-duration="1h" -``` +- Mute an account indefinitely. + ``` + enbas mute --type account --account-name @name@example.social --mute-notifications" + ``` +- Mute an account for 1 and a half hours. + ``` + enbas mute --type account --account-name @name@example.social --mute-notifications --mute-duration="1 hour and 30 minutes" + ``` | flag | type | required | description | default | |------|------|----------|-------------|---------| | `type` | string | true | The resource you want to mute.
Here this should be `account`. | | | `account-name` | string | true | The name of the account to mute. | | | `mute-notifications` | boolean | false | Set to `true` to mute notifications as well as statuses. | false | -| `mute-duration` | string | false | Specify how long the account should be muted for.
Set to `0s` to mute indefinitely | 0s (indefinitely). | +| `mute-duration` | string | false | Specify how long the account should be muted for.
Set to `0 seconds` to mute indefinitely | 0 seconds (indefinitely). | ### Unmute an account @@ -476,7 +481,7 @@ Creates a new status. --content "The age-old question: which text editor do you prefer?" \ --add-poll \ --poll-allows-multiple-choices=false \ - --poll-expires-in 168h \ + --poll-expires-in "7 days" \ --poll-option "emacs" \ --poll-option "vim/neovim" \ --poll-option "nano" \ diff --git a/internal/flag/timedurationvalue.go b/internal/flag/timedurationvalue.go index cdf02ac..010a8d2 100644 --- a/internal/flag/timedurationvalue.go +++ b/internal/flag/timedurationvalue.go @@ -2,16 +2,21 @@ package flag import ( "fmt" + "regexp" + "strconv" + "strings" "time" ) +const timeDurationRegexPattern string = `[0-9]{1,4}\s+(days?|hours?|minutes?|seconds?)` + type TimeDurationValue struct { Duration time.Duration } func NewTimeDurationValue() TimeDurationValue { return TimeDurationValue{ - Duration: 0 * time.Second, + Duration: time.Duration(0), } } @@ -19,13 +24,56 @@ func (v TimeDurationValue) String() string { return v.Duration.String() } -func (v *TimeDurationValue) Set(text string) error { - duration, err := time.ParseDuration(text) - if err != nil { - return fmt.Errorf("unable to parse the value as time duration: %w", err) +func (v *TimeDurationValue) Set(value string) error { + pattern := regexp.MustCompile(timeDurationRegexPattern) + matches := pattern.FindAllString(value, -1) + + days, hours, minutes, seconds := 0, 0, 0, 0 + + var err error + + for ind := range len(matches) { + switch { + case strings.Contains(matches[ind], "day"): + days, err = parseInt(matches[ind]) + if err != nil { + return fmt.Errorf("unable to parse the integer from %s: %w", matches[ind], err) + } + case strings.Contains(matches[ind], "hour"): + hours, err = parseInt(matches[ind]) + if err != nil { + return fmt.Errorf("unable to parse the integer from %s: %w", matches[ind], err) + } + case strings.Contains(matches[ind], "minute"): + minutes, err = parseInt(matches[ind]) + if err != nil { + return fmt.Errorf("unable to parse the integer from %s: %w", matches[ind], err) + } + case strings.Contains(matches[ind], "second"): + seconds, err = parseInt(matches[ind]) + if err != nil { + return fmt.Errorf("unable to parse the integer from %s: %w", matches[ind], err) + } + } } - v.Duration = duration + durationValue := (days * 86400) + (hours * 3600) + (minutes * 60) + seconds + + v.Duration = time.Duration(durationValue) * time.Second return nil } + +func parseInt(text string) (int, error) { + split := strings.SplitN(text, " ", 2) + if len(split) != 2 { + return 0, fmt.Errorf("unexpected number of split for %s: want 2, got %d", text, len(split)) + } + + output, err := strconv.Atoi(split[0]) + if err != nil { + return 0, fmt.Errorf("unable to convert %s to an integer: %w", text, err) + } + + return output, nil +} diff --git a/internal/flag/timedurationvalue_test.go b/internal/flag/timedurationvalue_test.go new file mode 100644 index 0000000..81fca3e --- /dev/null +++ b/internal/flag/timedurationvalue_test.go @@ -0,0 +1,61 @@ +package flag_test + +import ( + "slices" + "testing" + + internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag" +) + +func TestTimeDurationValue(t *testing.T) { + parsingTests := []struct { + input string + want string + }{ + { + input: "1 day", + want: "24h0m0s", + }, + { + input: "3 days, 5 hours, 39 minutes and 6 seconds", + want: "77h39m6s", + }, + { + input: "1 minute and 30 seconds", + want: "1m30s", + }, + { + input: "(7 seconds) (21 hours) (41 days)", + want: "1005h0m7s", + }, + } + + value := internalFlag.NewTimeDurationValue() + + for _, test := range slices.All(parsingTests) { + if err := value.Set(test.input); err != nil { + t.Fatalf( + "Unable to parse %s into a TimeDurationValue: %v", + test.input, + err, + ) + } + + got := value.String() + + if got != test.want { + t.Errorf( + "Unexpected duration parsed from %s: want %s, got %s", + test.input, + test.want, + got, + ) + } else { + t.Logf( + "Expected duration parsed from %s: got %s", + test.input, + got, + ) + } + } +}