Skip to content

feat: create formatters to simplify the request output#20

Merged
eynopv merged 16 commits intomasterfrom
feat/formatters
Apr 6, 2025
Merged

feat: create formatters to simplify the request output#20
eynopv merged 16 commits intomasterfrom
feat/formatters

Conversation

@eynopv
Copy link
Owner

@eynopv eynopv commented Apr 6, 2025

Summary by CodeRabbit

  • New Features

    • Introduced enhanced terminal output formatting with support for both colorized and plain text displays.
    • Consolidated the printing functionality to present results in a more unified and streamlined manner.
    • Added a new colorization feature for terminal text output.
  • Bug Fixes

    • Removed detailed request and response printing options to simplify output management.
  • Refactor

    • Simplified how API responses are structured for more consistent and accessible output.
    • Updated the result handling to provide additional context and methods for accessing request and response data.
    • Introduced a new Formatter struct for improved formatting of various output types.
  • Chore

    • Upgraded the Go version and refreshed dependency management to ensure improved toolchain reliability.

@coderabbitai
Copy link

coderabbitai bot commented Apr 6, 2025

Walkthrough

This pull request consolidates and streamlines several aspects of the application’s behavior. In the command runner (cmd/run.go), multiple explicit print operations for HTTP request and response details have been removed in favor of a unified printer instance. The client’s result construction has been simplified by directly initializing a composite literal, and the expectation package (along with its tests) has been removed. Additionally, the Go module file has been updated for a newer Go version and dependencies. New printer functionalities have been added, including colorization support and a Formatter interface, while the Result struct has been restructured with new helper methods.

Changes

File(s) Change Summary
cmd/run.go Removed detailed printing blocks for request/response headers and bodies; replaced with a unified printing call using printer.NewPrinter and prntr.Print(result).
go.mod Upgraded Go version from 1.21.5 to 1.24.0, added new direct dependency golang.org/x/term v0.30.0, and new indirect dependency golang.org/x/sys v0.31.0.
pkg/client/client.go Updated the Do method to construct and return a result.Result using a composite literal, removing the intermediate NewResult call and related error handling.
pkg/expectation/* Removed expectation.go and expectation_test.go, deleting the Expectation struct and its tests.
pkg/printer/* Introduced colors.go (defines color constants and helper functions) and formatter.go (defines a Formatter interface with ColorFormatter and PlainFormatter implementations); updated printer.go with a consolidated Print method and a new formatter field; added tests in formatter_test.go and updated printer_test.go; removed constants.go containing legacy constants.
pkg/result/* Restructured the Result struct by removing fields (Path, Status, etc.) and adding new ones (Response, ResponseBody, RequestBody, Metadata); removed NewResult and introduced new methods (StatusLine, RequestLine, Json, Text); deleted result_test.go.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant Run as cmd/run.go
  participant NP as printer.NewPrinter
  participant P as Printer
  participant F as Formatter
  participant O as os.Stdout

  User->>Run: Execute command
  Run->>NP: Instantiate Printer with config
  NP->>P: Initialize Printer (set formatter via NewFormatter)
  Run->>P: Call Print(result)
  P->>F: Format request/response details
  P->>O: Write formatted output
Loading
sequenceDiagram
  participant C as Client
  participant R as Request
  participant RES as Result

  C->>R: Receive HTTP request
  C->>RES: Construct Result using composite literal<br/>(Response, RequestBody, Metadata)
  RES-->>C: Return Result
Loading

Possibly related PRs

  • V0.3.2 #6: The changes in the main PR, specifically in cmd/run.go, involve the removal of detailed printing logic for request and response details, while the retrieved PR modifies cmd/run.go to enhance the runRequest function with detailed conditional logging for those same request and response details, indicating a direct relationship in their modifications to the same function.

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b40b64 and 52a4e6b.

📒 Files selected for processing (4)
  • pkg/printer/formatter.go (1 hunks)
  • pkg/printer/formatter_test.go (1 hunks)
  • pkg/printer/printer.go (2 hunks)
  • pkg/printer/printer_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/printer/printer.go
  • pkg/printer/formatter.go
🧰 Additional context used
🧬 Code Definitions (2)
pkg/printer/printer_test.go (3)
pkg/printer/printer.go (4)
  • IsTerminal (14-14)
  • NewPrinter (29-39)
  • PrinterConfig (16-21)
  • Printer (23-27)
pkg/result/result.go (2)
  • Result (9-14)
  • Metadata (16-18)
pkg/printer/formatter.go (1)
  • Formatter (14-16)
pkg/printer/formatter_test.go (3)
pkg/result/result.go (2)
  • StatusLine (20-24)
  • RequestLine (26-30)
pkg/printer/colors.go (8)
  • Colorize (15-17)
  • ColorGreen (8-8)
  • ColorReset (6-6)
  • ColorCyan (12-12)
  • ColorYellow (9-9)
  • ColorRed (7-7)
  • ColorMagenta (11-11)
  • Cyan (39-41)
pkg/printer/formatter.go (1)
  • Formatter (14-16)
🔇 Additional comments (6)
pkg/printer/formatter_test.go (4)

13-84: Great implementation of table-driven tests for status line formatting!

The test covers various HTTP status codes and response times comprehensively. I appreciate how you've included both colorized and non-colorized cases, as well as different status codes (200, 301, 500) and response times (fast, medium, slow).


86-173: Well-structured tests for request line formatting.

The test cases provide good coverage of different HTTP methods (GET, POST, DELETE, OPTIONS) with both colorized and non-colorized output. The use of a common test URL helps maintain consistency across test cases.


175-220: Good coverage of JSON formatting edge cases.

Your test cases cover simple JSON, nested structures, empty objects, and especially the unmarshalable value (channel) case, which is an excellent edge case to test. This ensures robust error handling in the formatter.


252-259: Correctly implemented test for alphabetically sorted headers.

The test expectation is correct since the formatHeaders method sorts headers alphabetically, as confirmed in previous discussions. "Content-Type" comes before "X-Custom" in alphabetical order, so the expected output matches the implementation.

pkg/printer/printer_test.go (2)

15-37: Good approach to testing terminal detection logic.

I like how you've mocked the IsTerminal function to test both terminal and non-terminal scenarios. The test properly restores the original function using defer, which is a clean approach to handling test setup and teardown.


39-113: Comprehensive test coverage for the Print method.

The test effectively verifies different printer configurations by:

  1. Using a buffer to capture output (good testing practice)
  2. Testing various configuration combinations (response-only, request-only, all sections)
  3. Verifying that expected strings appear in the output

This ensures the printer behaves correctly in all scenarios.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (19)
pkg/result/result.go (3)

28-34: Potential duplication between status and request lines.
Consider pulling out common fields into a shared type if these line representations continue to grow in features. This can promote DRY principles.


36-45: Lack of error logging on JSON unmarshal.
When JSON decoding fails, the code currently returns nil without surfacing the issue. Consider logging or otherwise indicating invalid request body content.

 if err := json.Unmarshal(body, &data); err != nil {
+    // Log the error or handle it more gracefully
     return nil
 }

52-63: Silent JSON parse failures for response.
As with RequestJson, returning nil on parse failure can mask issues. Consider a small logging message or error callback to diagnose unexpected response formats.

 if err := json.Unmarshal(r.ResponseBody, &data); err != nil {
+    // Possibly log the error or propagate it
     return nil
 }
pkg/printer/printer.go (2)

26-27: Use struct fields or constructor arguments in place of global vars.
destination and isTerminal are currently global. If you want multiple printer instances or advanced usage, consider parameterizing these values.

-var destination io.Writer = os.Stdout
-var isTerminal = term.IsTerminal(int(os.Stdout.Fd()))
+func NewPrinter(config PrinterConfig, out io.Writer, isTerm bool) Printer {
+    formatter := NewFormatter(isTerm)
+    return Printer{
+        config:    config,
+        formatter: formatter,
+        destination: out,
+    }
+}

38-79: Single method for printing request/response details.
Consolidating headers and body printing into Print drastically reduces duplicated logic. For further maintainability, consider factoring out smaller methods for each section to keep Print more readable.

 func (p *Printer) Print(res *result.Result) {
     sections := []string{}

+    // Example: separate method for request header printing:
+    if p.config.PrintRequestHeaders {
+        sections = append(sections, p.printReqHeaders(res))
+    }
+
     fmt.Fprint(destination, strings.Join(sections, "\n"))
 }
pkg/printer/formatter_test.go (3)

77-90: Fix cuddling assignments issue according to golangci-lint

According to the static analysis tool, there's a style issue related to code structure. Assignments should be properly separated from if statements.

 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			var formatter interface {
 				StatusLine(result.StatusLine) string
 			}
+
 			if tt.useColor {
 				formatter = ColorFormatter{}
 			} else {
 				formatter = PlainFormatter{}
 			}
+
 			got := formatter.StatusLine(tt.line)
 			assert.Equal(t, tt.want, got)
 		})
🧰 Tools
🪛 GitHub Check: golangci

[failure] 87-87:
assignments should only be cuddled with other assignments (wsl)


[failure] 82-82:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)

87-87: assignments should only be cuddled with other assignments

(wsl)


82-82: if statements should only be cuddled with assignments

(wsl)

🪛 GitHub Actions: .github/workflows/ci.yml

[error] 82-82: if statements should only be cuddled with assignments (wsl)


173-186: Fix cuddling assignments issue according to golangci-lint

Similar to the previous issue, the assignments should be properly separated from if statements.

 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			var formatter interface {
 				RequestLine(result.RequestLine) string
 			}
+
 			if tt.useColor {
 				formatter = ColorFormatter{}
 			} else {
 				formatter = PlainFormatter{}
 			}
+
 			got := formatter.RequestLine(tt.line)
 			assert.Equal(t, tt.want, got)
 		})
🧰 Tools
🪛 GitHub Check: golangci

[failure] 183-183:
assignments should only be cuddled with other assignments (wsl)


[failure] 178-178:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)

178-178: if statements should only be cuddled with assignments

(wsl)


183-183: assignments should only be cuddled with other assignments

(wsl)


276-289: Fix cuddling assignments issue according to golangci-lint

Another instance of the same style issue. The assignments should be properly separated from if statements.

 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			var formatter interface {
 				Headers(http.Header) string
 			}
+
 			if tt.useColor {
 				formatter = ColorFormatter{}
 			} else {
 				formatter = PlainFormatter{}
 			}
+
 			got := formatter.Headers(tt.headers)
 			assert.Equal(t, tt.want, got)
 		})
🧰 Tools
🪛 GitHub Check: golangci

[failure] 286-286:
assignments should only be cuddled with other assignments (wsl)


[failure] 281-281:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)

281-281: if statements should only be cuddled with assignments

(wsl)


286-286: assignments should only be cuddled with other assignments

(wsl)

pkg/printer/colors.go (4)

1-6: Good package and type definition!

The Color type alias provides clear semantic meaning. Consider adding a documentation comment explaining the purpose of this type to improve code clarity.

+// Color represents an ANSI terminal color code as a string
 type Color = string

7-15: Add documentation for color constants

The color constants are well-defined and follow a consistent naming pattern. Consider adding a comment block to explain these color codes and their purpose.

+// ANSI color codes for terminal output
 const (
 	ColorReset   Color = "\033[0m"
 	ColorRed     Color = "\033[31m"
 	ColorGreen   Color = "\033[32m"
 	ColorYellow  Color = "\033[33m"
 	ColorBlue    Color = "\033[34m"
 	ColorMagenta Color = "\033[35m"
 	ColorCyan    Color = "\033[36m"
 )

17-19: Good implementation of the Colorize function

The function properly handles wrapping a string with color codes. However, for better efficiency, consider using string concatenation instead of fmt.Sprintf.

 func Colorize(s string, c Color) string {
-	return fmt.Sprintf("%v%s%v", c, s, ColorReset)
+	return c + s + ColorReset
 }

21-43: Refactor color functions to use Colorize

The color-specific functions use direct string concatenation while the Colorize function uses fmt.Sprintf. For better consistency and to reduce code duplication, consider refactoring these functions to leverage the Colorize function.

 func Red(s string) string {
-	return ColorRed + s + ColorReset
+	return Colorize(s, ColorRed)
 }

 func Green(s string) string {
-	return ColorGreen + s + ColorReset
+	return Colorize(s, ColorGreen)
 }

 func Yellow(s string) string {
-	return ColorYellow + s + ColorReset
+	return Colorize(s, ColorYellow)
 }

 func Blue(s string) string {
-	return ColorBlue + s + ColorReset
+	return Colorize(s, ColorBlue)
 }

 func Magenta(s string) string {
-	return ColorMagenta + s + ColorReset
+	return Colorize(s, ColorMagenta)
 }

 func Cyan(s string) string {
-	return ColorCyan + s + ColorReset
+	return Colorize(s, ColorCyan)
 }
pkg/printer/formatter.go (7)

13-18: Add documentation to the Formatter interface

The interface is well-defined, but would benefit from documentation comments explaining the purpose of each method.

+// Formatter provides methods to format different components of HTTP requests and responses
 type Formatter interface {
+	// StatusLine formats the status line of an HTTP response
 	StatusLine(statusLine result.StatusLine) string
+	// RequestLine formats the request line of an HTTP request
 	RequestLine(requestLine result.RequestLine) string
+	// Headers formats HTTP headers
 	Headers(headers http.Header) string
+	// Json formats a JSON object
 	Json(map[string]interface{}) string
 }

20-26: Add documentation to the NewFormatter function

Consider adding a documentation comment explaining the purpose of the function and its parameter.

+// NewFormatter creates a new formatter based on terminal capabilities
+// Returns ColorFormatter for terminal output, PlainFormatter otherwise
 func NewFormatter(isTerminal bool) Formatter {
 	if isTerminal {
 		return ColorFormatter{}
 	}

 	return PlainFormatter{}
 }

28-29: Add documentation to formatter types

Consider adding documentation comments explaining the purpose of each formatter type.

+// ColorFormatter provides colored output formatting for terminal display
 type ColorFormatter struct{}
+// PlainFormatter provides uncolored output formatting for non-terminal display
 type PlainFormatter struct{}

63-78: Use strings.Builder for more efficient header formatting

The current implementation uses += to build the string, which creates a new string allocation for each header. Using strings.Builder would be more efficient, especially for requests with many headers.

 func formatHeaders(headers http.Header, colorized bool) string {
-	s := ""
+	var sb strings.Builder
 
 	fstring := "%s: %s\n"
 
 	for key, value := range headers {
 		vs := strings.Join(value, ", ")
 		if colorized {
-			s += fmt.Sprintf(fstring, Cyan(key), vs)
+			fmt.Fprintf(&sb, fstring, Cyan(key), vs)
 		} else {
-			s += fmt.Sprintf(fstring, key, vs)
+			fmt.Fprintf(&sb, fstring, key, vs)
 		}
 	}
 
-	return s
+	return sb.String()
 }

80-113: Improve status code detection and document color logic

Instead of using string prefix checks, consider parsing the status code for clearer category checking. Also, document the color scheme to improve maintainability.

 func formatStatusLine(line result.StatusLine, colorized bool) string {
 	fstring := "%v %v [%v]\n"
 
 	if !colorized {
 		return fmt.Sprintf(fstring, line.Protocol, line.Status, line.Time)
 	}
 
 	var timeColor, statusColor Color
 
+	// Color status codes by category
+	// 1xx, 2xx: Green
+	// 3xx: Cyan
+	// 4xx, 5xx: Red
 	switch {
 	case strings.HasPrefix(line.Status, "2"), strings.HasPrefix(line.Status, "1"):
 		statusColor = ColorGreen
 	case strings.HasPrefix(line.Status, "3"):
 		statusColor = ColorCyan
 	default:
 		statusColor = ColorRed
 	}
 
+	// Color response time based on duration
+	// < 500ms: Default
+	// 500ms-1s: Yellow
+	// > 1s: Red
 	switch {
 	case line.Time < 500*time.Millisecond:
 		timeColor = ColorReset
 	case line.Time < 1000*time.Millisecond:
 		timeColor = ColorYellow
 	default:
 		timeColor = ColorRed
 	}

115-136: Document HTTP method color logic and handle case insensitivity

Add comments explaining the color scheme for HTTP methods and consider making the method matching case-insensitive for better robustness.

 func formatRequestLine(line result.RequestLine, colorized bool) string {
 	fstring := "%v %v %v\n"
 
 	if !colorized {
 		return fmt.Sprintf(fstring, line.Method, line.Url, line.Protocol)
 	}
 
 	var methodColor Color
 
+	// Color HTTP methods based on their semantics
+	// GET, HEAD: Green (safe methods)
+	// POST, PUT, PATCH: Yellow (resource creation/modification)
+	// DELETE: Red (resource removal)
+	// Others: Magenta
 	switch line.Method {
-	case "GET", "HEAD":
+	case "GET", "HEAD", "get", "head":
 		methodColor = ColorGreen
-	case "POST", "PUT", "PATCH":
+	case "POST", "PUT", "PATCH", "post", "put", "patch":
 		methodColor = ColorYellow
-	case "DELETE":
+	case "DELETE", "delete":
 		methodColor = ColorRed
 	default:
 		methodColor = ColorMagenta
 	}

Alternatively, use strings.ToUpper() for a more standardized approach:

 func formatRequestLine(line result.RequestLine, colorized bool) string {
 	fstring := "%v %v %v\n"
 
 	if !colorized {
 		return fmt.Sprintf(fstring, line.Method, line.Url, line.Protocol)
 	}
 
 	var methodColor Color
+	method := strings.ToUpper(line.Method)
 
 	switch method {
 	case "GET", "HEAD":
 		methodColor = ColorGreen
 	case "POST", "PUT", "PATCH":
 		methodColor = ColorYellow
 	case "DELETE":
 		methodColor = ColorRed
 	default:
 		methodColor = ColorMagenta
 	}
 
 	return fmt.Sprintf(fstring, Colorize(line.Method, methodColor), line.Url, line.Protocol)
 }

138-144: Improve JSON formatting error handling

The current implementation returns a generic error message when JSON parsing fails. Consider providing more detailed error information.

 func formatJson(j map[string]interface{}) string {
 	if prettyJson, err := json.MarshalIndent(j, "", " "); err != nil {
-		return "unable to parse json"
+		return fmt.Sprintf("unable to parse json: %v", err)
 	} else {
 		return string(prettyJson)
 	}
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1fbffa and 25f92b1.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (13)
  • cmd/run.go (1 hunks)
  • go.mod (1 hunks)
  • pkg/client/client.go (1 hunks)
  • pkg/expectation/expectation.go (0 hunks)
  • pkg/expectation/expectation_test.go (0 hunks)
  • pkg/printer/colors.go (1 hunks)
  • pkg/printer/constants.go (0 hunks)
  • pkg/printer/formatter.go (1 hunks)
  • pkg/printer/formatter_test.go (1 hunks)
  • pkg/printer/printer.go (2 hunks)
  • pkg/printer/printer_test.go (1 hunks)
  • pkg/result/result.go (1 hunks)
  • pkg/result/result_test.go (0 hunks)
💤 Files with no reviewable changes (4)
  • pkg/printer/constants.go
  • pkg/expectation/expectation_test.go
  • pkg/expectation/expectation.go
  • pkg/result/result_test.go
🧰 Additional context used
🧬 Code Definitions (7)
pkg/printer/printer.go (2)
pkg/printer/formatter.go (2)
  • Formatter (13-18)
  • NewFormatter (20-26)
pkg/result/result.go (3)
  • Result (9-14)
  • RequestLine (75-79)
  • StatusLine (69-73)
pkg/client/client.go (1)
pkg/result/result.go (2)
  • Result (9-14)
  • Metadata (16-18)
pkg/printer/formatter_test.go (3)
pkg/result/result.go (2)
  • StatusLine (69-73)
  • RequestLine (75-79)
pkg/printer/colors.go (8)
  • Colorize (17-19)
  • ColorGreen (10-10)
  • ColorReset (8-8)
  • ColorCyan (14-14)
  • ColorYellow (11-11)
  • ColorRed (9-9)
  • ColorMagenta (13-13)
  • Cyan (41-43)
pkg/printer/formatter.go (2)
  • ColorFormatter (28-28)
  • PlainFormatter (29-29)
cmd/run.go (1)
pkg/printer/printer.go (2)
  • NewPrinter (29-36)
  • PrinterConfig (14-19)
pkg/printer/formatter.go (2)
pkg/result/result.go (2)
  • StatusLine (69-73)
  • RequestLine (75-79)
pkg/printer/colors.go (9)
  • Cyan (41-43)
  • Color (5-5)
  • ColorGreen (10-10)
  • ColorCyan (14-14)
  • ColorRed (9-9)
  • ColorReset (8-8)
  • ColorYellow (11-11)
  • Colorize (17-19)
  • ColorMagenta (13-13)
pkg/printer/printer_test.go (3)
pkg/printer/printer.go (2)
  • NewPrinter (29-36)
  • PrinterConfig (14-19)
pkg/printer/formatter.go (2)
  • ColorFormatter (28-28)
  • PlainFormatter (29-29)
pkg/result/result.go (2)
  • Result (9-14)
  • Metadata (16-18)
pkg/result/result.go (1)
pkg/request/request.go (1)
  • Request (21-26)
🪛 GitHub Check: golangci
pkg/printer/formatter_test.go

[failure] 286-286:
assignments should only be cuddled with other assignments (wsl)


[failure] 281-281:
if statements should only be cuddled with assignments (wsl)


[failure] 183-183:
assignments should only be cuddled with other assignments (wsl)


[failure] 178-178:
if statements should only be cuddled with assignments (wsl)


[failure] 87-87:
assignments should only be cuddled with other assignments (wsl)


[failure] 82-82:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)
pkg/printer/formatter_test.go

87-87: assignments should only be cuddled with other assignments

(wsl)


178-178: if statements should only be cuddled with assignments

(wsl)


183-183: assignments should only be cuddled with other assignments

(wsl)


281-281: if statements should only be cuddled with assignments

(wsl)


286-286: assignments should only be cuddled with other assignments

(wsl)


82-82: if statements should only be cuddled with assignments

(wsl)

🪛 GitHub Actions: .github/workflows/ci.yml
pkg/printer/formatter_test.go

[error] 82-82: if statements should only be cuddled with assignments (wsl)

🔇 Additional comments (18)
pkg/result/result.go (5)

10-14: Consider potential memory usage and data consistency.
Storing the entire *http.Response in this struct can lead to increased memory usage or possible data inconsistencies if the response body remains open or un-consumed. Please ensure the response body is properly closed elsewhere in the codebase to avoid resource leaks.

Would you like me to generate a script to locate any usage patterns that leave the response body open or unhandled?


20-26: Pointer return pattern considered valid.
Returning a pointer to a transient StatusLine struct is fine, but ensure that all consumers of this method consistently handle the struct’s lifetime.


47-50: Straightforward text retrieval.
The RequestText() logic is concise, returning the raw request body as a string with no concerns raised.


65-67: Simple text output for response body.
The Text() method is a direct conversion from bytes to string, which is perfectly acceptable.


69-79: StatusLine and RequestLine structs look consistent.
These struct definitions align well with the rest of the changes. Having distinct types for request and status lines helps maintain clarity in formatting.

go.mod (2)

13-13: Explicit direct dependency on cobra.
Listing github.com/spf13/cobra v1.8.0 as a direct requirement clarifies usage and improves overall dependency tracking.


3-5:

Details

✅ Verification successful

Confirm new Go versions in the pipeline.
Switching to go 1.23.0 and specifying toolchain go1.24.0 can be beneficial. Ensure your CI/CD environment supports these versions without conflicts.


🏁 Script executed:

#!/bin/bash
# Check the installed Go version in the current environment
go version

Length of output: 44


CI/CD Go Version Verified
The pipeline is currently running Go version 1.24.1, which is compatible with your declared toolchain of go1.24.0—minor patch differences aside. The module still specifies go 1.23.0, which may be intentional if you're gradually upgrading or targeting specific language features. If consistency across directives is desired, consider updating either the go directive or the toolchain version to match the installed Go release. Otherwise, the configuration appears supported by your CI/CD environment.

pkg/printer/printer.go (3)

5-11: Imports reorganized for advanced I/O and terminal detection.
Adding io, os, golang.org/x/term, and referencing the result package directly are consistent with the new single-point printing approach.


22-23: Configuration and formatter separation.
Defining PrinterConfig and Formatter as standalone fields fosters clear separation of concerns, simplifying future enhancements to each.


30-36: Dynamic formatter initialization.
Creating a formatter based on isTerminal is a clean way to transparently switch between color and plain output.

pkg/printer/formatter_test.go (4)

13-91: Test_formatStatusLine is well structured and comprehensive.

The test covers various scenarios including non-colorized and colorized status lines with different response times and status codes. The test is well structured using a table-driven approach.

🧰 Tools
🪛 GitHub Check: golangci

[failure] 87-87:
assignments should only be cuddled with other assignments (wsl)


[failure] 82-82:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)

87-87: assignments should only be cuddled with other assignments

(wsl)


82-82: if statements should only be cuddled with assignments

(wsl)

🪛 GitHub Actions: .github/workflows/ci.yml

[error] 82-82: if statements should only be cuddled with assignments (wsl)


93-187: Test_formatRequestLine is well structured and validates different HTTP methods.

The test properly verifies both colorized and non-colorized representation of various HTTP methods (GET, POST, DELETE, OPTIONS) with appropriate colors.

🧰 Tools
🪛 GitHub Check: golangci

[failure] 183-183:
assignments should only be cuddled with other assignments (wsl)


[failure] 178-178:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)

178-178: if statements should only be cuddled with assignments

(wsl)


183-183: assignments should only be cuddled with other assignments

(wsl)


189-234: Test_formatJson includes good error handling for unmarshalable values.

The test correctly handles various JSON serialization scenarios including simple objects, nested objects, empty objects, and unmarshalable values (channel). The error case is particularly valuable for robust testing.


236-290: Test_formatHeaders effectively tests different header scenarios.

The test covers various scenarios including no headers, single header (colorized and non-colorized), and multiple headers. This comprehensive approach ensures the formatter handles all expected cases.

🧰 Tools
🪛 GitHub Check: golangci

[failure] 286-286:
assignments should only be cuddled with other assignments (wsl)


[failure] 281-281:
if statements should only be cuddled with assignments (wsl)

🪛 golangci-lint (1.64.8)

281-281: if statements should only be cuddled with assignments

(wsl)


286-286: assignments should only be cuddled with other assignments

(wsl)

pkg/client/client.go (1)

60-67: Simplified result construction with direct struct initialization.

The code has been refactored to use a direct struct initialization approach, which is cleaner and more idiomatic Go. The previous approach likely used a helper function with error handling that has been determined to be unnecessary.

cmd/run.go (1)

65-66: Simplified output printing with unified printer instance.

The code has been greatly simplified by replacing multiple explicit print operations with a unified printer instance. This approach is more maintainable and follows the Single Responsibility Principle by delegating the printing logic to a dedicated component.

pkg/printer/printer_test.go (2)

14-47: Well-structured test for NewPrinter with clear expectations.

This test properly validates that the correct formatter type is selected based on terminal status. The test setup also correctly preserves and restores global state using defer, which is important for test isolation.


49-125: Comprehensive Print method test with robust setup and validation.

The test for the Print method is well structured:

  1. It properly captures output using a buffer
  2. It correctly restores global state using defer
  3. It tests multiple configurations with clear expectations
  4. It verifies output content in a flexible way using string contains checks

This is an effective approach for testing complex output formatting functions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25f92b1 and 37efd77.

📒 Files selected for processing (1)
  • pkg/printer/formatter_test.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
pkg/printer/formatter_test.go (3)
pkg/result/result.go (2)
  • StatusLine (69-73)
  • RequestLine (75-79)
pkg/printer/colors.go (8)
  • Colorize (17-19)
  • ColorGreen (10-10)
  • ColorReset (8-8)
  • ColorCyan (14-14)
  • ColorYellow (11-11)
  • ColorRed (9-9)
  • ColorMagenta (13-13)
  • Cyan (41-43)
pkg/printer/formatter.go (2)
  • ColorFormatter (28-28)
  • PlainFormatter (29-29)
🪛 GitHub Actions: .github/workflows/ci.yml
pkg/printer/formatter_test.go

[error] 293-293: Test failed: expected equal; got: Content-Type: application/json
X-Custom: value1, value2
; want: X-Custom: value1, value2
Content-Type: application/json

🔇 Additional comments (3)
pkg/printer/formatter_test.go (3)

13-93: Well-structured test for HTTP status line formatting!

The test covers various status codes and response times, verifying both colorized and non-colorized outputs. This table-driven approach is clean and comprehensive.


95-191: Great test coverage for HTTP request line formatting

This test effectively covers different HTTP methods with appropriate color coding for each method type. The testing structure is consistent with the previous test.


193-238: JSON formatting test looks good

The test covers essential JSON formatting scenarios including simple, nested, empty objects, and error handling for unmarshalable values.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
pkg/printer/formatter.go (5)

21-27: Consider making the formatter option more descriptive than a boolean.

Relying on a single boolean (isTerminal) might be less expressive than using a dedicated type or enum, e.g. FormatterTypeColor vs. FormatterTypePlain. Such an approach makes the code more self-documenting and open for future extensibility (e.g., more than two formatters).


64-86: Optimize repeated string concatenation.

Building large strings in a loop can benefit from using a strings.Builder. This can reduce allocations and improve performance, especially for responses with numerous headers.

 func formatHeaders(headers http.Header, colorized bool) string {
-    s := ""
     fstring := "%s: %s\n"

     keys := make([]string, 0, len(headers))
     for k := range headers {
         keys = append(keys, k)
     }
     sort.Strings(keys)

-    for _, key := range keys {
-        value := strings.Join(headers[key], ", ")
-        if colorized {
-            s += fmt.Sprintf(fstring, Cyan(key), value)
-        } else {
-            s += fmt.Sprintf(fstring, key, value)
-        }
-    }
-    return s
+    var sb strings.Builder
+    for _, key := range keys {
+        value := strings.Join(headers[key], ", ")
+        if colorized {
+            sb.WriteString(fmt.Sprintf(fstring, Cyan(key), value))
+        } else {
+            sb.WriteString(fmt.Sprintf(fstring, key, value))
+        }
+    }
+    return sb.String()
 }

88-121: Evaluate status parsing to handle numeric HTTP codes more precisely.

Currently, we’re coloring based on string prefixes (e.g., "2" for 2xx codes, "3" for 3xx). If the status field might include unconventional patterns, consider parsing the numeric status code for more robust handling.


123-144: Handle case-insensitive HTTP methods.

If HTTP methods can come in various cases (e.g., “get” instead of “GET”), convert them to uppercase or a canonical form before applying color logic. This ensures consistent colorization for all valid HTTP methods.

 func formatRequestLine(line result.RequestLine, colorized bool) string {
     ...
-    switch line.Method {
+    method := strings.ToUpper(line.Method)
+    switch method {
         ...
     }
     ...
 }

146-152: Enhance error handling for unparseable JSON.

Currently, an error results in a generic “unable to parse json” message. Consider returning the error details or a structured error to help debug JSON issues without losing important context.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37efd77 and c121d68.

📒 Files selected for processing (1)
  • pkg/printer/formatter.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
pkg/printer/formatter.go (2)
pkg/result/result.go (2)
  • StatusLine (69-73)
  • RequestLine (75-79)
pkg/printer/colors.go (9)
  • Cyan (41-43)
  • Color (5-5)
  • ColorGreen (10-10)
  • ColorCyan (14-14)
  • ColorRed (9-9)
  • ColorReset (8-8)
  • ColorYellow (11-11)
  • Colorize (17-19)
  • ColorMagenta (13-13)
🔇 Additional comments (2)
pkg/printer/formatter.go (2)

14-19: Well-structured interface definition.

This interface elegantly captures all the required methods for HTTP output formatting (status lines, request lines, headers, and JSON), promoting consistency across different implementations.


32-38: Clear separation of color vs. plain header formatting.

Both implementations correctly delegate to formatHeaders, ensuring minimal code duplication. This is clean and consistent.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (10)
pkg/result/result.go (3)

34-39: Validate Response before accessing its fields.
r.Response.Proto and r.Response.Status are called without checks, which can panic if r.Response is nil. Consider defensive coding to avoid unexpected runtime errors.

func (r Result) StatusLine() *StatusLine {
-  return &StatusLine{
-    Protocol: r.Response.Proto,
-    Status:   r.Response.Status,
-    Time:     r.Metadata.ElapsedTime,
-  }
+  if r.Response == nil {
+    return &StatusLine{
+      Protocol: "",
+      Status:   "",
+      Time:     r.Metadata.ElapsedTime,
+    }
+  }
+  return &StatusLine{
+    Protocol: r.Response.Proto,
+    Status:   r.Response.Status,
+    Time:     r.Metadata.ElapsedTime,
+  }
}

42-48: Check for a nil request in RequestLine().
r.Response.Request may be nil if the response was not formed by a standard HTTP client or if an error occurred. Consider verifying that r.Response.Request is non-nil to prevent panics.

func (r Result) RequestLine() *RequestLine {
-  return &RequestLine{
-    Protocol: r.Response.Request.Proto,
-    Url:      r.Response.Request.URL.String(),
-    Method:   r.Response.Request.Method,
-  }
+  if r.Response == nil || r.Response.Request == nil {
+    return &RequestLine{}
+  }
+  return &RequestLine{
+    Protocol: r.Response.Request.Proto,
+    Url:      r.Response.Request.URL.String(),
+    Method:   r.Response.Request.Method,
+  }
}

50-61: Surface JSON unmarshal errors.
Currently, the function returns nil on any unmarshal error, offering no feedback. Consider returning the error or storing it in the Result for better observability and troubleshooting.

func (b Body) Json() map[string]any {
  if len(b) == 0 {
    return nil
  }

  var data map[string]any
  if err := json.Unmarshal(b, &data); err != nil {
-    return nil
+    // Return an empty map or attach error details in some way
+    return map[string]any{
+      "unmarshal_error": err.Error(),
+    }
  }
  return data
}
pkg/printer/printer.go (4)

26-27: Avoid using package-level globals for destination and isTerminal.
Storing these as global variables may affect testability and reusability. Consider using a struct field or constructor injection for improved isolation.

-var destination io.Writer = os.Stdout
-var isTerminal = term.IsTerminal(int(os.Stdout.Fd()))
+func NewPrinter(config PrinterConfig, dest io.Writer, checkTerminal bool) Printer {
+    ...
+}

60-67: Prevent copying Request by value.
req := *res.Response.Request copies the entire request struct. If the request is large or changes, it can be inefficient or misleading. Consider referencing the request struct directly instead of copying.

-func (p *Printer) printRequestHeaders(res *result.Result) string {
-    req := *res.Response.Request
+func (p *Printer) printRequestHeaders(res *result.Result) string {
+    req := res.Response.Request
     s := ""
-    s += p.formatter.RequestLine(*res.RequestLine())
-    s += p.formatter.Headers(req.Header)
+    s += p.formatter.RequestLine(*res.RequestLine()) // if safe
+    s += p.formatter.Headers(req.Header)
    return s
}

69-75: Consistent style in printResponseHeaders.
The logic for printing needs to ensure consistent spacing, newlines, and optional color formatting. Consider standardizing with printRequestHeaders or reusing a helper function.


77-87: Consider multi-format support for large bodies.
For bigger responses, printing everything at once might degrade performance. Think about streaming or chunking.

pkg/printer/formatter.go (3)

32-38: Avoid confusion with color-coded headers.
Currently, the code color-codes header keys but not values. This is acceptable, but you might color-code both keys and values for increased readability.


56-62: Consider colorizing JSON keys.
In the color formatter, Json() currently calls formatJson without color. If desired, you can parse, colorize keys and strings.


146-153: Return better error messages for JSON failures.
When pretty-printing JSON fails, it currently returns a generic string. Consider returning original JSON or error data for easier debugging.

func formatJson(j map[string]any) string {
    if prettyJson, err := json.MarshalIndent(j, "", " "); err != nil {
-       return "unable to parse json"
+       return fmt.Sprintf("unable to parse json: %v", err)
    } else {
        return string(prettyJson)
    }
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c121d68 and d0fa8f2.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • go.mod (1 hunks)
  • pkg/printer/colors.go (1 hunks)
  • pkg/printer/formatter.go (1 hunks)
  • pkg/printer/printer.go (2 hunks)
  • pkg/result/result.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • go.mod
  • pkg/printer/colors.go
🧰 Additional context used
🧬 Code Definitions (2)
pkg/result/result.go (1)
pkg/request/request.go (1)
  • Request (21-26)
pkg/printer/formatter.go (2)
pkg/result/result.go (2)
  • StatusLine (20-24)
  • RequestLine (26-30)
pkg/printer/colors.go (9)
  • Cyan (39-41)
  • Color (3-3)
  • ColorGreen (8-8)
  • ColorCyan (12-12)
  • ColorRed (7-7)
  • ColorReset (6-6)
  • ColorYellow (9-9)
  • Colorize (15-17)
  • ColorMagenta (11-11)
🔇 Additional comments (8)
pkg/result/result.go (2)

10-14: Guard against potential nil Response field.
If r.Response is ever nil, dereferencing it at lines 10-14 may cause a runtime panic. Ensure that callers always populate r.Response or add a nil check.

Would you like to explore the codebase to confirm Result.Response is never nil? I can generate a script to locate all Result instantiations and verify that Response is always assigned.


63-65: Looks good!
The Text() method is straightforward and appears correct, converting the raw bytes to a string.

pkg/printer/printer.go (1)

22-23: Ensure thread-safety if concurrent prints can occur.
Printer stores config and a formatter. If multiple goroutines print simultaneously, concurrency issues may arise. Confirm that this code won't be called from concurrent contexts without synchronization.

Would you like me to inspect usage sites of Printer to confirm single-threaded usage?

pkg/printer/formatter.go (5)

40-46: Minimal overhead in StatusLine().
This method is concise and effectively delegates to formatStatusLine. No issues found, well done!


48-54: Well-structured RequestLine() method.
Delegation to formatRequestLine is clean and maintains single-responsibility. No concerns.


64-86: Header sorting is correct.
Sorting header keys alphabetically is good for consistency, ensuring repeated runs produce stable output.


88-122: Status line color logic is intuitive.
The status code prefix-based color scheme is straightforward, and the time-based coloring is also clear. Good job on readability.


123-145: Method-based color coding is well-chosen.
Each HTTP method is assigned a distinctive color that stands out appropriately.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
pkg/printer/printer.go (4)

26-27: Consider making the global variables configurable per instance

Using global variables like destination and isTerminal can make testing difficult and introduce unexpected side effects. Consider making these configurable per Printer instance or using dependency injection to improve testability and maintainability.

type Printer struct {
	config    PrinterConfig
	formatter Formatter
+	destination io.Writer
+	isTerminal bool
}

-var destination io.Writer = os.Stdout
-var isTerminal = term.IsTerminal(int(os.Stdout.Fd()))

func NewPrinter(config PrinterConfig) Printer {
+	destination := os.Stdout
+	isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
	formatter := NewFormatter(isTerminal)

	return Printer{
		config:    config,
		formatter: formatter,
+		destination: destination,
+		isTerminal: isTerminal,
	}
}

65-72: Consider using a string builder for better performance

Using string concatenation (s += ...) can be inefficient as it creates new string objects with each operation. Consider using strings.Builder for better performance, especially when dealing with larger requests.

func (p *Printer) printRequestHeaders(res *result.Result) string {
	if res.Response.Request == nil {
		return "No HTTP request available\n"
	}
	req := *res.Response.Request
-	s := ""
+	var b strings.Builder
-	s += p.formatter.RequestLine(*res.RequestLine())
-	s += p.formatter.Headers(req.Header)
+	b.WriteString(p.formatter.RequestLine(*res.RequestLine()))
+	b.WriteString(p.formatter.Headers(req.Header))

-	return s
+	return b.String()
}

74-80: Consider using a string builder here too

Similar to the previous comment, use strings.Builder here as well for consistent implementation and better performance.

func (p *Printer) printResponseHeaders(res *result.Result) string {
-	s := ""
+	var b strings.Builder
-	s += p.formatter.StatusLine(*res.StatusLine())
-	s += p.formatter.Headers(res.Response.Header)
+	b.WriteString(p.formatter.StatusLine(*res.StatusLine()))
+	b.WriteString(p.formatter.Headers(res.Response.Header))

-	return s
+	return b.String()
}

82-92: Handle potential nil body

Consider adding a check for a nil body pointer to prevent potential panics.

func (p *Printer) printBody(body *result.Body) string {
+	if body == nil {
+		return ""
+	}

	if jsonBody := body.Json(); jsonBody != nil {
		return fmt.Sprintf("%v\n", p.formatter.Json(jsonBody))
	}

	if textBody := body.Text(); textBody != "" {
		return fmt.Sprintf("%v\n", textBody)
	}

	return ""
}
pkg/printer/printer_test.go (3)

52-58: Consider refactoring to avoid global variable manipulation

Modifying global variables in tests can lead to side effects between tests if they run in parallel. Consider refactoring the Printer to accept these values through constructor parameters, as suggested for the main implementation.

func TestPrinter_Print(t *testing.T) {
	var buf bytes.Buffer

-	oldDestination := destination
-	destination = &buf
-	oldIsTerminal := isTerminal
-	isTerminal = false
-
-	defer func() { destination = oldDestination }()
-	defer func() { isTerminal = oldIsTerminal }()

	// ... rest of test ...
}

Then modify how you create the printer:

p := NewPrinter(tc.config)
// Replace with something like:
p := NewPrinterWithCustom(tc.config, &buf, false)

77-109: Comprehensive test cases

The test cases are well-structured and cover various configuration scenarios. Consider adding a few more test cases:

  1. Test with nil response
  2. Test with nil request
  3. Test with empty bodies
  4. Test with JSON bodies

This would improve coverage of edge cases.


118-123: Consider more precise assertions

Using strings.Contains for assertions is flexible but might miss formatting issues or ordering problems. Consider using regex patterns or more structured assertion helpers for more precise verification of the output format.

out := buf.String()
for _, expect := range tc.expect {
-	if !strings.Contains(out, expect) {
-		t.Errorf("expected output to contain %q: %v", expect, out)
-	}
+	// Example of more precise assertion using regex
+	matched, err := regexp.MatchString(fmt.Sprintf(`(?m).*%s.*`, regexp.QuoteMeta(expect)), out)
+	if err != nil {
+		t.Fatalf("regex error: %v", err)
+	}
+	if !matched {
+		t.Errorf("expected output to match pattern containing %q: %v", expect, out)
+	}
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0fa8f2 and b942b11.

📒 Files selected for processing (2)
  • pkg/printer/printer.go (2 hunks)
  • pkg/printer/printer_test.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
pkg/printer/printer_test.go (3)
pkg/printer/printer.go (2)
  • NewPrinter (29-36)
  • PrinterConfig (14-19)
pkg/printer/formatter.go (2)
  • ColorFormatter (29-29)
  • PlainFormatter (30-30)
pkg/result/result.go (2)
  • Result (9-14)
  • Metadata (16-18)
pkg/printer/printer.go (2)
pkg/printer/formatter.go (2)
  • Formatter (14-19)
  • NewFormatter (21-27)
pkg/result/result.go (4)
  • Result (9-14)
  • RequestLine (26-30)
  • StatusLine (20-24)
  • Body (32-32)
🔇 Additional comments (2)
pkg/printer/printer.go (1)

39-42: LGTM!

I see you've added a nil check for res.Response as suggested in the previous review comment. This is a good practice even if, as you mentioned, the Client.Do implementation ensures it won't be nil.

pkg/printer/printer_test.go (1)

14-47: Good test design for NewPrinter

The table-driven test pattern is a good choice, and the test cases cover both terminal and non-terminal scenarios. The temporary modification of the global isTerminal variable is properly restored with defer.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
pkg/printer/printer.go (1)

71-78: Potential nil dereference for Request.
You dereference res.Response.Request without a safety check. You’ve mentioned it’s guaranteed non-nil in your flow, so this is acceptable. However, consider a brief comment or doc note for future maintainers.

pkg/printer/printer_test.go (1)

49-123: Comprehensive coverage but missing nil-response case.
The subtests thoroughly check different printing scenarios. Consider adding a case where res.Response is nil to confirm the fallback message “No HTTP response available.”

pkg/printer/formatter.go (2)

80-113: Status line color logic.
Time-based and status-based color choices look good. For more robust classification, consider parsing status codes numerically—but this is fine for a typical range of HTTP statuses.


138-144: formatJson fallback message.
You return “unable to parse json” without the actual error. Consider providing the error details for easier debugging.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b942b11 and bcdf42d.

📒 Files selected for processing (3)
  • pkg/printer/formatter.go (1 hunks)
  • pkg/printer/printer.go (2 hunks)
  • pkg/printer/printer_test.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
pkg/printer/printer_test.go (3)
pkg/printer/printer.go (4)
  • IsTerminal (14-14)
  • NewPrinter (29-42)
  • PrinterConfig (16-21)
  • Printer (23-27)
pkg/printer/formatter.go (2)
  • ColorFormatter (21-21)
  • PlainFormatter (22-22)
pkg/result/result.go (2)
  • Result (9-14)
  • Metadata (16-18)
pkg/printer/formatter.go (2)
pkg/result/result.go (2)
  • StatusLine (20-24)
  • RequestLine (26-30)
pkg/printer/colors.go (9)
  • Cyan (39-41)
  • Color (3-3)
  • ColorGreen (8-8)
  • ColorCyan (12-12)
  • ColorRed (7-7)
  • ColorReset (6-6)
  • ColorYellow (9-9)
  • Colorize (15-17)
  • ColorMagenta (11-11)
🔇 Additional comments (17)
pkg/printer/printer.go (7)

5-6: Imports look appropriate.
No issues found with the newly introduced imports. They are all used and serve valid purposes.

Also applies to: 9-9, 11-11


14-15: Test-friendly IsTerminal approach.
Defining IsTerminal as a variable enables easy stubbing in tests, which is a clean approach.


24-26: Struct fields are well-defined.
The Printer struct effectively captures config, destination, and formatter.


29-42: Factory method looks straightforward.
Using IsTerminal(int(os.Stdout.Fd())) to decide between colored or plain formatting is concise. No concurrency or error-handling concerns here.


44-69: Centralized printing logic is clear.
This method composes sections based on config and prints them together. If res.Response is nil, it exits gracefully. All good.


80-86: Response headers printing is straightforward.
No issues noted. Consistently reuses formatter logic to show headers.


88-98: Body printing logic is solid.
Nicely handles both JSON and textual output. Straightforward, no performance concerns noted.

pkg/printer/printer_test.go (2)

4-5: Test imports are valid.
All imports are relevant to test setup and assertions.

Also applies to: 7-7, 9-9


14-47: TestNewPrinter verifies formatter selection.
The table-driven approach to mock IsTerminal is clean and does not raise any issues.

pkg/printer/formatter.go (8)

14-20: Interface definition is concise.
The methods cover essential formatting tasks, aligning well with usage in printer.go.


21-22: Basic structs for color vs. plain formatting.
Splitting these into distinct types is straightforward and keeps logic clean.


24-30: Headers method is consistent for both color & plain.
Delegation to formatHeaders is appropriate; no redundant code.


32-38: StatusLine method uses shared helper.
No issues. Using formatStatusLine is consistent and avoids duplication.


40-46: RequestLine color vs. plain logic.
Same approach as StatusLine—clear separation, no code duplication.


48-54: JSON formatter logic.
Both implementations reuse formatJson, reducing duplication. No concerns.


56-78: Header formatting is well-handled.
Sorting keys for deterministic output is a nice touch. Also, color-coding is optional.


115-136: Request line color logic.
Method-based color approach is intuitive and easily extensible.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
pkg/printer/printer.go (2)

14-14: Consider making IsTerminal a function rather than a package-level variable.

A function-based approach (e.g., a helper func isTerminalOut() bool) can help avoid accidental mutations and makes it more explicit when terminal checks are performed.


44-69: Potential concurrency caveat.

If multiple goroutines call Print on the same Printer instance, the combined output may interleave. Consider documenting this or wrapping output in a synchronization mechanism if thread safety is required.

pkg/printer/formatter.go (1)

138-145: Consider more typical JSON indentation.

You currently indent with a single space per level (" "). Using two or four spaces can improve readability in common tooling.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bcdf42d and 5e9af0d.

📒 Files selected for processing (2)
  • pkg/printer/formatter.go (1 hunks)
  • pkg/printer/printer.go (2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
pkg/printer/formatter.go (2)
pkg/result/result.go (2)
  • StatusLine (20-24)
  • RequestLine (26-30)
pkg/printer/colors.go (9)
  • Cyan (39-41)
  • Color (3-3)
  • ColorGreen (8-8)
  • ColorCyan (12-12)
  • ColorRed (7-7)
  • ColorReset (6-6)
  • ColorYellow (9-9)
  • Colorize (15-17)
  • ColorMagenta (11-11)
🔇 Additional comments (16)
pkg/printer/printer.go (6)

5-11: Imports look good.

These imports are appropriate for the new functionality involving terminal detection, output to os.Stdout, and writing via io.Writer.


24-26: Flexible struct fields.

Introducing config, destination, and formatter in Printer provides a clean separation of concerns for configuration, output destination, and formatting logic.


29-42: Good constructor pattern.

NewPrinter correctly sets the destination to os.Stdout and chooses a formatter based on IsTerminal. This pattern simplifies instantiation and makes testing easier.


71-74: No explicit nil-check for Response.Request.

Based on your design, the Request should always be available here. This is acceptable if you’re certain it cannot be nil in normal usage. Otherwise, you can add a defensive check for extra safety.


76-79: Response headers extraction.

This function straightforwardly formats the response’s status line and headers. The logic is clean and consistent with the request headers printing.


80-90: Text vs. JSON body handling is concise.

The printBody method clearly distinguishes between JSON and text bodies. Providing a fallback to an empty string on no content is also reasonable.

pkg/printer/formatter.go (10)

3-12: Imports are appropriate.

These imports cover JSON encoding, HTTP handling, sorting, and string manipulation needed for formatting.


14-19: Well-defined Formatter interface.

Having methods for each relevant part of HTTP output (status line, request line, headers, JSON) makes this interface easy to implement and extend.


21-22: Color vs. plain output strategy.

Defining separate structs for color and plain formatting is clear. It keeps each approach distinct without adding branching logic.


24-30: Reusability in Headers methods.

Both color and plain formatters delegate to formatHeaders, avoiding code duplication and ensuring consistent output across implementations.


32-38: Status line logic separation.

Similarly, delegating to formatStatusLine ensures shared logic for color and plain variants, improving maintainability.


40-46: Request line colorization is straightforward.

The design cleanly separates the color-coded method logic from the rest of the format.


48-54: Unified JSON method.

Both formatters call the same JSON formatting helper, reducing duplication while providing consistent output.


56-78: Efficient sorting approach in formatHeaders.

Collecting header keys and sorting them ensures predictable output ordering. The color-aware approach is well isolated in a single block.


80-113: Robust status coloring logic.

Using strings.HasPrefix for status code checks is a simple technique that covers common ranges. The time-based color choice also adds a helpful performance indicator.


115-136: Method-based color coding.

Switching on HTTP methods ensures consistent color usage. The defaulting to ColorMagenta for unknown methods is a neat fallback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
pkg/printer/formatter.go (3)

21-22: Optional: unify implementations under a single struct.

We already encapsulate colorization as a boolean flag in the helper functions. If you prefer reducing duplications, consider a single struct that holds a boolean for color output, simplifying maintenance.


48-50: Consider colorizing JSON in ColorFormatter.

Currently, both ColorFormatter and PlainFormatter produce identical JSON output. If you intend color-coded JSON, consider adding color highlighting to keys or braces for consistency with the rest of the color-coded outputs.

Also applies to: 52-54


80-105: Make response time thresholds customizable or configurable.

The current thresholds for coloring response times (500 ms for no color and 1 second for red) might be too rigid for some use cases. Exposing them as configuration or environment variables could make this feature more flexible.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e9af0d and 9b40b64.

📒 Files selected for processing (2)
  • pkg/printer/formatter.go (1 hunks)
  • pkg/printer/formatter_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/printer/formatter_test.go
🧰 Additional context used
🧬 Code Definitions (1)
pkg/printer/formatter.go (2)
pkg/result/result.go (2)
  • StatusLine (20-24)
  • RequestLine (26-30)
pkg/printer/colors.go (9)
  • Cyan (39-41)
  • Color (3-3)
  • ColorGreen (8-8)
  • ColorCyan (12-12)
  • ColorRed (7-7)
  • ColorReset (6-6)
  • ColorYellow (9-9)
  • Colorize (15-17)
  • ColorMagenta (11-11)
🔇 Additional comments (2)
pkg/printer/formatter.go (2)

14-19: Well-structured interface for extensibility.

Declaring the Formatter interface centralizes formatting concerns and makes it easy to add more formatters in the future.


138-144: Edge case handling for non-JSON or cyclic structures.

While an error case is handled, callers might benefit from a broader approach to reporting or retrying failed JSON conversions (e.g., partial fallback or structured logs). Confirm this minimal error message meets your needs.

@eynopv eynopv merged commit 8d1f072 into master Apr 6, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant