Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 19, 2025

The library lacked json.Marshal/json.Unmarshal equivalents, forcing users to work with map[string]interface{} and manual type assertions instead of idiomatic Go structs.

Changes

New API functions (pkg/toon/marshal.go):

  • Marshal(v interface{}, options EncodeOptions) ([]byte, error) - converts Go values to TOON format using reflection
  • Unmarshal(data []byte, v interface{}, options DecodeOptions) error - parses TOON into Go values

Struct tag support: Use toon:"fieldname" tags like json tags. Fields marked toon:"-" or unexported are ignored.

Type handling: Supports primitives, structs, maps, slices, arrays, and nested structures. Handles type conversions (e.g., numeric strings → string fields).

Usage

type Config struct {
    Name    string   `toon:"name"`
    Port    int      `toon:"port"`
    Tags    []string `toon:"tags"`
}

// Marshal
config := Config{Name: "app", Port: 8080, Tags: []string{"api", "web"}}
data, _ := toon.Marshal(config, toon.EncodeOptions{IndentSize: 2})

// Unmarshal  
var loaded Config
toon.Unmarshal(data, &loaded, toon.DecodeOptions{IndentSize: 2})

No changes to existing Encode/Decode functions. 15 test cases cover struct marshaling, nested objects, arrays, maps, and type conversions.

Original prompt

This section details on the original issue you should resolve

<issue_title>This project isn't useful without a way to marshal or unmarshal</issue_title>
<issue_description>Without a way to Marshal or unmashal the given toon string directly into the go code like we do with json, this library isn't really useful</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Marshal/Unmarshal functions enabling direct Go struct to TOON conversions with struct tag support, mirroring encoding/json style tagging.
  • Documentation

    • Added library usage examples demonstrating struct-based encoding/decoding workflows and practical Marshal/Unmarshal usage patterns.
  • Tests

    • Added comprehensive test suite covering struct marshaling, nested structures, maps, arrays, and error handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds Marshal and Unmarshal functions to the toon library, enabling direct conversion between Go structs and TOON format using reflection and struct tags. Includes comprehensive test coverage, documentation updates, and example programs demonstrating round-trip serialization workflows.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added feature bullet describing Marshal/Unmarshal with struct tag support; included usage examples for struct-based encoding/decoding workflows with toon.Marshal and toon.Unmarshal functions.
Core Implementation
pkg/toon/marshal.go
Implemented Marshal(v interface{}, options EncodeOptions) ([]byte, error) and Unmarshal(data []byte, v interface{}, options DecodeOptions) error functions. Added recursive helper functions (toJsonValue, toJsonStruct, fromJsonValue, fromJsonObjectToStruct) using reflection to convert between Go values and generic JsonValue representation, with support for struct tags toon:"fieldname" and toon:"-".
Test Suite
pkg/toon/marshal_test.go
Added comprehensive test coverage for marshaling/unmarshaling including simple structs, nested structures, maps, arrays, nil handling, primitives, field tagging, and error cases. Tests validate round-trip serialization and tag behavior.
Example Programs
tests/test_issue_demonstration.go, tests/test_marshal_example.go
Created demonstration and example programs showcasing end-to-end Marshal/Unmarshal usage with struct definitions (AppConfig, User, Profile) featuring toon struct tags and indentation options.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Marshal as Marshal()
    participant toJsonValue as toJsonValue()
    participant Encode as Encode()
    participant Unmarshal as Unmarshal()
    participant fromJsonValue as fromJsonValue()
    participant Target as Target<br/>(Go Value)

    Client->>Marshal: Marshal(struct,<br/>options)
    Marshal->>toJsonValue: Convert struct to<br/>JsonValue tree
    toJsonValue->>toJsonValue: Recursively handle<br/>fields & tags
    toJsonValue-->>Marshal: JsonValue
    Marshal->>Encode: Encode(JsonValue,<br/>options)
    Encode-->>Marshal: []byte (TOON)
    Marshal-->>Client: []byte (TOON)
    
    Client->>Unmarshal: Unmarshal([]byte,<br/>target pointer)
    Unmarshal->>Encode: Decode([]byte)
    Encode-->>Unmarshal: JsonValue
    Unmarshal->>fromJsonValue: Populate target from<br/>JsonValue
    fromJsonValue->>fromJsonValue: Recursively map fields<br/>& validate tags
    fromJsonValue->>Target: Set field values
    Unmarshal-->>Client: error (nil on success)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

Areas requiring extra attention:

  • Reflection logic in toJsonValue and fromJsonValue helpers: Complex type-switching and recursive traversal for nested structs, maps, slices, and arrays; verify correct handling of pointers, interfaces, and edge cases (nil values, circular references).
  • Struct tag parsing and field mapping: Ensure toon:"fieldname" and toon:"-" tags are correctly parsed and applied; verify unexported fields are properly ignored.
  • Type coercion and conversion: Review string/int/float/bool conversions for potential data loss or unexpected behavior (e.g., number-to-string coercions).
  • Error messages and validation: Confirm error handling covers all unsupported types and type mismatches with descriptive messages.
  • Test coverage for boundary cases: Verify tests adequately cover deeply nested structures, empty slices/maps, nil pointers, and invalid target types.

Poem

🐰 A hop through structs, a tag-guided way,
Marshal and unmarshal make TOON come to play!
From Go types to bytes, round-trip complete,
Struct serialization, oh what a feat! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.13% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change—adding Marshal/Unmarshal functions for direct struct conversion, which is the primary focus of all code changes.
Linked Issues check ✅ Passed The PR fully addresses issue #1 by implementing Marshal and Unmarshal functions with struct tag support, enabling direct TOON ↔ Go struct conversion similar to encoding/json.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing Marshal/Unmarshal functionality: core implementation, comprehensive tests, documentation examples, and demo files.

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI and others added 3 commits November 19, 2025 22:03
Co-authored-by: tnfssc <29162020+tnfssc@users.noreply.github.com>
Co-authored-by: tnfssc <29162020+tnfssc@users.noreply.github.com>
Co-authored-by: tnfssc <29162020+tnfssc@users.noreply.github.com>
Copilot AI changed the title [WIP] Add marshalling and unmarshalling functions Add Marshal/Unmarshal functions for direct struct conversion Nov 19, 2025
Copilot AI requested a review from tnfssc November 19, 2025 22:09
@tnfssc
Copy link
Owner

tnfssc commented Nov 20, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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 (1)
pkg/toon/marshal_test.go (1)

332-344: Prefer standard library function.

The contains and findSubstring helper functions reinvent strings.Contains from the standard library.

Replace the custom implementation with the standard library:

-// Helper function to check if a string contains a substring
-func contains(s, substr string) bool {
-	return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && findSubstring(s, substr))
-}
-
-func findSubstring(s, substr string) bool {
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return true
-		}
-	}
-	return false
-}
+// Helper function to check if a string contains a substring
+func contains(s, substr string) bool {
+	return strings.Contains(s, substr)
+}

Note: strings.Contains is already imported in the test file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 260bcc1 and 7e70268.

📒 Files selected for processing (5)
  • README.md (2 hunks)
  • pkg/toon/marshal.go (1 hunks)
  • pkg/toon/marshal_test.go (1 hunks)
  • tests/test_issue_demonstration.go (1 hunks)
  • tests/test_marshal_example.go (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
README.md

63-63: Hard tabs
Column: 1

(MD010, no-hard-tabs)


64-64: Hard tabs
Column: 1

(MD010, no-hard-tabs)


66-66: Hard tabs
Column: 1

(MD010, no-hard-tabs)


70-70: Hard tabs
Column: 1

(MD010, no-hard-tabs)


71-71: Hard tabs
Column: 1

(MD010, no-hard-tabs)


72-72: Hard tabs
Column: 1

(MD010, no-hard-tabs)


76-76: Hard tabs
Column: 1

(MD010, no-hard-tabs)


77-77: Hard tabs
Column: 1

(MD010, no-hard-tabs)


78-78: Hard tabs
Column: 1

(MD010, no-hard-tabs)


79-79: Hard tabs
Column: 1

(MD010, no-hard-tabs)


80-80: Hard tabs
Column: 1

(MD010, no-hard-tabs)


81-81: Hard tabs
Column: 1

(MD010, no-hard-tabs)


83-83: Hard tabs
Column: 1

(MD010, no-hard-tabs)


84-84: Hard tabs
Column: 1

(MD010, no-hard-tabs)


85-85: Hard tabs
Column: 1

(MD010, no-hard-tabs)


86-86: Hard tabs
Column: 1

(MD010, no-hard-tabs)


87-87: Hard tabs
Column: 1

(MD010, no-hard-tabs)


89-89: Hard tabs
Column: 1

(MD010, no-hard-tabs)


90-90: Hard tabs
Column: 1

(MD010, no-hard-tabs)


91-91: Hard tabs
Column: 1

(MD010, no-hard-tabs)


92-92: Hard tabs
Column: 1

(MD010, no-hard-tabs)


93-93: Hard tabs
Column: 1

(MD010, no-hard-tabs)


94-94: Hard tabs
Column: 1

(MD010, no-hard-tabs)


95-95: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🔇 Additional comments (10)
README.md (1)

14-14: LGTM! Clear feature description.

The Marshal/Unmarshal feature is accurately described and appropriately compared to encoding/json.

tests/test_marshal_example.go (2)

10-27: LGTM! Well-structured demonstration types.

The struct definitions effectively demonstrate all key features: standard toon tags, unexported fields, and explicit exclusion with toon:"-".


29-103: LGTM! Comprehensive demonstration of functionality.

The example effectively demonstrates:

  • Round-trip struct marshaling/unmarshaling
  • Map handling
  • Error handling patterns
  • Field verification

This serves as excellent documentation for users.

tests/test_issue_demonstration.go (1)

1-69: LGTM! Excellent problem/solution demonstration.

This file effectively illustrates the issue from #1 and shows how Marshal/Unmarshal solves it. The contrast between the old map-based approach and the new struct-based approach clearly demonstrates the value of this feature.

pkg/toon/marshal_test.go (1)

8-30: LGTM! Well-designed test fixtures.

The test structs provide excellent coverage of different scenarios: primitives, collections, nested structures, and field exclusion patterns.

pkg/toon/marshal.go (5)

9-38: LGTM! Clean public API design.

The Marshal and Unmarshal functions follow the familiar encoding/json pattern, with clear documentation and proper error propagation.


40-79: LGTM! Solid reflection-based conversion.

The pointer dereferencing loop and type conversions are well-implemented. The approach of converting all numerics to float64 aligns with JSON semantics, which is appropriate for TOON.


119-156: LGTM! Proper struct field handling.

The struct tag parsing correctly handles toon:"-" for exclusion and extracts field names from tags. The comma-splitting logic (lines 141-144) prepares for potential future support of options like omitempty, though that's not currently implemented.


212-229: LGTM! Flexible string conversion.

The string field handling includes type coercion from numerics and booleans, which matches the PR's goal of supporting type conversions. This provides a user-friendly unmarshaling experience.


349-401: LGTM! Well-implemented struct population.

The struct unmarshaling correctly:

  • Maps TOON field names to struct fields using tags
  • Ignores unknown fields (matching encoding/json behavior)
  • Provides helpful error messages with field context (line 394)
  • Handles unexported fields and toon:"-" exclusions

…`Unmarshal` as public API, and enhance testing with new examples and CI workflow.
@tnfssc tnfssc marked this pull request as ready for review November 20, 2025 07:46
@tnfssc tnfssc merged commit e993fb8 into develop Nov 20, 2025
7 checks passed
@tnfssc tnfssc deleted the copilot/add-marshalling-support branch November 20, 2025 07:46
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.

This project isn't useful without a way to marshal or unmarshal

2 participants