Skip to content
Open
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
95 changes: 95 additions & 0 deletions app/spike/internal/cmd/format/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// \\ SPIKE: Secure your secrets with SPIFFE. — https://spike.ist/
// \\\\\ Copyright 2024-present SPIKE contributors.
// \\\\\\\ SPDX-License-Identifier: Apache-2.0

package format

import (
"fmt"

"github.com/spf13/cobra"
)

// OutputFormat represents the supported output formats.
type OutputFormat int

const (
// Human represents human-readable output format.
Human OutputFormat = iota
// JSON represents JSON output format.
JSON
// YAML represents YAML output format.
YAML
)

// String returns the canonical string representation of the format.
func (f OutputFormat) String() string {
switch f {
case Human:
return "human"
case JSON:
return "json"
case YAML:
return "yaml"
default:
return "unknown"
}
}

// AddFormatFlag adds a standardized format flag to the given command.
// The format flag supports the following options:
// - human, h, plain, p: Human-readable, friendly output (default)
// - json, j: Valid JSON output (for scripting/parsing)
// - yaml, y: Valid YAML output (for scripting/parsing)
//
// Parameters:
// - cmd: The Cobra command to add the flag to
func AddFormatFlag(cmd *cobra.Command) {
cmd.Flags().StringP("format", "f", "human",
"Output format: human/h/plain/p, json/j, or yaml/y")
}

// GetFormat retrieves and validates the format flag from the command.
// It supports multiple aliases for each format:
// - human, h, plain, p -> Human format
// - json, j -> JSON format
// - yaml, y -> YAML format
//
// Parameters:
// - cmd: The Cobra command containing the format flag
//
// Returns:
// - OutputFormat: The parsed output format
// - error: An error if the format is invalid
func GetFormat(cmd *cobra.Command) (OutputFormat, error) {
formatStr, _ := cmd.Flags().GetString("format")
return ParseFormat(formatStr)
}

// ParseFormat parses a format string into an OutputFormat.
// It supports multiple aliases for each format:
// - human, h, plain, p, "" (empty) -> Human format
// - json, j -> JSON format
// - yaml, y -> YAML format
//
// Parameters:
// - formatStr: The format string to parse
//
// Returns:
// - OutputFormat: The parsed output format
// - error: An error if the format is invalid
func ParseFormat(formatStr string) (OutputFormat, error) {
switch formatStr {
case "human", "h", "plain", "p", "":
return Human, nil
case "json", "j":
return JSON, nil
case "yaml", "y":
return YAML, nil
default:
return Human, fmt.Errorf(
"invalid format '%s'. Valid formats are: "+
"human/h/plain/p, json/j, yaml/y",
formatStr)
}
}
231 changes: 231 additions & 0 deletions app/spike/internal/cmd/format/format_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// \\ SPIKE: Secure your secrets with SPIFFE. — https://spike.ist/
// \\\\\ Copyright 2024-present SPIKE contributors.
// \\\\\\\ SPDX-License-Identifier: Apache-2.0

package format

import (
"testing"

"github.com/spf13/cobra"
)

func TestOutputFormat_String(t *testing.T) {
tests := []struct {
name string
format OutputFormat
want string
}{
{
name: "Human format",
format: Human,
want: "human",
},
{
name: "JSON format",
format: JSON,
want: "json",
},
{
name: "YAML format",
format: YAML,
want: "yaml",
},
{
name: "Unknown format",
format: OutputFormat(999),
want: "unknown",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.format.String(); got != tt.want {
t.Errorf("OutputFormat.String() = %v, want %v",
got, tt.want)
}
})
}
}

func TestParseFormat(t *testing.T) {
tests := []struct {
name string
formatStr string
want OutputFormat
wantErr bool
}{
// Human format aliases
{
name: "Format 'human'",
formatStr: "human",
want: Human,
wantErr: false,
},
{
name: "Format 'h'",
formatStr: "h",
want: Human,
wantErr: false,
},
{
name: "Format 'plain'",
formatStr: "plain",
want: Human,
wantErr: false,
},
{
name: "Format 'p'",
formatStr: "p",
want: Human,
wantErr: false,
},
// JSON format aliases
{
name: "Format 'json'",
formatStr: "json",
want: JSON,
wantErr: false,
},
{
name: "Format 'j'",
formatStr: "j",
want: JSON,
wantErr: false,
},
// YAML format aliases
{
name: "Format 'yaml'",
formatStr: "yaml",
want: YAML,
wantErr: false,
},
{
name: "Format 'y'",
formatStr: "y",
want: YAML,
wantErr: false,
},
{
name: "Empty format defaults to human",
formatStr: "",
want: Human,
wantErr: false,
},
// Invalid formats
{
name: "Invalid format",
formatStr: "invalid",
want: Human,
wantErr: true,
},
{
name: "Case sensitive - JSON uppercase",
formatStr: "JSON",
want: Human,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseFormat(tt.formatStr)
if (err != nil) != tt.wantErr {
t.Errorf("ParseFormat() error = %v, wantErr %v",
err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ParseFormat() = %v, want %v", got, tt.want)
}
})
}
}

func TestAddFormatFlag(t *testing.T) {
cmd := &cobra.Command{
Use: "test",
Short: "Test command",
}

AddFormatFlag(cmd)

// Test that the flag was added
flag := cmd.Flags().Lookup("format")
if flag == nil {
t.Fatal("AddFormatFlag() did not add format flag")
}

// Test default value
if flag.DefValue != "human" {
t.Errorf("AddFormatFlag() default = %v, want %v",
flag.DefValue, "human")
}

// Test shorthand
shortFlag := cmd.Flags().ShorthandLookup("f")
if shortFlag == nil {
t.Fatal("AddFormatFlag() did not add shorthand flag")
}
}

func TestGetFormat(t *testing.T) {
tests := []struct {
name string
flagValue string
want OutputFormat
wantErr bool
}{
{
name: "Get human format",
flagValue: "human",
want: Human,
wantErr: false,
},
{
name: "Get json format",
flagValue: "json",
want: JSON,
wantErr: false,
},
{
name: "Get yaml format",
flagValue: "yaml",
want: YAML,
wantErr: false,
},
{
name: "Get format with alias 'p'",
flagValue: "p",
want: Human,
wantErr: false,
},
{
name: "Get invalid format",
flagValue: "invalid",
want: Human,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := &cobra.Command{
Use: "test",
Short: "Test command",
}
AddFormatFlag(cmd)
_ = cmd.Flags().Set("format", tt.flagValue)

got, err := GetFormat(cmd)
if (err != nil) != tt.wantErr {
t.Errorf("GetFormat() error = %v, wantErr %v",
err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GetFormat() = %v, want %v", got, tt.want)
}
})
}
}
13 changes: 8 additions & 5 deletions app/spike/internal/cmd/policy/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@

package policy

import "github.com/spf13/cobra"
import (
"github.com/spf13/cobra"

// addFormatFlag adds a format flag to the given command to allow specifying
// the output format (human or JSON).
"github.com/spiffe/spike/app/spike/internal/cmd/format"
)

// addFormatFlag adds a standardized format flag to the given command.
// Supports human/h/plain/p, json/j, and yaml/y formats.
//
// Parameters:
// - cmd: The Cobra command to add the flag to
func addFormatFlag(cmd *cobra.Command) {
cmd.Flags().String("format", "human",
"Output format: 'human' or 'json'")
format.AddFormatFlag(cmd)
}

// addNameFlag adds a name flag to the given command to allow specifying
Expand Down
Loading
Loading