Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions internal/cmd/catalogcmd/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"math"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -105,9 +104,11 @@ func newTrack(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderTrack(track, fields)
var rendered string
if opts.id {
rendered = output.RenderTrackIDs([]client.Track{track})
} else {
rendered = output.RenderTrack(track, fields)
}
return writeOutput(command, rendered)
})
Expand Down Expand Up @@ -137,9 +138,11 @@ func newAlbum(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderAlbum(album, fields)
var rendered string
if opts.id {
rendered = output.RenderAlbumIDs([]client.Album{album})
} else {
rendered = output.RenderAlbum(album, fields)
}
return writeOutput(command, rendered)
})
Expand Down Expand Up @@ -168,9 +171,11 @@ func newArtist(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderArtist(artist, fields)
var rendered string
if opts.id {
rendered = output.RenderArtistIDs([]client.Artist{artist})
} else {
rendered = output.RenderArtist(artist, fields)
}
return writeOutput(command, rendered)
})
Expand All @@ -194,7 +199,7 @@ func newAlbumTracks(deps Dependencies) *cobra.Command {
}
}
scope := "album-tracks:" + id
offset, err := decodeTraversalToken(scope, opts.nextPageToken, 50)
offset, err := decodeTraversalToken(scope, opts.nextPageToken)
if err != nil {
return err
}
Expand All @@ -207,11 +212,11 @@ func newAlbumTracks(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderTracks(page.Items, fields)
var rendered string
if opts.id {
rendered = output.RenderTrackIDs(page.Items)
} else {
rendered = "Album ID: " + id + "\n" + rendered
rendered = "Album ID: " + id + "\n" + output.RenderTracks(page.Items, fields)
}
return writeListOutput(command, rendered, scope, page.Offset, page.Limit, page.HasNext)
})
Expand All @@ -235,7 +240,7 @@ func newArtistAlbums(deps Dependencies) *cobra.Command {
}
}
scope := "artist-albums:" + id
offset, err := decodeTraversalToken(scope, opts.nextPageToken, 10)
offset, err := decodeTraversalToken(scope, opts.nextPageToken)
if err != nil {
return err
}
Expand All @@ -248,11 +253,11 @@ func newArtistAlbums(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderAlbums(page.Items, fields)
var rendered string
if opts.id {
rendered = output.RenderAlbumIDs(page.Items)
} else {
rendered = "Artist ID: " + id + "\n" + rendered
rendered = "Artist ID: " + id + "\n" + output.RenderAlbums(page.Items, fields)
}
return writeListOutput(command, rendered, scope, page.Offset, page.Limit, page.HasNext)
})
Expand All @@ -276,8 +281,8 @@ func listCommand(parent, resource string, maxResults int, artwork bool, opts *li
return command
}

func decodeTraversalToken(scope, value string, pageLimit int) (int, error) {
offset, err := pagetoken.Decode(scope, value, math.MaxInt-pageLimit)
func decodeTraversalToken(scope, value string) (int, error) {
offset, err := pagetoken.Decode(scope, value)
if err != nil {
return 0, exitcode.New(exitcode.Usage, errors.New("invalid --next-page-token"))
}
Expand Down
8 changes: 8 additions & 0 deletions internal/cmd/catalogcmd/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ func TestCatalogGetOutputFlags(t *testing.T) {
if err != nil || stdout != id+"\n" {
t.Fatalf("stdout=%q error=%v", stdout, err)
}
stdout, _, _, _, err = execute("albums", "get", id, "--id")
if err != nil || stdout != id+"\n" {
t.Fatalf("album stdout=%q error=%v", stdout, err)
}
stdout, _, _, _, err = execute("artists", "get", id, "--id")
if err != nil || stdout != id+"\n" {
t.Fatalf("artist stdout=%q error=%v", stdout, err)
}
stdout, _, _, _, err = execute("tracks", "get", id, "--fields", "track,album_id")
if err != nil || stdout != id+" Song\nAlbum ID: album-1\n" {
t.Fatalf("stdout=%q error=%v", stdout, err)
Expand Down
11 changes: 7 additions & 4 deletions internal/cmd/librarycmd/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"math"
"slices"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -92,9 +91,11 @@ func newTrackList(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderSavedTracks(page.Items, fields)
var rendered string
if opts.id {
rendered = output.RenderSavedTrackIDs(page.Items)
} else {
rendered = output.RenderSavedTracks(page.Items, fields)
}
return writeListOutput(command, rendered, "tracks", trackPageScope, page.Offset, page.Limit, page.HasNext)
})
Expand Down Expand Up @@ -123,9 +124,11 @@ func newAlbumList(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderSavedAlbums(page.Items, fields)
var rendered string
if opts.id {
rendered = output.RenderSavedAlbumIDs(page.Items)
} else {
rendered = output.RenderSavedAlbums(page.Items, fields)
}
return writeListOutput(command, rendered, "albums", albumPageScope, page.Offset, page.Limit, page.HasNext)
})
Expand All @@ -141,7 +144,7 @@ func listCommand(short, resource, pageScope string, opts *listOptions, prepare f
if err := prepare(); err != nil {
return err
}
offset, err := pagetoken.Decode(pageScope, opts.nextPageToken, math.MaxInt-50)
offset, err := pagetoken.Decode(pageScope, opts.nextPageToken)
if err != nil {
return exitcode.New(exitcode.Usage, errors.New("invalid --next-page-token"))
}
Expand Down
13 changes: 8 additions & 5 deletions internal/cmd/playlistcmd/playlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"math"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -611,7 +610,7 @@ func newItemsList(deps Dependencies) *cobra.Command {
}
}
scope := playlistItemPageScope(id)
offset, err := pagetoken.Decode(scope, opts.nextPageToken, math.MaxInt-50)
offset, err := pagetoken.Decode(scope, opts.nextPageToken)
if err != nil {
return exitcode.New(exitcode.Usage, errors.New("invalid --next-page-token"))
}
Expand Down Expand Up @@ -666,7 +665,7 @@ func newList(deps Dependencies) *cobra.Command {
return exitcode.New(exitcode.Usage, err)
}
}
offset, err := pagetoken.Decode(playlistPageScope, opts.nextPageToken, math.MaxInt-50)
offset, err := pagetoken.Decode(playlistPageScope, opts.nextPageToken)
if err != nil {
return exitcode.New(exitcode.Usage, errors.New("invalid --next-page-token"))
}
Expand All @@ -679,9 +678,11 @@ func newList(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderPlaylists(page.Items, fields)
var rendered string
if opts.id {
rendered = output.RenderPlaylistIDs(page.Items)
} else {
rendered = output.RenderPlaylists(page.Items, fields)
}
if err := writeOutput(command, rendered); err != nil {
return err
Expand Down Expand Up @@ -727,9 +728,11 @@ func newGet(deps Dependencies) *cobra.Command {
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderPlaylist(playlist, fields)
var rendered string
if opts.id {
rendered = output.RenderPlaylistIDs([]client.Playlist{playlist})
} else {
rendered = output.RenderPlaylist(playlist, fields)
}
return writeOutput(command, rendered)
},
Expand Down
10 changes: 2 additions & 8 deletions internal/cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,10 @@ type Dependencies struct {
OpenSetCredentialStore setcredential.StoreOpener
Now func() time.Time
Interactive bool
Prompt func(*initcmd.Setup) error
OpenBrowser func(string) error
HTTPClient *http.Client
OAuthEndpoints auth.Endpoints
APIBaseURL string
SaveConfig func(config.Config) error
}

// New constructs the top-level command from its runtime effects.
Expand Down Expand Up @@ -89,12 +87,8 @@ func New(deps Dependencies) *cobra.Command {
authorizer := auth.Authorizer{
HTTPClient: deps.HTTPClient, Endpoints: deps.OAuthEndpoints, OpenBrowser: deps.OpenBrowser,
}
saveConfig := deps.SaveConfig
if saveConfig == nil {
saveConfig = func(value config.Config) error { return config.Save(deps.Scope, value) }
}
cmd.AddCommand(initcmd.New(initcmd.Dependencies{
Scope: deps.Scope, Interactive: deps.Interactive, Prompt: deps.Prompt,
Scope: deps.Scope, Interactive: deps.Interactive,
Initializer: initcmd.Initializer{
OpenStore: deps.OpenInitStore, Now: deps.Now, Authorize: authorizer.Authorize,
Verify: func(ctx context.Context, _ config.Config, envelope token.Envelope) (client.User, error) {
Expand All @@ -108,7 +102,7 @@ func New(deps Dependencies) *cobra.Command {
}))
return (client.Client{HTTPClient: httpClient, BaseURL: deps.APIBaseURL}).Me(ctx)
},
SaveConfig: saveConfig,
SaveConfig: func(value config.Config) error { return config.Save(deps.Scope, value) },
},
}))
sessionOpener := session.Opener{
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/root/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ func TestInitRequiresStdinModeForHTTPSCallback(t *testing.T) {

func TestBackendValidationRunsForStoreFreeCommands(t *testing.T) {
for _, backend := range []string{"definitely-invalid", "memory"} {
for _, args := range [][]string{{"config", "path"}, {"search", "track", "query"}} {
for _, args := range [][]string{nil, {"config", "path"}, {"search", "track", "query"}} {
h := newHarness(t)
err := h.execute(append([]string{"--backend", backend}, args...)...)
if exitcode.Code(err) != exitcode.Usage {
Expand Down
16 changes: 11 additions & 5 deletions internal/cmd/searchcmd/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ func runTrack(command *cobra.Command, deps Dependencies, query string, options s
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderTracks(page.Items, fields)
var rendered string
if options.id {
rendered = output.RenderTrackIDs(page.Items)
} else {
rendered = output.RenderTracks(page.Items, fields)
}
return writeSearchOutput(command, rendered, "track", page.Offset, page.Limit, page.HasNext)
}
Expand Down Expand Up @@ -121,9 +123,11 @@ func runAlbum(command *cobra.Command, deps Dependencies, query string, options s
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderAlbums(page.Items, fields)
var rendered string
if options.id {
rendered = output.RenderAlbumIDs(page.Items)
} else {
rendered = output.RenderAlbums(page.Items, fields)
}
return writeSearchOutput(command, rendered, "album", page.Offset, page.Limit, page.HasNext)
}
Expand Down Expand Up @@ -173,9 +177,11 @@ func runArtist(command *cobra.Command, deps Dependencies, query string, options
if err != nil {
return exitcode.New(cmdutil.Classify(err), err)
}
rendered := output.RenderArtists(page.Items, fields)
var rendered string
if options.id {
rendered = output.RenderArtistIDs(page.Items)
} else {
rendered = output.RenderArtists(page.Items, fields)
}
return writeSearchOutput(command, rendered, "artist", page.Offset, page.Limit, page.HasNext)
}
Expand Down Expand Up @@ -208,8 +214,8 @@ func writeSearchOutput(command *cobra.Command, rendered, surface string, offset,
}

func decodePageToken(surface, value string) (int, error) {
offset, err := pagetoken.Decode(surface, value, maxOffset)
if err != nil {
offset, err := pagetoken.Decode(surface, value)
if err != nil || offset > maxOffset {
return 0, errors.New("invalid --next-page-token")
}
return offset, nil
Expand Down
1 change: 0 additions & 1 deletion internal/exitcode/exitcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const (
Generic = 1
Usage = 2
Config = 3
NotFound = 4
Upstream = 5
)

Expand Down
6 changes: 3 additions & 3 deletions internal/pagetoken/pagetoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func Encode(scope string, offset int) string {
return base64.RawURLEncoding.EncodeToString([]byte("v1:" + scope + ":" + strconv.Itoa(offset)))
}

// Decode validates a token for one command scope and offset ceiling.
func Decode(scope, value string, maxOffset int) (int, error) {
// Decode validates a token for one command scope.
func Decode(scope, value string) (int, error) {
if value == "" {
return 0, nil
}
Expand All @@ -31,7 +31,7 @@ func Decode(scope, value string, maxOffset int) (int, error) {
return 0, errInvalid
}
offset, err := strconv.Atoi(strings.TrimPrefix(string(decoded), prefix))
if err != nil || offset < 0 || offset > maxOffset {
if err != nil || offset < 0 {
return 0, errInvalid
}
return offset, nil
Expand Down
7 changes: 3 additions & 4 deletions internal/pagetoken/pagetoken_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestRoundTripAndStableEncoding(t *testing.T) {
if token != base64.RawURLEncoding.EncodeToString([]byte("v1:"+scope+":50")) {
t.Fatalf("token=%q", token)
}
offset, err := Decode(scope, token, 1000)
offset, err := Decode(scope, token)
if err != nil || offset != 50 {
t.Fatalf("offset=%d error=%v", offset, err)
}
Expand All @@ -28,17 +28,16 @@ func TestDecodeRejectsInvalidTokens(t *testing.T) {
encoded("v1:album:1"),
encoded("v1:track:-1"),
encoded("v1:track:not-a-number"),
encoded("v1:track:1001"),
strings.Repeat("a", maxEncodedLength+1),
} {
if _, err := Decode("track", token, 1000); err == nil {
if _, err := Decode("track", token); err == nil {
t.Fatalf("token %q accepted", token)
}
}
}

func TestDecodeEmptyTokenStartsAtZero(t *testing.T) {
offset, err := Decode("track", "", 1000)
offset, err := Decode("track", "")
if err != nil || offset != 0 {
t.Fatalf("offset=%d error=%v", offset, err)
}
Expand Down
18 changes: 7 additions & 11 deletions internal/token/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,17 @@ func requireEOF(decoder *json.Decoder) error {
}

func normalizeScopes(input []string) ([]string, error) {
seen := make(map[string]struct{}, len(input))
for _, value := range input {
if len(input) == 0 {
return nil, errors.New("oauth token envelope scopes must not be empty")
}
result := make([]string, len(input))
for i, value := range input {
scope := strings.TrimSpace(value)
if scope == "" {
return nil, errors.New("oauth token envelope scopes must contain only non-empty strings")
}
seen[scope] = struct{}{}
}
if len(seen) == 0 {
return nil, errors.New("oauth token envelope scopes must not be empty")
}
result := make([]string, 0, len(seen))
for scope := range seen {
result = append(result, scope)
result[i] = scope
}
slices.Sort(result)
return result, nil
return slices.Compact(result), nil
}
Loading
Loading